Across microservices, transaction needs to be done by exposing Prepare & Commit APIs. Also there needs to be a transaction manager to coordinate transaction.
For example, Assume there are 2 different Bank and $100 from Account_A of Bank1 has to be transferred to Account_B of Bank2. Furthermore assume that central banking authority is responsible for transaction to finish
the way 2PC will work is like following:
Central banking authority(Transaction Manager) will receive request to transfer $100 from Account_A from Bank1 to Account_B from Bank2.
a. https://CentralBank/Transaction?from=Bank1-Account_A&to=Bank2-Account_B&amount=100
Central Bank will save this into its transaction database with some transaction Id = 123. Also it will return transaction id to call, so that at later point it can call to get status of transaction.
a. add transaction 123 in database with status open
PREPARE PHASE Transaction Manager will issue following RPC commands:
a. https://Bank1/Prepare?Account=Account_A&money=100&action=subtract&transactionid=123
b. https://Bank2/Prepare?Account=Account_B&money=100&action=add&transactionid=123
COMMIT PHASE Once it gets successful response for both the calls in Prepare phase, then it moves to Commit phase where it issues following commands:
a. move transaction 123 to committed state
b. https://Bank1/Commit?transactionid=123
c. https://Bank2/Commit?transactionid=123
Once it gets successful response for both the calls in commit phase, central bank can move transaction to Completed state(optional)
If any of the step from PREPARE or COMMIT phase fails then Transaction coordinator aborts the transaction by issuing following commands:
a. move transaction 123 to Failed state
b. https://Bank1/Rollback?transactionid=123
c. https://Bank2/Rollback?transactionid=123
Problem above is form of Distributed Atomic commit, and 2PC is one way of doing it. Also note 2PC has lot of downsides like what if after PREPARE phase central bank crashes. Also what if 4.c step fails but 4.b succeeds, etc. Discussing those is very vast study in itself, but still is something to be aware of. Despite having lot of downsides 2PC is used widely because of its simplicity.
Do we need to use TransactionManager service as a separate microservice to provide 2PC between many microservices?
Theoretically No. If you closely observe any of the bank(Bank1 or Bank2) can act as transaction manager as well(it just needs a separate database table Transaction), but practically lot of time it is kept as separate microservice.
TransactionManager
, which will be a part of this application, so we could delegate managing distributed transactions across *many databases/JMS systems toTransactionManager
. But I can't imagine how to manage multiple databases across different microservices where we use database per service pattern and each service has access only to its DB. How to do 2PC/XA distributed transactions in that case? – Dmitriy Mishenyov