Saga Pattern
The Saga Pattern is a design pattern for managing data consistency across microservices in distributed transaction scenarios. A saga is a sequence of local transactions. Each local transaction updates the database and publishes a message or event to trigger the next local transaction in the saga.
1. The Problem: Distributed Transactions
In a monolithic application with a single database, you can use ACID transactions (Atomicity, Consistency, Isolation, Durability). If you update two tables and the second update fails, the database rolls back the entire transaction automatically.
In a microservices architecture, services have their own private databases. You cannot use a standard database transaction that spans multiple services.
- Scenario: An e-commerce "Checkout" involves:
Order Service: Create order.Inventory Service: Reserve items.Payment Service: Charge credit card.
- Failure: If the Payment fails, you must undo the Inventory reservation and cancel the Order.
2. The Solution: Compensating Transactions
Since we can't rely on database rollbacks, we must implement Compensating Transactions.
- If a step fails, the saga executes a series of compensating steps that undo the changes made by the preceding steps.
Example:
- Transaction:
ReserveInventory() - Compensation:
ReleaseInventory()
3. Implementation Approaches
Choreography (Event-Based)
There is no central coordinator. Each service produces and listens to events to decide on an action.
- Flow:
Order Servicecreates order -> EmitsOrderCreated.Inventory ServicehearsOrderCreated-> Reserves items -> EmitsInventoryReserved.Payment ServicehearsInventoryReserved-> Charges card -> EmitsPaymentProcessed.
- Pros: Simple, loose coupling.
- Cons: Hard to track the workflow (who listens to what?), cyclic dependencies risk.
Orchestration (Command-Based)
A central coordinator (Orchestrator) tells the participants what local transactions to execute.
- Flow:
Order Service(Orchestrator) tellsInventory Service: "Reserve items".Inventory Servicereplies: "Done".Order ServicetellsPayment Service: "Charge card".
- Pros: Centralized logic, easy to understand the flow.
- Cons: The orchestrator can become a "God Service" with too much logic.
4. Pros and Cons
| Pros | Cons |
|---|---|
| Data Consistency: Maintains consistency across services without distributed locking (2PC). | Complexity: Much more complex to implement and debug than ACID transactions. |
| Performance: Better performance than Two-Phase Commit (2PC) because it avoids locking resources. | Eventual Consistency: Data is not consistent immediately. Users might see "Pending" states. |
| Compensating Logic: Writing undo logic for every action is tedious and error-prone. |
5. Two-Phase Commit (2PC)
Two-Phase Commit (2PC) is an alternative to Sagas for handling distributed transactions. It provides strong consistency (ACID) rather than eventual consistency.
How it Works
- Prepare Phase: The transaction coordinator asks all participants if they are ready to commit. Participants lock the necessary resources and vote "Yes" or "No".
- Commit Phase:
- If all participants voted "Yes", the coordinator sends a "Commit" command.
- If any participant voted "No" (or timed out), the coordinator sends a "Rollback" command.
Why Sagas are often preferred over 2PC
- Blocking: In 2PC, resources are locked from the Prepare phase until the Commit phase completes. If the coordinator fails, resources remain locked. Sagas do not hold locks across services.
- Performance: 2PC is chatty and slow. Sagas are asynchronous and non-blocking.
- Scalability: 2PC scales poorly in distributed systems due to locking.
6. Compensating Transaction Pattern
A compensating transaction is an operation that undoes the effect of a previously committed transaction. It is the "undo" button for distributed systems.
Key Characteristics
- Idempotent: It must be safe to retry the compensation multiple times. If the system crashes while compensating, it will try again upon restart.
- Not a Perfect Undo: It might not restore the system to the exact previous state. For example, if you booked a seat and then cancelled it, the seat is free again, but the history shows a booking and a cancellation.
Handling Failures
What happens if the compensating transaction itself fails?
- Retry: The standard approach is to retry indefinitely until it succeeds.
- Manual Intervention: If it fails permanently (e.g., due to a bug), a human operator must intervene to fix the data consistency.
programming/microservices-architecture programming/event-driven-architecture programming/database-basics