Nowadays microservices is one of the biggest buzzword when it comes to choosing an architecture.
I want to talk about the ugly side of this “silver bullet”. One of the most known problem with microservices are the transactions.
Wait a second, what are transactions?
Transactions are single “bulk” actions that need to be accomplished within the ACID concept.
More on this in link: ACID.
The most common use of this is databases transactions, or simplifying with an example: When you’re starting to persist some data in a database table and need to commit or revert after the first action; the role transaction is the whole process, since the first SQL (or no SQL) command, until it revert or commit at the end.
Ok. But, what is the problem with microservices?
Let’s suppose we have 2 microservices: A and B, which have started a process on A and in the middle of this process it calls B to persist some information.
After it has done some actions on microservices A to end the process. On a Happy Path, this looks like a normal and simple thing.
What happens with the persisted information on Microservice B if we get an error after it persist on Microservice A? If you understood our example you got it right! We have created an inconsistency on persisted data because of the difference contexts.
1- Do not segregate the context
The obvious thought about the example in question is: Why are they separated?
And that’s the answer to our problem.
The easier way to control transactions with databases is the most simple one, don’t separate the contexts of the domains, if you could do the persistence only in one microservice, the classic commit or revert database approach could be used. The biggest problem is, it only makes sense when you are in the transition between some monolithic architecture to a microservices, because we’ll only have this option when, firstly the domain is not yet properly segregated, and second, we are talking about only one database.
2- Two Phase Commit (2PC)
The 2PC is an algorithm of distributed systems that uses a coordinator to start and finish all transactions that has more than one participant.
In this case, the name 2 Phase commit comes with the use of two phases, first the Prepare Phase, this is when all the transactions participants notify the coordinator to get prepared, than the second one is the Commit or Rollback phase, when all participants notify again the coordinator but with a status, if one has gone to a problem, the transaction get on Rollback phase.
This is a more complete approach because it don’t care about how the contexts are segregated, but you need a coordinator that knows all of them.
This shows that there is a big advantage on not depending anymore about the contexts.
You can use heterogeneous technologies, and not care so much about the databases like the traditional transactions.
The biggest disadvantage is: there is a blocking protocol. Every participant of the transaction will be blocked until all processes have been completed. This could be a big problem for huge processing routines. Examples of algorithms: XA Standard and REST-AT Standard
3- Eventual consistency
Thinking about the “laws” for good software, this is the best known approach: Using the mechanism of “Everything you do, you need to know how to undo”, this concept in theory is really simple.
When talking about our example, if microservice A gets an error on its end, it needs to know how to call microservice B who need to know how to undo what it did. Okay, really simple, isn’t it? Not so much… because you created two new “problems”.
First, the complexity of this routine got extended, it has more calls, and always needs to have an undo path; and second, what happens when you call the undo, and the undo gets an error?
With this, we have a big advantage in that you don’t have a blocking protocol, and there are a lot of cases that you can undo, getting another answer that doesn’t depend on the transactional context to that problem. The big disadvantage is that is not so simple to get it done, can be really hard to implement this kind of algorithm, getting the logic to do and undo things with logics of retry.
Saying this, the biggest pattern that used that is the SAGA Pattern, this is the mostly used on big cases of microservices, to get its user of being done, normally it uses an circuit breaker framework for this kind of approach. SAGA implementations for Java, C#
This an introduction to the problems and solutions with advantages and disadvantages Entering in each of this would need a couple articles, my goal is to help you guys and remember there isn’t a silver bullet for any thing, but mostly suitable for any kind of situation, this is yet a difficult problem when talking about distributed software that needs to evolve.
I hope this helps you to get closer to an easier and most enjoyable implementation for a really complex architecture like microservices.
About the author
Allan Peres is graduated in Internet Systems at Unicesumar, post graduating at Orientation to Objects in Java and Architecture of Distributed Systems.
Works as Developer at IT Services unity at DB1, with great interest in enterprise software architecture, team leadership and agnostic language.
Future Software Architect and University Professor.