Pradeep
3 min readAug 17, 2021

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

  1. 2 phase commit
  2. Saga using compensating transactions

The details of the above 2 solutions and their pitfalls are mentioned in the Redhat article

https://developers.redhat.com/blog/2018/10/01/patterns-for-distributed-transactions-within-a-microservices-architecture

Another solution

Another possible solution is to maintain a transaction log using a transaction log microservice, this requires

  1. a unique id for each transaction, i.e. UUID
  2. 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

  1. rollback a set of simple or complex operations on multiple microservices
  2. 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.

Failure of first microservice

In this scenario the “update customer funds” fails for whatever reason and the conductor/client aborts the transaction using the UUID.

Failure of second microservice

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.