In this article, we are going to see about the Saga pattern which is an asynchronous pattern that performs a sequence of transactions in each microservice and publishes messages or events to proceed to the next step. If any step fails in between, compensating steps will be executed by the Saga pattern to reverse the transaction
We can see from the above diagram that the Saga patterns perform a sequence of local transactions in each service. Each service updates its database and then publishes a message or event which will trigger the next local transaction.
So we have to write the logic to commit the transaction and also have a mechanism to reverse the flow when something goes wrong anywhere in the transaction. All the transactions and compensating transactions will happen through the listeners that listen to an event or a message from the queue
The compensating transaction must be idempotent and should be able to retry if the previous try fails
The saga pattern can be implemented using 2 ways namely
- Choreography-Based Saga Pattern
- Orchestration-Based Saga Pattern
In the Choreography pattern, there is no centralized point of control and the services will listen to a message broker to read the message and emit events/messages back to the queue for other services to listen and consume
The following steps are executed in the Choreography pattern
- Order Service gets the request from a user to create an order and hence the order is created in PENDING state
- After creating the order in Pending state, it emits an event in the Order Events Channel or Queue
- The Customer Service listens to the event and attempts to reserve the credit
- Then it emits the event to Order Service
- The Order Service now approves the Order or Rejects it based on the outcome of the event received from the Customer Service
- The Choreography pattern is easy to implement
- This is good for use cases with few services participating in the transaction
- There is no Single point of failure as the transaction is distributed across services
- When more services are added to the transaction, then Choreography patterns will get complicated
- Eg: If Order service needs to event to Customer service and Customer Service emits an event to Payment service and so on, then the workflow gets bigger and will be difficult to track and debug
- There might be scenarios where two services might be waiting for each other’s events and end up in a deadlock state
In the Orchestration-based saga pattern, there is a centralized component that controls the transaction and this component is called the Orchestrator. It issues commands to the services and decides to commit or abort the transaction based on the outcome received from the saga participants. If any of the microservices fail, the orchestrator will invoke the compensating transactions. The Orchestrator can exist inside the microservice that triggers the flow of transactions or the orchestrator can exist as a separate component outside the service
- Orchestrator as a stand-alone component
As we see in the diagram below, the Orchestrator is a stand-alone service that interacts with other services that are involved in the transaction. Every service receives the command from the orchestrator and emits the event response back to the orchestrator.
2. Orchestrator as a component inside Order Service
As we see in the below diagram, the Create Order Saga is the Orchestrator component that resides inside the Order Service
The following steps are executed in the orchestrator pattern
- When the user places the order, the endpoint in Order controller is invoked, which then calls the Order Service
- The order service creates the Create Order Saga Orchestrator, which is nothing but an orchestrator (object) that tells the microservices what local transactions to execute
- The saga orchestrator creates the Order in PENDING state
- It will then send the Reserve Credit command to the Customer Service
- The Customer Service attempts to reserve the credit and sends the outcome back to the Saga orchestrator
- The saga orchestrator approves or rejects the order based on the outcome
- Unlike the Choreography pattern, the Orchestrator pattern can be used for complex workflows and any new services can be added as part of the transaction and can be managed
- Having an orchestrator as a transaction coordinator helps to control the transaction and the flow of activities
- No possibility of cyclic dependencies
- Saga participants i.e microservices are independent and do not know about other services and hence separation of business logic
- There is a design complexity when compared to the Choreography pattern as we have the additional implementation of a coordination logic.
- There is a single point of failure here because the orchestrator manages the complete workflow.
In this article, we saw what is an asynchronous pattern for transaction management in microservices and also explored the Saga pattern and its two variants namely Choreography based and Orchestrator based. We went in-depth about the 2 patterns and then discussed their benefits and drawbacks. Based on the use case, one of the patterns has been chosen for your application.
Saga pattern enables an application to maintain data consistency across multiple services by using local transactions instead of distributed transactions. But this programming model is a bit complicated and takes more time as we have to write the compensating transactions logic across all the services Overall, the asynchronous Saga patterns are much better and more efficient for microservices when compared to synchronous patterns like Two-Phase Commit (2 PC) and Three-Phase Commit (3 PC). But it has its own drawbacks and handling distributed transactions across multiple microservices is always problematic in terms of ACID compliance hence the distributed transactions should be avoided if possible.
I work as a freelance Architect at Ontoborn, who are experts in putting together a team needed for building your product. This article was originally published on my personal blog.