Sign in
TodayMapLearnPracticeReview
Library
12 MINadvancedReliability & ResilienceNot started

Compensating Transaction

A compensating transaction undoes the effects of a previously committed transaction by executing a reverse operation — not by rolling back. It's the rollback mechanism of the Saga pattern: when a step in a multi-service workflow fails, you call compensations for each prior step in reverse order. Unlike a database rollback, compensations are business-level operations that work across services and don't require distributed locks.

Why this matters

Distributed transactions across microservices can't use ACID: there's no global lock manager, two-phase commit is too slow, and holding locks across services creates cascading failures. The Saga pattern replaces atomicity with a sequence of local transactions and compensations: if step 3 fails, run compensations for steps 2 and 1. This is how every real e-commerce, travel, and payments system handles multi-step workflows. Without compensating transactions, partial failures leave systems in inconsistent states — charged but not shipped, reserved but not booked — that are hard to detect and harder to fix.

Prerequisites
  • Distributed Transactions
  • Saga Pattern
  • Idempotent Operations
Related
  • Saga Pattern
  • Two-Phase Commit
  • Idempotent Operations
  • Event Sourcing
Used in

Foundational.

Lesson

How it works

A compensating transaction is the answer to: "I committed step 3, but step 5 failed — how do I undo step 3?" In a single ACID database, you'd ROLLBACK. But across services, each commit is final — the inventory service doesn't hold a lock waiting for the payment service to confirm. You can't roll back; you have to compensate.

Compensation means: execute a new operation that semantically undoes the original. If you charged a card, refund it. If you reserved inventory, release it. If you sent a confirmation email, send a cancellation. The original committed transaction stays committed; the compensation is a new, separate transaction.

This is the heart of the Saga pattern: a sequence of local transactions, each with a compensation. On any failure, run compensations in reverse order. The end state is consistent — not because of ACID, but because the compensations restored invariants.

Key insight: compensation is not rollback. A rollback restores the previous state as if the transaction never happened — only possible when locks are held. A compensation accepts that the transaction happened (other systems may have observed it) and adds a new transaction that semantically reverses it.

