Skip to main content
Pattern

Saga (Long-running distributed transaction)

Problem

You have a business transaction that spans multiple services or databases (place order → charge payment → reserve inventory → send confirmation). Two-phase commit is too slow, too coupling, and often not available across service boundaries.

Context

Common in microservices architectures and long-running workflows. Any time your 'transaction' involves calls to 2+ independent systems that don't share a database, you're in Saga territory.

Solution

Break the transaction into a sequence of local transactions, each in its own service. Define a compensating action for each step (charge → refund, reserve → release). Execute steps in order; if any step fails, run compensating actions for all completed steps in reverse. Two flavors: choreography (each service publishes events) or orchestration (a central coordinator drives the flow).

Trade-offs

  • No ACID — you get eventual consistency plus explicit compensation logic
  • Compensating actions are semantically hard: refund isn't inverse of charge (fees), release inventory may not restore holds
  • Debugging is hard — you have to trace an event chain across services
  • Choreography = loose coupling but no single source of truth; orchestration = single point of coordination but tighter coupling
  • Failures during compensation need their own retry + escalation policy

Failure modes

  • Non-idempotent compensating actions run twice (retry) → double refund
  • Compensating action itself fails → half-completed rollback; needs manual intervention
  • Race between saga step and other transactions on the same entity → inconsistent state
  • Saga coordinator becomes a hot service; if it goes down, all in-flight sagas stall

When to use

  • Multi-service business transactions where 2PC is unavailable or too slow
  • Long-running processes (hours to days) where holding transaction locks is not feasible
  • You can define correct-enough compensating actions for every step

When NOT to use

  • All entities are in the same database — use a plain DB transaction
  • You cannot define a semantically correct compensating action (e.g., sending a physical letter)
  • Very simple 2-step operations where the failure mode is trivial (retry, no need for saga)