Distributed Transactions & Resilience
The Saga Pattern in Spring Boot — Choreography and Orchestration
Implementing step events channels or central state orchestrators with rollback compensations.
Interview: Saga pattern implementation details. Expect questions comparing Choreography vs Orchestration, writing compensating transactions, managing out-of-order events, and enforcing idempotency.
Introduction
When you replace distributed transactions with independent local transactions, you must ensure that all steps in a business process complete successfully. If an order is created and payment is charged, but inventory allocation fails, the system enters an inconsistent state unless you revert the previous steps.
The **Saga Pattern** solves this. A Saga is a sequence of local transactions. Each transaction updates database records locally and publishes an event. If a step fails, the Saga executes **Compensating Transactions** (explicit rollbacks) to undo changes made by the preceding steps.
Why It Matters
Sagas enable eventual consistency across microservice databases without resource locking, maintaining high system availability and throughput.
Choreography vs. Orchestration Sagas
Sagas can be coordinated in two ways:
• Choreography (Decentralized): Services listen to event brokers and execute steps independently (e.g. OrderService publishes OrderCreated, PaymentService consumes it, charges the client, and publishes PaymentCharged). Simple to configure but hard to debug as the number of services grows.
• Orchestration (Centralized): A dedicated class (Orchestrator) manages execution by sending commands and listening to reply channels. It explicitly directs transaction steps and triggers compensations during failures, making workflows easy to track.
Practical Example
Let's write an Order Orchestrator class in Spring Boot that manages a checkout workflow and triggers compensation during failures:
Quick Quiz
Q1: Why must compensating transactions in a Saga be designed to be idempotent?
A) Because they run inside a single database transaction.
B) Because they can be triggered multiple times due to network retries, and repeating them must not corrupt database state.
C) It makes the code compile faster.
D) It encrypts the compensation payload.
Answer: B — Network partitions can cause retry runs. Idempotency guarantees that executing the compensation multiple times has the same effect as executing it once.
Scenario-Based Challenge
Production Scenario:
You implement a Choreography-based Saga. During a payment failure, the payment service publishes a PaymentFailed event. However, due to a network partition, the inventory service never receives the event, leaving the customer's inventory locked indefinitely. How do you resolve this?
To prevent orphans and lock leaks during failures:
1. **Use Message Acknowledgment (At-Least-Once):** Configure Kafka or RabbitMQ to require consumer acknowledgments, ensuring the message broker retries delivery until the inventory service consumes the event.
2. **Implement Timeouts:** Configure an expiration window on inventory locks. If the inventory service does not receive a confirmation event within 15 minutes, it automatically releases the items.
3. **Outbox Pattern:** Ensure the payment service saves the failure event to its Outbox table atomically, guaranteeing the event is eventually published to the broker.
Interview Questions
1. Can a compensating transaction in a Saga trigger a SQL ROLLBACK command?
No. A SQL rollback only works before a transaction commits. In a Saga, each local transaction commits immediately to free resources. Therefore, compensation requires executing a new, forward compensating action (such as issuing a refund or restocking inventory) to undo the committed changes.
Production Considerations
For complex Sagas, use state machines (like temporal.io or Spring State Machine) to manage state transitions, timeouts, and retries persistently, preventing state loss during application crashes.