Part III — Distributed Transactions & Coordination · Chapter 3

Sagas & compensating transactions

Hook

A trip booking touches four separate services — a payments service, an airline, a hotel, a card network. 2PC would hold locks across all four for the whole trip. What if one of them is a third-party API that has never heard of your coordinator and never will?

Intuition
✓ Reserve funds
Reserve funds

Step through it: three steps succeed, the fourth fails — and instead of leaving the first three half-done forever, each one gets explicitly undone, starting from the most recent and working backward.

Formalize

There's no vote and no coordinator holding locks. Instead, each local step ii is paired with its own compensating action CiC_i, and one rule governs recovery:

  1. Run steps forward, one at a time

    Each step is its own local transaction — it commits immediately, on its own service, with no cross-service lock held.

  2. On failure, undo everything already completed

    If step kk fails, every step 0,1,,k10, 1, \dots, k-1 already committed and must be compensated.

  3. Undo in strict reverse order — LIFO

    Compensations run as a stack: the last thing that succeeded is the first thing undone. This matters whenever compensations depend on each other's side effects being reversed in the right order (e.g. you can't safely release funds before an in-flight charge against them is cancelled).

Play
✓ Reserve funds
2 step(s) compensated this run

Try "Fail at: Charge card" — 3 compensations run, hotel first, then flight, then funds, exactly reversed. Try "Fail at: Reserve funds" — the very first step — and nothing needs undoing at all, because nothing had completed yet.

Worked example

Booking hotel fails (index 2), after funds were reserved and the flight was booked.

  1. What completed before the failure

    Step 0 (Reserve funds) and step 1 (Book flight) both succeeded. Step 2 (Book hotel) fails.

  2. Reverse the completed list

    Completed order was [0,1][0, 1]; compensation order is the reverse, [1,0][1, 0].

  3. Run the compensations
    1. Undo step 1's action first: "Cancel flight"
    2. Then undo step 0's: "Release funds"
    3. Charge card (step 3) never ran, so it needs no compensation at all
Checkpoint

Pick the step that should fail so that exactly 2 completed steps get compensated.

Pick a failure point
Summary

A saga trades distributed locking for explicit, per-step undo actions run in strict reverse order on failure — trading atomicity's clean guarantee ("all or nothing, instantly") for something weaker but far more workable across independent services: "all steps complete, or every completed step gets explicitly, eventually, undone."