Skip to content
Discovery Distributed Systems 4 min read · Updated 6 Aug 2026

The Saga Pattern: Transactions That Cannot Roll Back

advanced distributed-systemsmicroservicestransactions

Inside one database, a multi-step operation is straightforward. Wrap it in a transaction; if anything fails, ROLLBACK, and the intermediate states never existed as far as anyone can tell.

Now put those steps in four services with four databases. There is no shared transaction log, no coordinator that can undo committed work, and the payment service has already charged a real card. ROLLBACK is not available. Not difficult — unavailable.

A saga is what you use instead: a sequence of local transactions, each with a compensating action that semantically undoes it.

The shape

An order that reserves stock, charges a card and books a courier:

1. Order service     create order (pending)     ⟲ mark cancelled
2. Inventory service reserve 1 unit             ⟲ release reservation
3. Payment service   charge ₹1,999              ⟲ refund ₹1,999
4. Shipping service  book courier               ⟲ cancel booking
5. Order service     mark confirmed

If step 4 fails, you run the compensations for 3, 2 and 1 in reverse. The customer ends up refunded and un-reserved. The system is consistent again — but it passed through states where money had moved and stock was held, and those states were visible.

That is the trade the saga makes, and it must be acceptable to the business before it is acceptable to the architecture.

Compensation is not rollback

The most important idea here, and the one that makes sagas genuinely difficult:

A rollback erases. A compensation is a new forward action that happens to be corrective.

A refund is not the inverse of a charge. It is a second transaction. The customer sees both on their statement. There may be a fee. The original may have triggered a fraud check, a loyalty accrual, a webhook to an accounting system — none of which un-happen because you refunded.

So compensations must be written deliberately, and some things have no compensation at all:

  • A sent email cannot be unsent. Order irreversible steps last, or gate them behind the saga completing.
  • Stock released may already be gone. Between reservation and release, someone else bought it. Your compensation succeeded and the customer still cannot have the item.
  • A refund can fail. The compensation itself needs retries — and a place for a human to look when retries are exhausted.

Orchestration or choreography

Orchestration. A coordinator drives the steps and owns the failure logic.

Orchestrator → reserve stock → charge → book courier → confirm
             ← on failure, run compensations in reverse

The whole process is readable in one file. You can query where any saga is stuck. The cost is a component every step depends on, and it must be durable — if the orchestrator dies mid-saga, it has to resume from persisted state, not memory.

Choreography. Services publish events; others react.

OrderCreated → (inventory) StockReserved → (payment) PaymentTaken → (shipping) …

No central component, looser coupling. The cost is that the process exists nowhere — to answer “what happens when payment fails” you read four services and hope you found every subscriber. Debugging is archaeology.

Use orchestration for anything with money, more than three steps, or a support team who will be asked where an order got stuck. Choreography suits simple fan-out where no one needs to reason about the whole.

What a saga requires to be safe

Sagas are built on messaging, so they inherit every messaging problem.

Every step must be idempotent. Retries are guaranteed, so a step will be delivered twice. Charging twice because a broker redelivered is the exact failure the saga was meant to prevent. Use idempotency keys on every step and every compensation.

State must be persisted before publishing. If a service commits its local transaction and then crashes before emitting its event, the saga stalls forever. Writing the event in the same transaction as the state change, then publishing from that record, is the outbox pattern — a saga without one has a permanent race at every step.

Timeouts need explicit handling. A step that neither succeeds nor fails is the normal case in distributed systems, not an edge case. Every saga step needs a deadline and a decision about what the deadline means — usually compensate, occasionally escalate.

Sagas need observability. A correlation id on every message, a queryable current state per saga, and an alert on sagas stuck beyond a threshold. Without it, the failure mode is silent: orders that are neither completed nor cancelled, discovered when a customer complains.

Before you reach for one

The best saga is the one you did not need. Ask honestly:

  • Could these steps share a database? Then use one transaction. A saga to coordinate two services that could have been one module is complexity you chose.
  • Does the step need to be synchronous? Charging at checkout, yes. Awarding loyalty points, no — that can be an event nobody waits for, and events that nobody waits for do not need compensating.
  • Is eventual consistency acceptable here? If the answer is yes for most steps, much of the saga machinery disappears.

What you have actually built

Not a distributed transaction. There is no such thing available to you at reasonable cost, which is why the pattern exists.

You have built a business process with its failure paths written down and executed automatically, instead of left to a support ticket. The intermediate states are real and visible, the compensations are ordinary forward operations, and the guarantee is not atomicity but eventual resolution — every saga finishes, either completed or compensated, and you can prove which.

Quick answers

What is the saga pattern?
A way to manage a business process spanning multiple services without a distributed transaction. Each step commits locally, and each step has a compensating action that semantically undoes it if a later step fails.
What is the difference between orchestration and choreography?
In orchestration a coordinator explicitly calls each step and decides what happens on failure, so the flow lives in one readable place. In choreography services react to each other's events with no central controller, which decouples them but leaves the overall process written nowhere.
What is a compensating transaction?
An action that semantically undoes a completed step — refunding a payment rather than erasing it, releasing reserved stock rather than un-decrementing it. It is a new forward action, not a rollback, and it leaves a visible trace.
When should you not use a saga?
When the steps could live in one database. A single ACID transaction is simpler, safer and faster than any saga, so the first question is always whether the process genuinely has to span services rather than whether the services were split too early.

References

Related Discoveries