Distributed transaction boundaries and microservices
Transactional boundaries guarantee that transactions are atomic, consistent, isolate and durable and that all the operations within a transaction succeed or all of them fail.
This guarantees that the data in the persistent store (database) is in a consistent state.
Legacy systems
Example of a atomic transaction in which an order is associated with a Customer.
Source: https://developers.redhat.com/blog/2018/10/01/patterns-for-distributed-transactions-within-a-microservices-architecture
Problem
The problem with decomposing the above into a microservices architecture is that a failure to “CreateOrder” would result in a partially complete transaction that needs to be rectified by rolling back “UpdateCustomerFund”.
2 possible Solutions
- 2 phase commit
- Saga using compensating transactions
The details of the above 2 solutions and their pitfalls are mentioned in the Redhat article
Another solution
Another possible solution is to maintain a transaction log using a transaction log microservice, this requires
- a unique id for each transaction, i.e. UUID
- The operational context
a) name of a microservice
b) name/type of operation performed (HTTP POST/PUT)
c) input to the operation (Json, HTTP headers/parameters)
d) operation to rollback the above (HTTP DELETE)
e) input to the rollback operation (Json, HTTP headers/parameters)
f) current state (failed/succeeded/aborted)
A transaction log will make it easy
- rollback a set of simple or complex operations on multiple microservices
- cleanup incomplete transactions
Happy path and 2 failure scenarios
In the happy path a transaction is logged on the Transaction log microservice prior to proceeding with updating customer funds and creating an order.
The transaction is then marked as complete in the transaction log database, fronted by the microservice.
In this scenario the “update customer funds” fails for whatever reason and the conductor/client aborts the transaction using the UUID.
In this scenario the “create order” fails, the transaction is aborted and the transaction log microservice rolls back the “Update Customer funds” using the contextual information from the database.
Happy path state
The transaction log database in a happy path scenario will look like this
Failure 1 — state before abort
Failure 2— state before abort
Prior to abort the conductor can inform the transaction log ms of the operations that failed/succeeded.
Failure 1/2 — state after abort
After aborting the “Update funds” the transaction log ms updates all the operations as aborted for that particular UUID
Conclusion
Reliable transactions an after thought in microservice based architectures.
This article provides a solution to maintaining transactional boundaries in an atomic, consistent, durable, isolated, scalable and reliable manner.