Saga Pattern
Managing distributed transactions across services using a sequence of local transactions with compensating actions.
Overview
The Saga pattern manages distributed transactions across multiple services without a two-phase commit. A saga is a sequence of local transactions; if one fails, compensating transactions are executed in reverse to undo the work already done. There are two coordination styles: Choreography (services react to events) and Orchestration (a central coordinator commands each step).
Origin
The term "saga" in this context was introduced by Hector Garcia-Molina and Kenneth Salem in "Sagas" (1987) for long-lived database transactions. It was rediscovered for microservices by Chris Richardson and others around 2018.
Examples
Orchestrated saga for order fulfilment
class PlaceOrderSaga {
async execute(order) {
const steps = [
{ do: () => paymentService.reserve(order), undo: (r) => paymentService.release(r.reservationId) },
{ do: () => inventoryService.reserve(order), undo: (r) => inventoryService.release(r.reservationId) },
{ do: () => shippingService.schedule(order), undo: (r) => shippingService.cancel(r.shipmentId) },
]
const completed = []
for (const step of steps) {
try {
const result = await step.do()
completed.push({ step, result })
} catch (err) {
// Compensate in reverse order
for (const { step: s, result: r } of completed.reverse()) {
await s.undo(r).catch(e => compensationLog.error(e))
}
throw new SagaFailedError(err)
}
}
}
}Compensating transactions must be idempotent, they may be executed more than once if the orchestrator crashes and retries. Use idempotency keys.
Use Cases
- 01Order placement: reserve payment, reserve inventory, schedule shipping, all must succeed or all must roll back
- 02User onboarding across multiple services: create account, send welcome email, initialise preferences
- 03Booking systems: hold seat, charge card, issue ticket, distributed across separate services
When Not to Use
- //When a single database transaction is possible, ACID guarantees are far simpler and more reliable
- //When compensating transactions cannot be defined, some side effects (sent emails, fired webhooks) cannot be undone
- //When the failure mode analysis of all compensation paths is too complex to reason about safely
Technical Notes
- Choreography vs Orchestration: choreography is more decoupled but harder to observe (the saga has no explicit owner); orchestration is easier to debug but the orchestrator is a central dependency
- Compensating transactions are not rollbacks in the ACID sense, they produce new transactions that reverse the effect. They run in a future transaction, not the same one
- Saga state must be persisted so that if the orchestrator crashes mid-saga, it can resume or compensate after restart
More in Architecture