69

Saga: distributed transaction = compensations minus isolation

#
  • pattern
  • context-dependent
  • fragment

Conflicts with: Saga ↔ isolation

Distributed ACID transactions exist (XA, 2PC) and give a stronger contract, atomicity across all participants at once, but they pay in coordination: participants hold locks until the protocol completes, and between prepare and the decision they are not free to decide on their own. A replicated coordinator (a decision log under Raft) removes the single point of failure, but it removes neither the locks nor the extra round trips. For long-running cross-service processes a saga is more practical: a chain of local transactions, each with a compensating action. The main cost people forget: isolation is gone. Intermediate states are visible to other participants, and a compensation does not “roll back”; it writes a new fact (a refund ≠ cancelling the charge).

Code example (fragment)
Javafragment
// steps and compensations are declared in pairs; state is an explicit persisted type
sealed interface SagaStep permits ReserveInventory, ChargeCard, BookShipment {}

record ChargeCard(Cents amount) implements SagaStep {
    Compensation compensation() { return new RefundCard(amount); } // a new fact, not a rollback
}

// isolation by hand: a semantic lock; an order in status PENDING_PAYMENT
// is invisible to resale until the saga completes or is compensated

An honest assessment. As the number of steps grows, an explicit orchestrator (a state machine) is usually easier to observe and recover than implicit choreography: the question “who owns the process right now?” stops having a clear answer exactly when you need one at 3 a.m. This is a heuristic, not a threshold: for two participants with a clear contract, choreography is cheaper. A long-running saga = a workflow engine (Temporal and the like), which is not a new idea but a persisted saga orchestrator.

Last technical review: 2026-08-04 · Reference · 112 principles