durable-workflows

1 posts

cloudflare

How we built saga rollbacks for Cloudflare Workflows (opens in new tab)

Cloudflare Workflows now supports saga rollbacks, letting each durable step declare how to compensate for its side effects if a later operation fails. This addresses partial failures in multi-step processes, such as refunding a debit when a subsequent credit cannot complete. Rollbacks execute in reverse order and preserve Workflow durability, while requiring the same idempotency safeguards as normal steps. ## The Saga Problem - Durable Workflows can retry steps and persist state, but completed external operations cannot always be directly undone. - In a bank transfer: - Bank A debits the sender. - Bank B fails to credit the recipient. - The original debit must be reversed with a new credit operation. - The pairing of a forward action and its semantic compensation is known as the saga pattern. ## Manual Compensation Before Rollbacks - Developers had to track which steps completed and write centralized `try`/`catch` logic. - Compensation had to: - Run only for completed operations. - Execute in reverse order. - Continue even if one rollback fails. - Remain durable and retryable. - This approach becomes increasingly complex as workflows gain more steps. ## Rollback Functions on `step.do()` - Rollback logic is now declared directly in the step’s options: ```js await step.do("debit-bank-a", debitFn, { rollback: async ({ output }) => refundFn(output.id), }); ``` - Each step carries its own undo operation, making compensation easier to maintain. - Rollbacks can use the original step output, such as a payment or transaction ID. - If a later step fails, previously registered rollback handlers run automatically in reverse step-start order. ## Idempotency and Partial Failures - Rollback functions must be idempotent because they may be retried. - External operations should use idempotency keys to prevent duplicate refunds, credits, or inventory releases. - A step that fails may still need compensation: - It could have modified an external system before failing. - The operation may have succeeded even though Workflows never received its result. - Rollback handlers must therefore handle `output === undefined`. - If user code catches an error and the Workflow continues, rollback does not immediately start. However, if the Workflow later fails, previously registered handlers can still run. ## Practical Usage - Developers pass an options object with a `rollback` function as the final argument to `step.do()`. - Rollbacks can reverse payments, release resources, or perform other compensating actions. - This removes the need for growing manual catch blocks and explicit rollback ordering while retaining durable execution behavior. Cloudflare’s rollback support is best suited to workflows involving external side effects. Developers should define compensation alongside every reversible step and make both forward and rollback operations safely repeatable.