Distributed Transaction — Implementation using REDIS and NodeJS
The distributed transaction implementation has become one of the biggest challenges in microservice development due to its distributed nature. There are some well-known patterns available to address this problem. In this article, I am going to explain a sample implementation done using Redis
and Nodejs
. First of all, let's discuss the background of the problem which we are going to solve.
In design, RDBMS
systems support ACID
transactions. Transactions revert themselves if any statement gets failed. The Microservice model doesn't support this because of the dedicated datastore available for service. Most of these datastores behave according to the BASE
properties. You can read more about ACID
vs BASE
from this article (Link).
It is important to understand a complete ACID transaction
is not possible in the microservice model. Hence your distributed transaction implementation is highly subjective to the problem domain and expectation of the system. I have made some assumptions when implementing this sample.
Let's see the component design of the solution first.
Components
- REST Service
- Order Service
- Payment Service
- Delivery Service
- Status Service
REST Service
The REST service is the entry point. It accepts and validates the request before generating an id for the transaction. The generated id returns to the client in the http response. Validated request forwards to the ORDER: QUEUE
for further processing.
REST Service Request and Response
Data get stored in an Eventual consistent manner so anytime we can replay the transaction and do manual or batch reconciliation on failure.
Order Service/Payment/Delivery Services
Each Service reads the request, processes it further, and embeds additional details such as cost. Then forwards the event to the next queue. The reconciliation process is also implemented in the same service. So the responsibility of reversing the transaction is given to the relevant service.
Recovery
Let's look at the reconciliation process.
The diagram shows how the recovery process gets executed when the payment operation fails. I have simulated this by setting item id 200 as a failure case. In this scenario, the payment service informs the Order service to revert the transaction. Order service changes the soft status to failure. The front end can see that transaction failed. There are some best practices when designing reversing transactions. The reverse process should be simple and it shouldn't fail easily. Irreversible
steps such as send SMS should execute at end of the transaction.
Conclution
The implementation done in this article is very simple. The actual problem domains are much more complex and may involve dozens of services and hundreds of events. But the basic concepts remain the same. There are lots of technologies and concepts that can help to solve similar issues (Link).
I have used Redis
as the queue in this sample. But, Kafka
might be a better queue selection for production as we can use Kafka ack
to implement application-level fault tolerance, Kafka Connect
with Debezium
as CDC solution and Kafka
itself as the event sourcing database.