Skip to content
HLD Learn/Distributed Transactions & Resilience
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Distributed Transactions & the SAGA Pattern

5 min read

You'll learn to

  • -Explain why one ACID transaction cannot span several services, and why two-phase commit is avoided
  • -Explain a saga as a series of local transactions with a compensating action for each step
  • -Compare orchestration and choreography
  • -Name the pitfalls: compensations are not perfect undos, steps must be repeatable, and partial states are visible

In the databases module you met ACID transactions: a group of changes inside one database that either all happen or none do. The moment a workflow spans several independent services, each owning its own database (the pattern the microservices module covers in depth), a single transaction can no longer wrap the whole thing. Placing an order might touch an Orders service, a Payments service and an Inventory service, and there is no single database to ask for an all-or-nothing guarantee. This chapter explains the standard answer.

An Analogy: Booking a Holiday

You book a holiday from three separate companies: a flight, a hotel and a car. There is no master button that books all three or none. You book the flight, then the hotel. If the car is unavailable, you do not get to roll back time. Instead you phone the hotel and cancel, then phone the airline and cancel, perhaps losing a cancellation fee. Each booking has its own cancellation that undoes it as well as it can.

  • -Each booking is a local transaction: it succeeds or fails inside one company's system.
  • -Each cancellation is a compensating action: a new step that semantically reverses an earlier one.
  • -The whole sequence, with its cancellations, is a saga.
  • -The cancellation fee is a reminder that a compensation is not always a perfect undo.

Why Not One Big Transaction?

There is a classic protocol, two-phase commit, in which a coordinator asks every participant to prepare and then, if all agree, tells them to commit. It does give all-or-nothing across databases, but at a price that microservices rarely accept. While participants wait for the coordinator's decision they hold locks, and if the coordinator crashes at the wrong moment they can be stuck holding them. Every service must also be up at the same moment, so the availability of the whole is the product of the availability of every part. Sagas give up the all-or-nothing atomicity in exchange for services that stay independent.

A Saga in Action

Take the order workflow, with a compensation for each step:

  • -Step 1: create the order as pending. Compensation: mark it canceled.
  • -Step 2: charge the payment. Compensation: refund it.
  • -Step 3: reserve the inventory. Compensation: release the reservation.
  • -Step 4: confirm the order.

Now suppose step 3 fails because the item is out of stock. The saga does not roll back, it runs compensations backward through the steps that already succeeded: refund the payment, then cancel the order. The customer ends with no charge and a canceled order, and every service stayed in charge of its own data. The diagram shows exactly that:

1. Create Order
2. Charge Payment
3. Reserve Inventory - FAILS

If step 3 fails, the SAGA runs compensating transactions backward through the steps that already succeeded, instead of a single all-or-nothing rollback.

1. Create Order2. Charge Payment- succeeds2. Charge Payment3. Reserve Inventory - FAILS- succeeds, then fails here3. Reserve Inventory - FAILS2. Charge Payment- compensate: refund2. Charge Payment1. Create Order- compensate: cancel order

Who Runs the Saga?

  • -Orchestration: a central coordinator (the orchestrator) tells each service what to do next and what to compensate on failure. The logic lives in one place and is easy to follow and monitor, but the orchestrator is another component that has to be reliable.
  • -Choreography: there is no coordinator. Each service listens for events and reacts: Payments reacts to OrderCreated, Inventory reacts to PaymentTaken. It is loosely coupled, but the overall flow is spread across services, and is hard to see and to debug as it grows.

Pitfalls

  • -Compensations are not perfect undos. A refund is not the same as the charge never happening, and an email that has been sent cannot be unsent. Design a compensation for each step, and prefer steps that are easy to reverse early on.
  • -Every step and every compensation must be safe to repeat, because messages are delivered at least once and a saga step may be retried. This is the idempotency idea from this module.
  • -Partial states are visible. Between step 2 and step 4 the customer has been charged but the order is not confirmed. Show clearly that it is pending.
  • -Saga progress must be saved, usually in a database, so that a crash in the middle can be resumed or compensated afterwards.

The Wiki card below goes further into distributed transactions and the alternatives:

From the Wiki🔄 Distributed Transactions & SAGA Pattern

How to maintain data consistency across multiple services/databases when a single ACID transaction cannot span service boundaries.

You book a holiday: flight ($500), hotel ($300), rental car ($150). If the hotel is fully booked, you must undo the flight booking and the car reservation. This multi-step process that needs all-or-nothing semantics across independent systems is a distributed transaction. SAGA manages this by executing each step and explicitly undoing previous steps if one fails.

In a monolith, a database transaction wraps all operations: BEGIN TRANSACTION; debit card; reserve seat; send email; COMMIT. ACID guarantees: if any step fails, all roll back.

In microservices, each service owns its own database. You cannot wrap a transaction across PaymentService.Postgres + InventoryService.MongoDB + EmailService.MySQL. They are completely separate systems.

Two-Phase Commit (2PC): A coordinator asks all participants to PREPARE (lock resources). If all say "yes, ready", the coordinator sends COMMIT. If any says "no", sends ABORT. Drawback: blocking protocol - if the coordinator crashes after PREPARE but before COMMIT, all participants are locked forever waiting. High latency. Rarely used in microservices.

SAGA Pattern: Decomposes a distributed transaction into a sequence of local transactions, each with a corresponding compensating transaction for undo. Two styles:

Choreography-based SAGA: Each service listens for events and publishes events. No central coordinator. Example: OrderService publishes "OrderCreated" → InventoryService reserves stock and publishes "StockReserved" → PaymentService charges card and publishes "PaymentProcessed" → EmailService sends confirmation. If PaymentService fails: publishes "PaymentFailed" → InventoryService listens and releases reservation → OrderService marks order as failed.

  • -SAGA trades atomicity for availability - temporary inconsistency is acceptable in exchange for system resilience.
  • -Compensating transactions must be idempotent - they may be called multiple times.
  • -Choreography: loose coupling, hard to track overall state. Orchestration: tight coupling, easy to monitor.
  • -SAGA state must be durably persisted (in a DB) so the orchestrator survives crashes.
  • -Use where: e-commerce checkout, hotel/flight booking, multi-step financial workflows.
Read the full Wiki deep-dive (+1 more)

In one line: when no single transaction can span your services, split the work into local steps, each with a compensating action, and run the compensations backward if a step fails. Choose orchestration for clarity or choreography for loose coupling, and make every step safe to repeat.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!