Distributed Transactions & the SAGA Pattern
You'll learn to
- -Explain why a single ACID transaction can't span services
Module 3 covered ACID transactions inside a single database. The moment a workflow spans multiple independent services (each owning its own database, a pattern the next module covers in depth), a single ACID transaction can no longer wrap the whole thing. You need a different way to keep a multi-step process consistent.
If step 3 fails, the SAGA runs compensating transactions backward through the steps that already succeeded, instead of a single all-or-nothing rollback.
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.
In a SAGA, if step 3 of 4 fails, why not just roll back the whole thing like a normal database transaction?
"You can: just tell the earlier services to undo their part."
"There's no single transaction coordinator holding locks across all four services the way a database would: each step already committed independently in its own database. 'Rolling back' means explicitly running a compensating action for each already-completed step (refund the payment, release the inventory hold), in reverse order. It's not a rollback in the ACID sense at all; it's a deliberate, application-level undo, and someone has to have designed a working compensation for every step up front."