Dark matter force: prefer ACID over BASE
microservice architecture architecting dark energy and dark mattera dark energy, dark matter force
System operations are best implemented as ACID transactions. That’s because ACID transactions are simple and familiar programming model. The challenge, however, is that ACID transactions don’t work well across service boundaries. All participating services and their databases must, for example, support 2PC, which limits your choice of technologies and reduces availability since all participants must be available to commit the distributed transaction. Consequently, an operation that spans services must use eventually consistent (BASE) transactions (e.g. Sagas). The problem with BASE transaction is that, as I describe in my book, and in my distributed data patterns bootcamp, they lack of isolation (the ‘I’ in ACID) of ACID transactions and can be much more complicated to implement. As a result, the desire to implement an operation using an ACID transaction acts as an attractive force between the operation’s subdomains.
The strength of the force depends upon the operation
The strength of the force depends upon the details of an operation. Let’s consider a couple of examples
Example: createOrder()
- Order Management
and Fulfillment
subdomains
Consider a createOrder()
operation that does the following:
- Creates an
Order
in theOrder Management
subdomain - Notifies the
Fulfillment
subdomain to deliver theOrder
The downsides of distributing this operation across the Order Service
and the Fulfillment Service
are fairly minor.
The createOrder()
operation could be implemented by a simple choreography-based saga.
The Saga consists of two steps:
- The
Order Service
creates anOrder
and publishes anOrderCreated
event Fulfillment Service
handles theOrderCreated
event and schedules the delivery of theOrder
In this example, the lack of ACID transactions is not a significant problem. Let’s now look at a more complex example.
Example: createOrder()
- Order Management
, Customer Management
, Inventory Management
and Fulfillment
subdomains
Consider a more complex version of the createOrder()
operation that does the following:
- Creates an
Order
in theOrder Management
subdomain - Reserves credit in the
Customer Management
subdomain - Reserves inventory in the
Inventory Management
subdomain - Schedules a shipment the
Fulfillment
subdomain to deliver theOrder
Let’s imagine that these four domains are implemented as separate services and that createOrder()
were implemented using a Saga.
While this might seem simple, it’s actually quite complex:
- The first challenge is that steps 2 and 3 can fail. As a result, steps 1 and 2 need compensating transactions to undo the changes that were previously committed.
- The problem with compensating transactions that now the saga might update entities twice - once in the regular transaction and then again in the compensating transaction.
As a result, there’s the possibility of what is termed a dirty read.
Another transaction or saga could read a
Order
orCustomer
after the regular transaction and before the compensating transaction has been executed. It’s really what is essentially uncommitted changes that can lead to incorrect results. Preventing dirty reads requires additional complexity in the saga implementation.
A simpler solution would be to eliminate the need for compensating transactions by collocating the Order Management
, Customer Management
, and Inventory Management
subdomains in a single Order Service
.
The drawback of this approach is that it might violate one or more of the dark energy forces.
Using eventual consistency requires careful design
As you can see, using eventually consistent transactions (BASE) can be simple in some cases and complex in others. Unlike ACID, using eventual consistency can require careful design and analysis to prevents bugs caused by the lack of isolation. Moreover, you also need to determine whether the additional complexity is a worthwhile trade-off. This is one of the benefits of using the Assemblage design process.