Practical consequences:

  • Compensations must be idempotent — they may be retried. A refund operation called twice must not double-refund.
  • Compensations must be commutative with concurrent reads — between the original commit and the compensation, other services may have read the value. They saw the original; they need to handle the change.
  • Some operations are hard to compensate — sending an email (you can't unsend it; you can send a follow-up), invoking an external API that's not idempotent, or triggering a physical action.
  • Compensations can fail too — what if the refund call fails? You need retries, dead-letter queues, and human escalation. Saga orchestration frameworks (Temporal, Cadence, AWS Step Functions) handle this.

Sagas can be coordinated two ways:

  • Choreography: each service emits events; the next service listens and proceeds. No central coordinator. Simple to start, hard to follow as the chain grows — debugging "where did this saga go?" requires tracing events across services.
  • Orchestration: a central orchestrator (Temporal, Step Functions, a custom state machine) calls each service and handles compensations explicitly. More code, but the workflow is visible in one place and the orchestrator tracks state across retries and crashes.

Most production systems converge on orchestration for complex sagas because visibility and recoverability matter. Choreography works for short, simple workflows where each service can be reasonably assumed to handle its own compensations.

Either way, the saga state must be persisted — not in memory — so a crash mid-saga can resume. The orchestrator (or a saga-state table) tracks which steps completed and which compensations are still owed.

Not everything can be compensated

Some operations have no meaningful compensation. Sending an email, mailing a physical product, triggering an SMS — these have side effects in the real world. The pattern handles this by either (a) deferring the irreversible step to the end of the saga, after all compensatable steps have succeeded, or (b) accepting that the operation will happen and compensating with a follow-up (e.g., a cancellation email). Design sagas so irreversible steps are last; if the saga fails before reaching them, no real-world effect occurred.

The classic saga weakness is isolation. Between step 3 (reserve inventory) and step 4 (charge), other queries can see the reserved inventory — they might show 'low stock' or reject a parallel order that would have succeeded if the saga had compensated. ACID's 'I' (isolation) is what sagas give up.

Mitigations:

  • Semantic locks: mark records as 'pending' so other queries know not to act on them yet.
  • Commutative operations: design steps so concurrent reads don't cause incorrect behavior (e.g., reserve inventory with a tentative count, not a hard decrement).
  • Short sagas: keep the saga duration short to minimize the window of inconsistency.
  • Compensating reads: queries that need a consistent snapshot read from a projection updated only after saga completion.

This is the fundamental trade-off of sagas: you lose ACID isolation but gain the ability to do transactions across services without distributed locks.

Check yourself
interview

Your checkout saga reserves inventory, then charges the card. The charge fails. What does the compensation look like?

Pick one answer.

Check yourself
core

Why must compensations be idempotent?

Pick one answer.

Check yourself
advanced

Which of these operations is hardest to compensate, and what's the standard pattern to handle it?

Pick one answer.

Engineering mental model

Mental model. Think of Compensating Transaction as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Compensating Transaction mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”

Design lens

Before choosing Compensating Transaction, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.

Original NO CAP systems visual for Compensating Transaction.
Image unavailable. Original NO CAP systems visual for Compensating Transaction.
Compensating Transaction: a compact system-thinking visual.— Original NO CAP visual.
SELECT id, created_at
FROM records
WHERE tenant_id = ?
ORDER BY created_at DESC
LIMIT 50;

-- Ask: which index makes this query predictable at scale?
A minimal engineering sketch for reasoning about Compensating Transaction.

Back-of-the-envelope reasoning

Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.

Interactive sandboxdeterministic

Interactive thought experiment: Compensating Transaction

Change the variables below and predict what breaks first in Compensating Transaction. The production lab can later reuse these same inputs.

System pressure6%
Try this

Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.

Hint

If you are stuck on Compensating Transaction, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.

Check yourself
solid

You increase traffic by 10× in a system using Compensating Transaction. What should you inspect first?

Pick one answer.

Check yourself
interview

Which statement is the safest engineering habit when using Compensating Transaction?

Pick one answer.

Try this
interview

You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.

Production scenario: your system uses Compensating Transaction, traffic suddenly spikes, and p99 latency doubles. What is your first move?

Interview drill

Answer this without notes: When would you choose Compensating Transaction, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.

Engineering lens

For Compensating Transaction, start with access patterns rather than brand names. Identify the dominant reads/writes, data relationships, consistency requirements, partition key, hot keys and failure behavior before choosing a storage strategy.

Numerical sanity check

A rough capacity check: required write throughput ≈ peak writes/s × average record size. At 5,000 writes/s and 2 KB average payloads, the raw incoming data stream is about 10 MB/s before indexes, replication and overhead.

Check yourself
interview

Do not optimize for a memorized definition. Reason from the workload and failure mode.

A team proposes Compensating Transaction because it 'scales'. What workload characteristic would make that choice a poor fit?

Pick one answer.

Trade-offs

What you gain, what you pay

Pros
  • +Enables multi-service transactions without distributed locks.
  • +Compensations work on already-committed state — no held locks, no cascading failures.
  • +Survives orchestrator crashes via persistent saga state.
  • +Maps naturally to real business operations (refund, cancel, release).
Cons
  • −Loss of ACID isolation — intermediate states are visible to other queries.
  • −Some operations (emails, physical actions) can't truly be compensated.
  • −Compensations can fail too — needs retries, dead-letter queues, human escalation.
  • −Operational complexity: orchestrator, saga state, monitoring of stuck sagas.
Failure modes

How this breaks in production

  • Non-idempotent compensation retried after a crash double-applies (e.g., double refund).
  • Compensation itself fails — saga stuck in a partially-compensated state, needs human intervention.
  • Intermediate state observed by another service that acted on it (e.g., showed 'low stock' and rejected a parallel order).
  • Irreversible step run mid-saga that can't be compensated when a later step fails.
Common mistakes

Don't fall into these traps

  • •Treating compensation as rollback — assuming no other system observed the intermediate state.
  • •Running irreversible steps mid-saga instead of at the end.
  • •Not persisting saga state — orchestrator crash loses the workflow.
  • •Not making compensations idempotent — retry causes double-undo.
Where you see it

Real systems using this

Travel booking: flight + hotel + car as a saga with compensations.E-commerce checkout: reserve inventory → charge → ship, with refunds and releases.Money transfer: debit → credit → notify, with reverse transfer if credit fails.
Teardowns

How real systems implement this

  • Temporal — Workflow orchestration engine that persists saga state across crashes, retries failed activities and compensations, and supports idempotency keys. Built for exactly this pattern.
  • AWS Step Functions — State-machine-based orchestration with built-in compensation via Catch/Retry blocks. Each state can have a compensating state triggered on failure.
Interview prompts

Practice saying it out loud

  • Q1What's the difference between compensation and rollback?
  • Q2Why must compensations be idempotent? What goes wrong if they aren't?
  • Q3How do you handle operations that can't be compensated (e.g., sending an email)?
  • Q4Compare choreography and orchestration for sagas. When would you choose each?
Research

Further reading & references

System Design Primer
Open source
ByteByteGo — Scale from zero to millions
ByteByteGo
System Design Tutorial
GeeksforGeeks
System Design Roadmap
roadmap.sh
Reliability & Resilience reference
Reference
Reliability & Resilience reference
Reference
Reliability & Resilience reference
Reference
AWS Well-Architected
AWS

Core explanations are original NO CAP material. External references are provided for deeper study and standards.

What next?

Mark as understood once the mental model clicks.

Next recommended

Saga Pattern