Distributed systems
Sagas and compensating actions
A saga runs a business operation as local transactions, each with an undo step. Learn how compensations work, how to order the steps, and why sagas are atomic eventually but not isolated.
A saga is a long business operation split into a sequence of local transactions. Each step commits on its own, in its own service. If a later step fails, the saga runs compensating transactions for the steps that already completed, in reverse order. There is no global lock and no two-phase commit.
Concept
A compensating transaction is the designed undo for a step. It is a new action that reverses the business effect of the step, not a database rollback.
Sagas come from Garcia-Molina and Salem's 1987 paper. The idea is old, and it's still the most common way to handle multi-service workflows.
The big idea
Compensation is a semantic undo, not a rollback. You can't un-send an email or un-charge a card. You can send a correction email or issue a refund. So each step needs a designed, tested counterpart.
Walking through a trip booking
A travel site books a flight, a hotel and a rental car as one trip.
| Step | Action | Compensation |
|---|---|---|
| T1 | Create trip record (PENDING) | Mark trip CANCELLED |
| T2 | Book flight (airline API) | Cancel flight booking |
| T3 | Book hotel | Cancel hotel booking |
| T4 | Charge card | Refund card |
| T5 | Book car | Cancel car booking |
| T6 | Mark trip CONFIRMED | None. This is the end. |
Suppose T5 fails because no cars are available. The saga runs C4 (refund), then C3 (cancel hotel), then C2 (cancel flight), then C1 (mark trip cancelled). The user sees: "We couldn't complete your booking. You've been refunded."
Designing the steps
Three choices make a saga robust.
- Order steps by how hard they are to undo. Put steps that can't be undone, such as sending a confirmation or shipping goods, after the pivot transaction. The pivot is the point beyond which the saga commits to finishing. Steps after the pivot must be retried until they succeed.
- Put likely failures early. If car availability often fails, check it before charging the card. Fewer compensations run, and customers see fewer charge-then-refund pairs.
- Make every step and compensation idempotent. The saga retries steps whose outcome is unknown. See idempotency and retries.
Compensations must not fail permanently. A refund that can't be issued needs retries and, eventually, a queue that a person watches.
Solid arrows are forward steps. Dotted arrows are failures that trigger compensation.
Sagas lack isolation
A saga is atomic eventually, through compensation. It is not isolated. Between the charge (T4) and its refund (C4), other transactions can see the intermediate state. A support dashboard may show a charge for a trip that is about to be cancelled. Another saga may read a hotel room as booked when it is about to be released. Countermeasures:
- Semantic locks. Mark records
PENDINGand have other operations treat them carefully. For example, leave pending orders out of revenue reports. - Commutative updates. Design updates so order doesn't matter. Increment and decrement, rather than set.
- Re-read and verify. Check a value again before acting on it in a later step.
Common mistakes
- Treating compensation as rollback. A refund is a new charge in the other direction, with its own failure modes.
- Putting irreversible steps early. Once an email is sent, the saga can't undo it.
- Forgetting that compensations can fail. Give them retries and an escalation path.
Check yourself
1. In the trip saga, the car booking fails. Which compensations run, and in what order? C4 (refund the card), then C3 (cancel the hotel), then C2 (cancel the flight), then C1 (mark the trip cancelled).
2. Why is a saga atomic only eventually? Until the compensations finish, other readers can see the intermediate state, such as a charge on a trip that is about to be cancelled.
Key takeaways
- A saga is a sequence of local transactions, each with a designed compensation.
- Compensation is a semantic undo. A refund is not a rollback.
- Put irreversible steps after the pivot, and make every step idempotent.
- Sagas are atomic eventually but not isolated, so plan for intermediate states.
Further learning
Caitie McCaffrey's GOTO talk is the classic introduction to sagas in practice, from the 1987 paper to how she applied them at scale on Halo's backend.
Next, see who drives a saga, in orchestration versus choreography.
Related
Two-phase commit
The classic protocol for atomic commits across several databases. Learn how prepare and commit work, why a crashed coordinator blocks everyone, and how distributed SQL databases reduce that risk.
Orchestration versus choreography for sagas
A saga can be driven by a central orchestrator, or by services reacting to each other's events. Learn the trade-offs, and why durable workflow engines make orchestration practical.
Case study: payment system
Moving money correctly, with payment intents, idempotency at every hop, a double-entry ledger, webhooks and reconciliation when the network lies. Start here for the payments map.