Two-phased commits are an “architectural smell”

Brian Jones
CodeX
Published in
2 min readJun 16, 2023

One of the common challenges in designing a microservice architecture is how to handle distributed transactions across multiple services. A distributed transaction is a transaction that involves multiple resources, such as databases, message queues, or external APIs. A distributed transaction needs to ensure that either all the resources are updated successfully, or none of them are updated at all. This is also known as the atomicity property of transactions.

There are two general patterns to achieving this. The Saga pattern, and the two-phased commit.

The saga pattern involves a transaction being implemented as a series of smaller transactions. If one fails then the saga handles this, possibly by rolling back previous commits. This isn’t what this article is about though.

The two-phased commit involves a coordinator service that initiates the transaction and communicates with all the participant services involved in the transaction. The coordinator service asks each participant service to prepare to commit the transaction and then awaits their responses. If all the participant services agree, the coordinator service sends a commit message to all of them. If any of the participant services fails to prepare or rejects the commit, then the coordinator sends a rollback message to the participants.

However, two-phased commits have some serious drawbacks that make it unsuitable for use in systems adopting a microservices architecture.

Firstly, two-phased commits require all the participant services to be available and responsive during the transaction. If any of the services is down or slow, the transaction will fail or block. This is not consistent with the availability and resilience principles of microservices.

Secondly, two-phased commits require all the participant services to lock their resources during the transaction. This reduces the concurrency and scalability of the system, as other requests that need to access the same resources will be blocked. This affects the performance of the system.

Thirdly, two-phased commits introduce tight coupling between the coordinator service and the participant services. The coordinator service needs to know all the participant services and their endpoints, and the participant services need to implement a specific protocol to communicate with the coordinator service. This violates the autonomy and decoupling principles of microservices.

If you are using two-phased commit within your microservices architecture then you should consider if your microservices architecture is working for you. There are a number of alternatives.

One option is to break down the service, into smaller and more independent services that don’t require coordination.

A second option is to look at other distributed transaction patterns such as the saga.

A third option is to do a larger service, think small monolith. In which you can use more database-driven natural transaction patterns.

Whichever option you take. Architectural patterns are not an end in their own right. Be pragmatic, and make them serve you.

--

--