Knowledge Guide
HomeSystem DesignMicroservices Patterns

The Saga Pattern A Solution

A saga keeps a multi-service business operation atomic without a distributed lock by breaking it into a sequence of local ACID transactions — each commits independently in its own service and database — and undoing any already-committed step with an explicit compensating transaction the moment a later step fails. There is no global rollback to fall back on, because each local commit is already durable and visible; the saga instead semantically reverses what was done. That single trade — swapping one distributed transaction for many local ones plus hand-written inverses — is the whole pattern, and every hard part below follows from it.

How the mechanism actually works

Each forward step Ti has a paired compensation Ci that semantically undoes it (refund, not "un-charge"; cancel order, not "delete row"). A saga log — held by the orchestrator, or reconstructed from the event stream in choreography — records which steps committed so recovery knows exactly how far to unwind.

Two recovery directions exist, and mixing them up is the classic design error. Backward recovery runs Ci … C1 in reverse order to abort. Forward recovery retries a failed step until it succeeds. You cannot always choose backward recovery, which forces the key classification of steps:

This ordering — all compensatable steps first, then the pivot, then only retriable steps — is what makes an eventually-consistent saga tractable. Put a non-compensatable side effect (an email) before the pivot and you can no longer honestly abort.

diagram
diagram

Traced example: an order that fails at inventory

Customer places order O-8842 for 2 units of SKU-19, total $250. The orchestrator drives four steps. Step 3 fails because only 1 unit is in stock. Watch the log and the reverse-order unwind:

#StepService (local txn)ResultSaga log after
1T1 create orderOrder: insert O-8842, status PENDINGOK → emits OrderCreated[T1✔]
2T2 charge card (pivot)Payment: authorize+capture $250 → PAY-551OK → emits PaymentCaptured[T1✔, T2✔]
3T3 reserve stockInventory: reserve 2×SKU-19FAIL — 1 available, business-rule violation[T1✔, T2✔, T3�’✗]
4C2 compensate T2Payment: refund $250 against PAY-551OK (idempotent)[T1✔, T2↩]
5C1 compensate T1Order: set O-8842 status CANCELLEDOK[T1↩] → saga aborted

Two things a distributed 2PC would have hidden are now glaringly visible. First, between step 2 and step 5 the customer's card was really charged — the money moved and only later came back; the system was temporarily inconsistent by design (eventual consistency). Second, T3 didn't get compensated: it never committed, so there is nothing to undo — only committed steps are compensated. Had inventory been the pivot instead, and had we already sent a "shipped" email as a post-pivot step, we could not have aborted at all — we would have had to go forward and retry until inventory freed up.

The isolation problem — the caveat nobody mentions first

Local ACID transactions give you A, C, and D. Sagas throw away the I. Because each step commits immediately, its intermediate state is visible to other transactions before the saga finishes — the exact anomalies isolation was invented to prevent:

You manage this with saga-specific countermeasures (from Garcia-Molina's original work and Richardson's Microservices Patterns):

Choreography vs orchestration

These are the two ways to drive the sequence. In choreography there is no coordinator: each service subscribes to events and reacts (Order emits OrderCreated → Payment reacts and emits PaymentCaptured → Inventory reacts…). In orchestration a central saga orchestrator issues commands ("Payment: charge $250") and decides the next step from each reply.

The honest decision rule is not "simple vs complex" alone — it is about coupling, visibility, and cyclic dependencies:

Pitfalls a working engineer hits

When to use it — and when NOT to

Reach for a saga when a single business operation must update data owned by multiple services/databases, you cannot (or won't) run a distributed transaction across them, and the business can tolerate a brief window of inconsistency plus explicit compensation. Concrete signals: microservices with database-per-service, long-lived operations (checkout, booking, onboarding), or steps that span systems you don't control (payment gateways, shipping APIs).

Named alternative — 2PC / distributed ACID transaction (XA). 2PC gives you real isolation and an automatic atomic rollback: no compensations to write, no dirty reads. What it costs: a coordinator holds locks across all participants for the whole transaction, so throughput collapses under contention, tail latency spikes, and a coordinator crash after "prepare" leaves resources blocked (the blocking problem). Many cloud datastores and message brokers simply don't support XA. Sagas buy availability, low coupling, and long-lived operations at the price of lost isolation and hand-written, idempotent compensation logic.

Takeaways


Re-authored/Deepened for this guide. Sources: Hector Garcia-Molina & Kenneth Salem, "Sagas" (ACM SIGMOD, 1987) — the original long-lived-transaction paper and compensation model; Chris Richardson, Microservices Patterns (Manning, 2018) and microservices.io — saga orchestration/choreography, pivot & retriable classification, and countermeasures for lost isolation; Martin Kleppmann, Designing Data-Intensive Applications (O'Reilly, 2017) — 2PC, the blocking problem, and eventual consistency trade-offs.

🤖 Don't fully get this? Learn it with Claude

Stuck on The Saga Pattern A Solution? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **The Saga Pattern A Solution** (System Design). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **The Saga Pattern A Solution** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **The Saga Pattern A Solution**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **The Saga Pattern A Solution**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes