Question 1 · Section 17

What is Saga pattern and when to use it

In a monolith, an ACID transaction guarantees that all changes are either committed or rolled back together. In microservices, data is spread across different databases, and the...

Language versions: English Russian Ukrainian

Junior Level

Saga is a pattern for managing distributed transactions in microservices.

In a monolith, an ACID transaction guarantees that all changes are either committed or rolled back together. In microservices, data is spread across different databases, and there is no mechanism that can atomically change everything at once.

In a monolith there is one database and regular transactions (ACID). In microservices, each service has its own database, and you cannot make a single transaction across multiple services.

Saga solves this problem:

  • Breaks a large transaction into a sequence of local transactions
  • Each local transaction runs in its own service
  • If something goes wrong — compensating transactions (rollback) are executed
Example — online store order:
1. Order Service: create order
2. Payment Service: charge money
3. Inventory Service: reserve product
4. Shipping Service: ship order

If Payment fails -> cancel order (compensation)
If Inventory fails -> refund money + cancel order
// What the user sees: order created -> payment processed -> inventory reserved.
// If Shipping fails: compensation cancels everything, user sees "order cancelled".
// Eventual consistency typically takes seconds to minutes.

Middle Level

Two types of Saga

1. Choreography:

Each service publishes an event -> other services listen and react

Order Service -> "OrderCreated" -> Payment Service
Payment Service -> "PaymentCompleted" -> Inventory Service
Inventory Service -> "InventoryReserved" -> Shipping Service

2. Orchestration:

A central orchestrator manages the entire process

        Orchestrator
       /    |    \     \
  Order  Payment  Inventory  Shipping

When to use

Saga is suitable when:

  • A transaction spans multiple services
  • Eventual consistency is acceptable (not immediate consistency)
  • There is no single database

If business requirements legally require strict ACID (bank transfers), Saga may not be suitable without additional guarantees (locks, two-phase commit).

Common mistakes

  1. Missing compensation:
    Order -> Payment -> Inventory (fail)
    Need: refund(payment) + cancel(order)
    If refund is forgotten -> money is charged, no order exists
    

Senior Level

Internal Implementation

Saga log:

Each Saga must log:
1. Started steps
2. Completed steps
3. Compensations

This allows state recovery after a restart

Architectural Trade-offs

Choreography Orchestration
No single point of failure Orchestrator — single point of failure
Hard to trace the flow Easy to monitor
Cyclic dependencies No cyclic dependencies
Good for simple Sagas Good for complex Sagas

Edge Cases

1. Idempotency:

A compensating transaction may be called twice
-> Idempotency is required

refund(paymentId):
    if alreadyRefunded(paymentId): return
    doRefund(paymentId)
    markRefunded(paymentId)

2. Partial failure:

Saga: A -> B -> C (fail)
Compensations: compensate(B) -> compensate(A)

If compensate(B) also fails -> retry + alert

Production Experience

Order Saga:

@SagaDefinition
public class OrderSaga {

    @StartSaga
    @SagaEventHandler(associationProperty = "orderId")
    public void handle(OrderCreatedEvent event) {
        SagaLifecycle.start();
        commandGateway.send(new ReserveInventoryCommand(event.orderId()));
    }

    @SagaEventHandler(associationProperty = "orderId")
    public void handle(InventoryReservedEvent event) {
        commandGateway.send(new ProcessPaymentCommand(event.orderId()));
    }

    @SagaEventHandler(associationProperty = "orderId")
    public void handle(PaymentCompletedEvent event) {
        commandGateway.send(new ShipOrderCommand(event.orderId()));
        SagaLifecycle.end();
    }

    @SagaEventHandler(associationProperty = "orderId")
    public void handle(InventoryReservationFailedEvent event) {
        // Compensation — cancel order
        commandGateway.send(new CancelOrderCommand(event.orderId()));
        SagaLifecycle.end();
    }
}

Best Practices

✅ Log every Saga step
✅ Implement idempotency for compensations
✅ Use timeouts for each step
✅ Monitor latency between steps

❌ Don't use for simple CRUD operations
❌ Don't forget compensating transactions
❌ Don't ignore partial failures

Interview Cheat Sheet

Must know:

  • Saga — pattern for distributed transactions without a single database
  • Two types: Choreography (event-driven) and Orchestration (coordinator-driven)
  • Compensating transactions undo steps in reverse order
  • Eventual consistency — data is not consistent instantly
  • Idempotency is critical for compensations
  • Transactional Outbox guarantees event delivery
  • Choreography — 2-3 services, Orchestration — 5+ services
  • Saga timeout protects against hung transactions

Common follow-up questions:

  • How does Saga differ from 2PC? Saga does not block resources, works with heterogeneous databases, but provides eventual consistency instead of strict ACID.
  • What happens if a compensation also fails? Retry + alert for manual intervention.
  • When NOT to use Saga? For simple CRUD operations in a single service or when strict ACID is required (bank transfers).
  • How to monitor Sagas? Log every step, track duration between steps, use distributed tracing.

Red flags (DO NOT say):

  • “Saga guarantees ACID” — no, eventual consistency
  • “Compensation = ROLLBACK” — no, it’s a new transaction
  • “Choreography is better for 10 services” — no, orchestration is
  • “Saga is not needed if you have Kafka” — Kafka is only transport, logic is still needed

Related topics:

  • [[2. What is the difference between choreography and orchestration in Saga]]
  • [[3. How to implement distributed transactions in microservices]]
  • [[4. What are compensating transactions]]
  • [[13. What is Database per Service pattern]]
  • [[22. What is distributed tracing]]