XYLEX Group

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

  1. Workflow executes a series of steps (activities)
  2. As each step succeeds, a compensation function is registered
  3. If any step fails, the workflow switches to compensating mode
  4. Registered compensations execute in reverse order (LIFO)
  5. Workflow completes with compensated status

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).