Coursify

System Design for Software Engineers

Distributed Transactions: Maintaining Consistency

Distributed Transactions: Maintaining Consistency

In a monolith, keeping data consistent is easy. You wrap your database calls in a single transaction, and the database ensures that either everything succeeds or everything fails (ACID). In microservices, every service has its own database. A single business process (like "Place Order") might involve the Order Service, Payment Service, and Inventory Service.

How do you ensure consistency across three different databases?

1. Two-Phase Commit (2PC)

A classic approach where a "Coordinator" asks all services to prepare for a commit, and then tells them all to commit if everyone is ready.

  • Pros: Strong consistency.
  • Cons: Extremely slow (high latency). If the coordinator or any service hangs, the whole system locks up. Not recommended for high-scale microservices.

2. The Saga Pattern

A Saga is a sequence of local transactions. Each service performs its own local transaction and then publishes an event to trigger the next service.

  • Compensating Transactions: If one step fails, the Saga must trigger "undo" actions for all previous steps to return the system to a consistent state.
  • Example: If 'Payment' fails, the 'Order' must be canceled and 'Inventory' must be returned.

Implementing a Choreography-Based Saga

  1. 1
    Step 1

    The 'Order Service' creates an order in its local database with a status of PENDING and publishes an OrderCreated event to a message broker.

  2. 2
    Step 2

    The 'Payment Service' listens for OrderCreated. it processes the payment locally and publishes a PaymentSuccessful event.

  3. 3
    Step 3

    The 'Inventory Service' listens for PaymentSuccessful. It tries to reserve items but finds they are out of stock. It publishes an InventoryFailed event.

  4. 4
    Step 4

    The 'Payment Service' listens for InventoryFailed. It immediately executes a Refund transaction and publishes a PaymentRefunded event.

  5. 5
    Step 5

    The 'Order Service' listens for PaymentRefunded and updates the order status to CANCELED. The system is now consistent, even though the order failed.

Saga Variations

  1. Choreography: Services talk to each other via events. No central controller. (Decoupled, but hard to track the overall state).
  2. Orchestration: A central "Saga Orchestrator" tells each service what to do and handles the failures. (Easier to manage, but the orchestrator is a new point of failure/complexity).

Eventual Consistency

Sagas do not provide ACID consistency; they provide Eventual Consistency. For a few seconds, the order might be 'Pending' while the inventory is being checked. You must design your UI and business logic to handle these transient states.

Common Mistakes

  • Forgetting Compensation: Implementing the "happy path" but not the "undo" path. This leads to orphaned records (e.g., a payment was taken but no order exists).
  • Circular Dependencies: Designing Sagas where Service A triggers B, which triggers C, which triggers A. This creates infinite loops.
  • Lack of Idempotency: If an event is delivered twice, the compensating transaction (like a refund) must not happen twice.

Recap

  • 2PC is for strong consistency but doesn't scale.
  • Sagas use local transactions and Compensating Actions to maintain eventual consistency.
  • Choreography is decentralized; Orchestration is centralized.
  • Idempotency is critical for both the main and compensating transactions.

Knowledge Check

Question 1 of 3
Q1Single choice

"What is a 'Compensating Transaction' in the Saga pattern?"

Distributed Transactions: Maintaining Consistency | System Design for Software Engineers | Coursify