Operating a Production System
In a monolithic application with a single SQL database, you can rely on ACID transactions to ensure that a series of operations either all succeed or all fail together. This makes it easy to maintain data consistency.
But what happens in a microservices architecture? A single business operation (like placing an order) might require changes to be made in several different services, each with its own database.
How do you ensure that all three of these operations succeed? What happens if the Inventory Service fails after the payment has already been processed? You can't use a traditional database transaction because the data is spread across multiple, independent databases.
This is the problem of distributed transactions.
The classic solution to distributed transactions is the Two-Phase Commit (2PC) protocol.
How it works:
Why 2PC is Rarely Used in Modern Systems: While 2PC provides strong consistency, it has several major drawbacks that make it unpopular for modern, high-availability web applications:
Because of the problems with 2PC, most modern microservices architectures embrace eventual consistency and use a pattern called Saga to manage long-running transactions.
A Saga is a sequence of local transactions. Each local transaction updates the database in a single service and then publishes an event or message that triggers the next local transaction in the saga.
If a local transaction fails, the saga executes a series of compensating transactions that undo the changes made by the preceding local transactions.
Example: The Order Placement Saga
Let's look at our e-commerce example again.
The "Happy Path" (Successful transaction):
Create Order request to the Order Service.PENDING status, and saves it. It then publishes an OrderCreated event.OrderCreated event, starts a local transaction, and processes the payment. On success, it publishes a PaymentSucceeded event.PaymentSucceeded event, starts a local transaction, and decrements the stock. On success, it publishes an InventoryUpdated event.InventoryUpdated event and updates the order's status from PENDING to CONFIRMED. The saga is complete.The "Failure Path" (with compensating transactions):
Let's say the Inventory Service fails.
PaymentSucceeded event but finds that the item is out of stock. It publishes an InventoryUpdateFailed event.InventoryUpdateFailed event and executes a compensating transaction: it refunds the payment to the user. It then publishes a PaymentRefunded event.InventoryUpdateFailed event (or the PaymentRefunded event) and executes a compensating transaction: it updates the order's status from PENDING to FAILED.The end result is that the system is back in a consistent state. The order is marked as failed, and the user has not been charged.
Pros of the Saga Pattern:
Cons of the Saga Pattern:
In a system design interview, if you are faced with a problem that requires transactional consistency across multiple microservices, you should propose the Saga pattern. Explaining how it works with events and compensating transactions shows a deep understanding of modern distributed systems design and its trade-offs.