Saga pattern

On this page 7

The saga pattern is a way to keep a multi-step business transaction consistent across several services or systems without a single distributed lock. The transaction is split into a sequence of local transactions, each committed independently, and every step has a compensating action that semantically undoes it if a later step fails. In the reliability of automated workflows, sagas are the standard answer to the question of how to roll back work that spans systems which cannot share one database transaction.

The idea dates back to database research in the late 1980s, where long-lived transactions were broken into steps to avoid holding locks for hours. Today the pattern is central to microservices, workflow engines and integration platforms, where a booking, an order or an onboarding process touches payment, inventory, messaging and third-party APIs.

How the saga pattern works

A saga defines an ordered list of steps T1, T2 to Tn and a matching list of compensations C1, C2 to Cn. Each step commits its own change and then triggers the next. If step Tk fails, the saga runs the compensations for the already completed steps in reverse order, from Ck-1 back to C1. The result is not an exact restoration of the past state but a business-consistent outcome, such as a refunded payment instead of an erased one.

Sagas therefore provide eventual consistency rather than isolation. Between steps, other processes can observe intermediate states, for example a reserved seat that is not yet paid. Designers handle this with explicit statuses such as pending or reserved, and with semantic locks that mark a record as being in progress.

ElementRoleExample in an order saga
Local transactionCommits one step in one systemReserve stock in the warehouse
Compensating actionSemantically reverses a stepRelease the stock reservation
Pivot stepPoint after which the saga must completeCapture the payment
Retriable stepMust eventually succeed after the pivotSend the confirmation email
Saga log or stateRecords progress for recoveryWorkflow history entry
Building blocks of a saga

Orchestration and choreography

A saga can be coordinated in two ways. In orchestration, a central coordinator, often a workflow engine, calls each step, waits for the result and decides whether to continue or compensate. In choreography, each service listens for events and publishes its own, so the flow emerges from event subscriptions without a central controller.

AspectOrchestrationChoreography
ControlCentral coordinatorDistributed event handlers
VisibilityWhole flow in one placeFlow spread across services
CouplingServices know the orchestratorServices know event types
Best forLong or complex flowsShort flows with few steps
Main riskCoordinator as a bottleneckHidden cyclic dependencies
Orchestrated and choreographed sagas compared

For automation teams, orchestration is usually the safer default because the state of every running saga can be inspected, retried and audited from a single place. Choreography suits small, stable flows where adding a coordinator would be heavier than the problem.

The saga pattern is often contrasted with two-phase commit, which locks all participants until a coordinator decides to commit or abort. Two-phase commit gives strong consistency but requires every participant to support the protocol and blocks resources during failures, which most SaaS APIs cannot offer. Sagas trade isolation for availability and loose coupling.

ApproachConsistencyLocks heldFits
Two-phase commitStrong, atomicYes, across participantsDatabases supporting the protocol
Saga patternEventual, compensatedNo global locksServices and external APIs
Retry onlyEventual if retries succeedNoSteps without business side effects
Manual reconciliationDelayed, human-drivenNoRare, low-volume exceptions
Consistency approaches for multi-system work

Sagas rely on neighboring reliability practices. Idempotency lets a step or compensation run twice without double effects. A retry policy with exponential backoff handles transient faults before compensation is triggered. A dead letter queue captures compensations that themselves keep failing, so a person can intervene.

Designing compensating actions

Compensation is the hardest part of a saga. Some actions are easy to reverse, such as canceling a reservation; others are not reversible at all, such as sending an email or shipping a parcel. The common rule is to place irreversible steps after the pivot step, once success is practically certain, and to design them so they can be retried until they succeed.

  • Make compensations idempotent, because they may be retried after timeouts.
  • Compensate semantically: issue a refund or a correction record rather than deleting history.
  • Persist saga state before calling each step, so recovery knows where to resume.
  • Set timeouts for every step, since a missing reply is the most common failure.
  • Alert on stuck sagas that remain in an intermediate state too long.

Pitfalls of the saga pattern

The most common mistake is assuming a saga behaves like a database transaction. Without isolation, dirty reads and lost updates can occur when two sagas touch the same record. Another pitfall is the ambiguous failure: a call times out, and the saga cannot tell whether the step committed. Querying the target system for status before compensating, or using idempotency keys, resolves most of these cases.

Teams also underestimate testing. Each failure point creates a different compensation path, so a saga with five steps needs tests for every possible failure position, including failures during compensation itself.

A non-technical view

Think of planning a trip. You book a flight, then a hotel, then a rental car. If the car is not available, you do not leave the flight and hotel in place. You cancel them one by one, in reverse order.

The saga pattern does the same for software. Each step is done separately. If something later fails, the earlier steps are undone with their own cancellation actions. The end result always makes business sense.

A concrete example

An online event platform sells tickets through an automated workflow with four steps: reserve seats, charge the card, issue tickets and send a confirmation. Each step calls a different service, and the payment provider is an external API.

The team implemented the flow as an orchestrated saga in a workflow engine. If the payment fails, the saga releases the seats. Card capture is the pivot step, so ticket issuing and the email are retried with backoff until they succeed. When a payment call timed out during a provider incident, the saga queried the payment status before deciding, and no customer was charged without receiving tickets. Stuck sagas older than fifteen minutes now raise an alert for the operations team.