Introduction to the Saga Design Pattern
Overview
As discussed in the microservices foundations page, a distributed system built from independently-databased services runs into the CAP boundary: a blocking, strongly-consistent coordinator (2PC/XA, a CP system), or an available system that reconciles state eventually (an AP system). The Saga pattern is the concrete design for the AP side of that trade-off. It replaces a single global transaction with a long-running transaction (LRT) — a sequence of smaller, independently committed local transactions, each of which can be undone by a corresponding compensating transaction if a later step in the sequence fails.
What is the Saga Pattern?
The Saga pattern is a microservices architectural pattern that ensures data consistency across multiple services without relying on a distributed transaction coordinator. A saga is a sequence of local transactions, where each local transaction updates data within a single service and publishes a result (an event or a response) that determines the next step in the sequence.
The pattern was originally described by Garcia-Molina and Salem for long-lived database transactions and defines two possible outcomes for a saga:
-
Forward recovery: every local transaction in the sequence completes successfully, and the saga runs to completion — no compensation is needed.
-
Backward recovery: a local transaction fails partway through, and the saga executes compensating transactions for every previously committed step, in reverse order, to undo their effects.
Because there is no database-level rollback across service boundaries — once a local transaction commits, it stays committed — backward recovery is implemented entirely through application-level compensation rather than a transactional rollback. For this to work without manual intervention, every compensating transaction must be idempotent and retryable: idempotent so that redelivery or retry never double-applies the undo, and retryable so that transient failures (a timeout, a temporarily unavailable dependency) don’t strand the saga in a partially-compensated state. A Saga Execution Coordinator (SEC) is the component responsible for guaranteeing both properties are honored as it drives the saga through forward or backward recovery.
The below diagram shows how to visualize the Saga pattern for the previously discussed online order processing scenario.
Types of Saga
The two implementations differ primarily in where the knowledge of the saga’s workflow lives — distributed across every participant, or centralized in one coordinator. This is the same trade-off that shows up throughout distributed systems design: choreography favors loose coupling, orchestration favors explicit control.
-
Choreography-based Saga
-
This is an event-driven implementation. Each service performs its local transaction and publishes a domain event describing what happened (
order-created,payment-processed). Every other participant subscribes only to the events it cares about, performs its own local transaction in reaction, and emits its own event — there is no participant that knows the saga end-to-end. -
Pros: No single point of failure or coordination bottleneck; services are decoupled and can be added or removed by simply subscribing to existing events, which scales organically.
-
Cons: The overall workflow is implicit — it only exists as the sum of every service’s event subscriptions — which makes it hard to answer "what state is order #123 in?" without tracing events across services. Cyclic event dependencies and duplicated compensation logic are common failure modes as the number of participants grows.
-
-
Orchestration-based Saga
-
This is a command-driven implementation. A central orchestrator holds the saga’s workflow definition as an explicit state machine, sends a command to each participant in turn, and waits synchronously or asynchronously for a response before deciding the next transition. If a participant reports failure, the orchestrator drives backward recovery by issuing compensating commands to every previously completed participant, in reverse order.
-
Pros: The workflow is explicit and centrally observable — saga state, in-flight step, and history are queryable from one place, which materially simplifies error handling, monitoring, and recovery after a coordinator restart.
-
Cons: The orchestrator is a critical-path dependency; it must itself be made highly available and its state durably persisted (rather than held only in memory) so that an orchestrator crash does not strand in-flight sagas.
-
StackSaga implements the orchestration-based model: it externalizes the saga workflow into a durable, restart-safe state machine so that saga state is never only in a service’s memory.
Saga Orchestration Pattern
Mechanically, an orchestrator is a finite state machine whose states are the saga’s atomic transactions and whose transitions are their outcomes (success, failure, timeout).
Every saga instance — one running execution of that workflow, for example one place-order request — is tracked against a unique correlation ID, so the orchestrator can persist, query, and resume any in-flight instance independently of the others.
Each step in the workflow definition binds together:
-
the participant service to invoke,
-
the command to send it,
-
the expected reply (or timeout) that triggers the next transition, and
-
the compensating command to invoke if backward recovery reaches this step.
Key Characteristics
-
Centralized Control: The orchestrator advances the state machine one transition at a time, so step ordering and compensation ordering are enforced in one place rather than reconstructed from distributed event traces.
-
Simplified Participants: Individual services only need to know how to execute the one command and one compensating command they own — none of them need to be aware of the saga’s overall shape or of any other participant.
-
Centralized Error Handling: Timeout handling, retry policy, and backward-recovery triggering live in the orchestrator, rather than being duplicated inside every participant.
-
Durable Execution State: Because saga instance state is persisted (rather than held only in the orchestrator’s memory), an orchestrator restart or crash does not lose track of in-flight sagas — execution can resume from the last recorded transition.
Eventual Consistency
Definition: Eventual consistency guarantees that, if no new updates are made to a given piece of data, all subsequent reads of that data will eventually return the last updated value. Unlike ACID’s immediate consistency, it does not promise that a read right after a write sees that write — only that, absent further writes, all replicas converge to the same value over time.
This is the consistency side of the BASE model (*B*asically *A*vailable, *S*oft state, *E*ventual consistency) — the availability-first counterpart to ACID that AP systems adopt once CAP forces the choice between blocking for consistency and staying available.
Characteristics:
-
Latency: Writes can return as soon as the local step commits — propagation to the rest of the system happens in the background rather than on the critical path of the request.
-
Availability: The system keeps serving requests even when some nodes or services are temporarily unreachable, rather than blocking until they respond.
-
Partition Tolerance: Because nodes don’t need to agree before responding, the system degrades gracefully under network partitions instead of stalling.
-
Use Cases: Common wherever immediate cross-node agreement isn’t required for correctness — DNS propagation, web/CDN caches, and NoSQL stores such as Cassandra or DynamoDB.
Example: On a social media platform, a profile update may take a moment to propagate to every read replica — different users can briefly see different versions of the profile before all replicas converge.
Eventual Consistency in Saga
A saga is precisely this model applied to a business transaction: each step is a local, independently committed write (satisfying Basically Available and Soft state — the overall business entity is, for a time, in neither its old nor its final state), and the saga’s job is to drive the system toward convergence.
-
Asynchronous, Independent Steps: Because each atomic transaction commits to its own service’s database independently of the others, there is a real time window in which the overall business entity (e.g. the order) is only partially updated across services.
-
Convergence via Forward or Backward Recovery: The saga converges the system to a consistent state one of two ways — forward recovery (every step succeeds, and the business entity reaches its intended final state) or backward recovery (a step fails, and compensations return the entity to its original state). Either path ends in a consistent state; what eventual consistency does not guarantee is which of the two you’ll land on while the saga is still in flight.
-
Retries close the gap: Transient failures are retried rather than immediately triggering compensation, which keeps the system converging on the forward path instead of unnecessarily unwinding work that would have succeeded on a second attempt.
Classification of SAGA transactions
According to the behaviors of the transaction, we can mainly identify 3 transaction types that can be happened when we use saga.
-
Success transactions
-
Compensating/Revert success transactions
-
Compensating/Revert failed transactions
Successful Transactions (Primary Execution Success Transactions)
GET_USER_DETAIL is not considered as an atomic transaction.
It’s just a query operation to get the user information. refer to Query Operation for more details.
|
Here we have 4 executions (3 atomic transactions) with 4 microservices. The entire transaction(Business transaction) will be completed after successfully executing the 4th atomic execution. All the primary executions are done successfully as we accepted those kind of transactions are called as Successful transactions.
Here is the summarized diagram for Success transaction.
Compensating Success Transaction
At this time, An exception occurred when make payment execution is executed. Then the primary executions process is stopped due to the error, and the compensating process is started to undo the successfully executed executions so far. And finally, the compensating process is also completed.
Even though this is a failed transaction from the business perspective, this is one of the successful transaction types from the Saga perspective. Because we have managed to keep the eventually consistent state by compensating the successfully executed transactions.
Here is the summarized diagram for compensating successful transaction.
Compensating Failed Transaction
At this time, An exception occurred when make payment execution is executed. Then the primary executions process is stopped due to the error, and the compensating process is started to undo the successfully executed executions so far. While then, unfortunately, an error occurred in compensating the process called CANCEL_ORDER.
| This scenario represents a theoretical edge case in distributed systems, developers should implement robust error handling to prevent compensating transaction failures. Compensating transactions must maintain idempotency and should only fail due to transient infrastructure issues such as Resource Unavailability problem. When encountering resource unavailability, implement exponential backoff retry mechanisms with circuit breakers to ensure eventual consistency is achieved once resources become available. |
| Don’t worry about handling those complex situations. Stacksaga provides the way that you can manage them easily. |
Here is the summarized diagram for compensating failed transaction.
Challenges and Considerations of Using Saga
-
Complexity: Designing the workflow, its compensating counterpart for every step, and the failure paths between them requires more upfront design than a single ACID transaction would.
-
Idempotency: Every compensating transaction — and every retried local transaction — must be idempotent, or retries and redeliveries can corrupt state instead of repairing it.
-
State Management: The orchestrator must durably persist saga instance state (not just hold it in memory) and correctly resume in-flight sagas after a restart, or a crash mid-saga can strand a business transaction half-completed.
-
Lack of Isolation: Because each local transaction commits independently and immediately, other services can observe intermediate, not-yet-consistent state while the saga is still in flight — there is no equivalent of ACID’s isolation level across the whole saga.
StackSaga’s orchestration engines, event-sourced state store, and retry agents exist specifically to absorb these challenges, so that individual services only need to implement their own local transaction and its compensating counterpart.