Dictionary

Saga pattern

The saga pattern runs a business process that spans several services as a sequence of local transactions, each paired with a compensating action. When a single ACID transaction across services is impractical, a saga lets each service commit its own step and undo earlier steps when a later one fails.

What is the saga pattern?

The saga pattern runs a business process that touches several services as a sequence of local transactions. Each service commits its own step in its own database, then triggers the next step. If a later step fails, the saga runs compensating actions that walk back the steps already done.

You reach for it when a single transaction cannot span every service. Wrapping order, payment, and stock in a single ACID transaction works when they share one database. Once each capability owns its own store, or sits behind a separate SaaS API, that guarantee is gone. A two-phase commit across all of them is usually impractical, and many cloud and SaaS systems do not support it at all.

The idea is older than microservices. Hector Garcia-Molina and Kenneth Salem described sagas in a 1987 paper as a way to run one long transaction as a chain of smaller ones, each paired with a compensating transaction that cancels its effect. A saga does not pretend the whole chain is atomic. It makes each step correct on its own and defines how to reverse the changes.

A worked example: order, payment, stock

Take a webshop checkout where four services each own a step:

  1. The order service creates the order in a pending state.

  2. The payment service charges the customer's card.

  3. The inventory service reserves the stock.

  4. The shipping service books the delivery, and the order is marked confirmed.

Now the stock reservation fails, because the last unit sold a second earlier. The saga does not undo everything automatically. It runs the compensating actions for the steps that already succeeded, in reverse order:

  • The payment service issues a refund for the charge.

  • The order service moves the order to cancelled and tells the customer.

The order ends in a clean state, but look at what happened to the money. The card was charged and then refunded. The customer's statement shows both lines, not a blank. That is the honest shape of a saga.

Choreography or orchestration

A saga needs a way to decide what runs next, and there are two styles. The choice is a real trade, not a detail.

With choreography, there is no central coordinator. Each service publishes an event when its step finishes, and the next service listens for that event and reacts. The order service publishes OrderCreated, the payment service reacts and publishes PaymentTaken, and the chain continues. Those events usually travel over a message queue, on top of an event-driven architecture. This stays light for a short flow with few services. It gets hard to follow as steps grow, because the sequence lives spread across every service instead of in one place, and services can start depending on each other in a loop.

With orchestration, one component, the orchestrator, holds the flow. It calls each service in turn, records where the saga is, and fires the compensating actions when a step fails. You gain one place to read the status and reason about failures, which pays off as the process grows. The cost is that the orchestrator is another component you build and run, and the whole flow leans on it.

Compensation is not rollback

The hard part of a saga is not the happy path. It is accepting that a compensating action is a new transaction, not a time machine. A refund is not the charge never having happened, and a cancellation is not the order never having existed. Each service already committed its step, so there is nothing to roll back in the database sense. You move the system forward into a corrected state instead.

Two consequences follow. First, a saga exposes its intermediate states. Between the payment step and the failure, other parts of the system can see the order as paid, because there is no isolation across services the way one database gives you. A customer might get a "payment received" note moments before the cancellation, so the flow has to tolerate that, and countermeasures such as semantic locks exist when it cannot.

Second, every step and every compensation has to be idempotent. A failure is often a timeout where you do not know whether the step actually ran, so you retry. If a refund or a stock release can fire twice, you get a double refund or a phantom unit back on the shelf. Idempotence, usually through an idempotency key or a processed-event table, is what makes those retries safe.

When to reach for a saga

A saga fits a process that spans systems you cannot put inside one transaction: payment, stock, invoicing, shipping, an external partner API. It earns its extra machinery when a failed step genuinely needs earlier work undone, and when eventual consistency is acceptable for the seconds or minutes the chain takes to settle.

It is the wrong tool when the steps really do share one database, where a plain transaction is simpler and safer. It also struggles when steps are tightly coupled or form a cycle, since the compensating logic then becomes as fragile as the thing it protects. To publish the triggering event in the same commit as the local step, teams often add a transactional outbox so a step and its message cannot drift apart.

Last Updated: July 10, 2026 Back to Dictionary
Keywords
saga pattern compensating transaction ACID transactions idempotence message queue transactional outbox event-driven architecture microservices distributed transaction orchestration choreography