In a typical microservice landscape, a business process can trigger individual transactions across multiple microservices. Since each microservice (by definition) controls its database, transaction management in such systems could be a tricky process. To add more complexity, the business transaction could be a long-lived process, where each service participating in the business process can complete or fail due to unrelated events. For eg, in a food delivery system, a registered order can fail long after order and payment processes were complete because there were no delivery services available for that day.
Transactionality in such systems can be realized by applying some well-known design patterns. In this blog post, we will understand a design style called the SAGA design pattern that can be used to manage a distributed transaction across multiple services.
All the code presented in this blog is available on Github.
What is SAGA⌗
SAGA is a design style used to preserve data consistency across several services. In this design pattern, a business process is set up as a sequence of local transactions updating only their local databases, and publishing a completion message to trigger the next local transaction. If any of these local transactions fail due to invalid business (or technical) conditions, then a failure message is published to the preceding transactions which informs them to rollback and cleanup any side-effects produced so far.
Types of SAGA⌗
There are two usual ways to enforce a SAGA pattern:
- Orchestration: Where a central coordinator is responsible for instructing individual services to continue or rollback.
- Choreography: Where each service listens to an event from other services, to determine if it should proceed or rollback.
SAGA systems are generally implemented as asynchronous services listening on an event bus (message queues, topics, etc) to decide if it should proceed or rollback. In this blog, we will implement a SAGA transaction using an orchestrator.
Let’s continue with our previous example of a food delivery system. To implement the SAGA design pattern, we need a central orchestrator called
Order Orchestrator. The orchestrator can be a process manager that receives the initial order request. Its core responsibilities are.
- Receive process initiation request and call the first service.
- Listen to success or failure feedback from the currently running service.
- For successful feedback, ask the next service to proceed.
- For failure feedback, relay a message to all participation services to rollback their transactions.
To demonstrate the implementation of SAGA, we will create an orchestrator which is triggered by an incoming HTTP request. The interested users can clone the repository, and follow the instructions in the readme to run the example locally. We also have a few microservices, each of which is capable of performing two operations.
- Start: When the orchestrator asks the service to execute an operation. (Create order, make the payment, prepare food, make delivery)
- Rollback: When the orchestrator asks the service to rollback the transaction.
Each micro-service replies back to orchestrator on the Reply channel. The service replies to the operation with 2 statuses
- Done: When the intended operation was successful. The orchestrator executes the next service in the pipeline.
- Error: When either of the service errors out. The orchestrator asks all the services to rollback the transaction.
In the figure above, the green arrows show the success path while the red ones show the rollback flow.
The orchestrator communicates with the microservices on their dedicated channel via a message which could be of types
Rollback. Services on the other hand reply to the orchestrator on Reply Channel with the operation status
Done if successful,
Error in case of operation failure. Based on these, statuses on the reply channel, the orchestrator decides if it should continue to the next logical operation or ask the microservices to roll back the transaction.
The orchestrator exposes an endpoint
localhost:8080. Opening this endpoint triggers the SAGA transaction. To demonstrate rollback, the delivery service errors out, thereby rolling back the entire transaction. The users should observe the same transaction-id on all the terminals.
References and further readings⌗
If you have any comments or found a mistake, please contact me here