Distributed Transactions & Saga Pattern
A single database transaction is straightforward — ACID guarantees handle it. But what happens when a business operation spans multiple services or databases? You can't just wrap everything in one transaction. This lesson covers two approaches: the classic Two-Phase Commit and the modern Saga pattern.
The Problem
Why Distributed Transactions Are Hard
Consider an e-commerce order:
- Order Service: Create order record
- Payment Service: Charge credit card
- Inventory Service: Reserve items
- Shipping Service: Schedule delivery
If payment succeeds but inventory is out of stock, you need to undo the payment. Each service has its own database — there's no single transaction that spans all of them.
Two-Phase Commit (2PC)
The classic approach: a coordinator orchestrates all participants through two phases.
How 2PC Works
- Phase 1 — Prepare: Coordinator asks each participant "Can you commit?" Each participant acquires locks, writes to a transaction log, and votes YES or NO.
- Phase 2 — Commit/Abort: If ALL vote YES → coordinator sends COMMIT to all. If ANY votes NO → coordinator sends ABORT to all.
2PC Problems
- Blocking: Participants hold locks while waiting for the coordinator's decision. If the coordinator dies, they're stuck.
- Single Point of Failure: If the coordinator crashes after Phase 1 but before Phase 2, participants can't decide on their own.
- Performance: Synchronous — all participants must be available and responsive. Latency = slowest participant.
- Not partition-tolerant: If a network partition splits coordinator from participants, the system blocks.
The Saga Pattern
Instead of one distributed transaction, break it into a sequence of local transactions. Each step has a compensating action — an "undo" that runs if a later step fails.
Choreography vs Orchestration
Choreography-Based Sagas
Each service listens for events and decides what to do next. No central coordinator.
- Order Service publishes "OrderCreated" → Payment Service hears it, charges card, publishes "PaymentCharged"
- Inventory Service hears "PaymentCharged" → reserves items, publishes "InventoryReserved"
- If Payment fails → publishes "PaymentFailed" → Order Service hears it and cancels
Pros: Simple, loosely coupled, no SPOF. Cons: Hard to understand the full flow, difficult to debug, risk of cyclic dependencies.
Orchestration-Based Sagas
A central orchestrator (saga coordinator) tells each service what to do and handles the flow logic.
- Orchestrator: "Payment Service, charge the card" → waits for response
- Orchestrator: "Inventory Service, reserve items" → waits for response
- If any step fails → orchestrator triggers compensations in reverse
Pros: Easy to understand, centralized flow logic, simpler testing. Cons: Orchestrator can be a bottleneck/SPOF, tighter coupling to coordinator.
Compensating Transactions
The "Undo" for Each Step
Compensations aren't simple rollbacks — they're new forward actions that semantically reverse the original:
- Charged card → Compensation: Issue refund (not "undo the charge" — that's impossible once processed)
- Reserved inventory → Compensation: Release reservation
- Sent email → Compensation: Send cancellation email
- Shipped package → Compensation: Initiate return process
Some actions are non-compensable (you can't un-send a physical package that's already delivered). Design sagas to put non-compensable steps last.
Real-World Examples
🏢 Airline Booking as a Saga
- Reserve seat (hold for 15 minutes) — Compensation: Release seat
- Charge credit card — Compensation: Refund payment
- Issue ticket — Compensation: Void ticket
- Send confirmation email — Compensation: Send cancellation email
If the credit card is declined at step 2, the system releases the seat (compensation for step 1). The customer's experience: "Sorry, payment failed. Please try again." No orphaned reservations.
🏢 Uber's Trip Lifecycle as a Saga
- Match rider → driver — Compensation: Release driver back to pool
- Start trip — Compensation: Cancel trip, no charge
- Navigate & drive — (in progress, no compensation needed yet)
- End trip & calculate fare — Compensation: Adjust fare
- Charge payment — Compensation: If payment fails → flag for resolution, don't block the driver
Uber can't "undo" a completed ride, so the saga handles payment failures by flagging for later resolution rather than reversing the physical trip.
Interactive: Build a Saga
An e-commerce order saga is running. Click "Fail" on any step to trigger compensations: