Saga Pattern
Distributed transactions with Sagas
Saga Pattern
The Saga pattern manages distributed transactions by providing compensation logic to undo changes if a workflow fails.
How It Works
- Workflow executes a series of steps (activities)
- As each step succeeds, a compensation function is registered
- If any step fails, the workflow switches to
compensatingmode - Registered compensations execute in reverse order (LIFO)
- Workflow completes with
compensatedstatus
Example
const orderSaga = workflow('order-saga', async (ctx, order) => {
// 1. Charge Card
const payment = await ctx.run(chargeCard, order);
// Register rollback
ctx.addCompensation(async () => {
await ctx.run(refundCard, payment.id);
});
// 2. Reserve Inventory
const inventory = await ctx.run(reserveInventory, order);
// Register rollback
ctx.addCompensation(async () => {
await ctx.run(releaseInventory, inventory.id);
});
// 3. Create Shipment
// If this fails, inventory is released, then card is refunded
await ctx.run(createShipment, order);
return { success: true };
});
Failure Strategies
To enable automatic compensation, set the failure strategy to compensate:
const sagaWorkflow = workflow('saga', handler, {
failureStrategy: 'compensate'
});
Compensation State
Compensations are tracked in the workflow state:
const state = await world.query(workflowId);
console.log(state.compensations);
// [
// { id: 'comp-1', executed: true, error: null },
// { id: 'comp-2', executed: true, error: null }
// ]
Handling Compensation Failures
If a compensation function itself fails, it is recorded in the history but does not stop other compensations from running. This ensures maximum possible cleanup.
Best practice is to make compensations robust and idempotent (e.g., retrying internally if needed).