Distributed transactions become painful exactly where the business wants atomicity but the architecture has already split across multiple services and stores.
In real engineering work, this chapter helps choose between 2PC, 3PC, Saga, and transactional outbox by domain boundaries, acceptable failure behavior, blocking risk, and coordination cost.
In interviews and design conversations, it is especially useful when you need to speak plainly about timeout semantics, partial commit, compensations, and idempotency requirements.
Practical value of this chapter
Design in practice
Helps choose transaction patterns by domain boundaries and acceptable failure behavior.
Decision quality
Compares 2PC, 3PC, and Saga by latency, locking impact, and operational complexity.
Interview articulation
Provides a clear narrative for coordinator, participants, commit point, and recovery.
Risk and trade-offs
Makes blocking, partial-commit, timeout, and idempotency trade-offs explicit.
Context
Consistency and idempotency
Distributed transactions are one way to ensure consistency, but not the only one.
Distributed transactions: two-phase commit (2PC) and three-phase commit (3PC) are needed where a business invariant requires changing several independent resources as one unit and an intermediate state is unacceptable. The price of that atomicity is extra latency, blocking, and the very recovery-after-partial-failure that the protocols exist to handle.
When is a distributed transaction needed?
- One business operation must atomically change several independent resources or services, and splitting it into separate steps is not an option.
- Even temporary divergence does not survive here — an intermediate result already counts as an error.
- When a commit lands only partially, the cost moves straight into finance, regulation, or what the user sees.
How two-phase commit (2PC) works
2PC: two-phase commit
Prepare -> votes -> global decision (commit/abort)
The coordinator collects participant votes and makes one global commit/abort decision for the whole transaction.
Strengths
- Simple coordination model that is easy to reason about.
- Clearly separates preparation from the final decision.
Risks
- Blocking is possible if the coordinator fails at the wrong time.
- Highly sensitive to timeout/retry tuning.
Protocol Steps
Current Command
Click Start to play the protocol step-by-step.
Coordinator
Waiting to start
Coordinator commands: 0
Participants
3 participants
Active step: 0 / 8
Order
participant A
Waiting for commands
Involved in steps: 0
Payment
participant B
Waiting for commands
Involved in steps: 0
Inventory
participant C
Waiting for commands
Involved in steps: 0
How three-phase commit (3PC) works
3PC: three-phase commit
CanCommit -> PreCommit -> DoCommit
Adds an intermediate PreCommit phase to reduce blocking risk when the coordinator has issues.
Strengths
- Reduces the chance of getting stuck in an uncertain state.
- Explicitly separates intent from final commit.
Risks
- More network rounds and a more complex state machine.
- Requires very careful timeout and recovery tuning.
Protocol Steps
Current Command
Click Start to play the protocol step-by-step.
Coordinator
Waiting to start
Coordinator commands: 0
Participants
3 participants
Active step: 0 / 12
Order
participant A
Waiting for commands
Involved in steps: 0
Payment
participant B
Waiting for commands
Involved in steps: 0
Inventory
participant C
Waiting for commands
Involved in steps: 0
Alternative
Event-Driven Architecture
More often than it looks, Saga plus transactional outbox is a better fit than global 2PC/3PC: no blocking, and consistency reached step by step.
Trade-offs and alternatives
2PC is easy to explain and adopt. The bill arrives when the coordinator fails after the prepare phase: participants stay blocked and hold their resources until it comes back.
3PC removes that hang at the cost of one more network round — and the protocol state machine grows with it, which someone now has to maintain correctly.
Network partitions, timeouts, and recovery after partial failures hit both protocols the same way: the error does not fail loudly, it leaves the transaction in an in-doubt state.
In a microservice architecture, a shared atomic transaction across services is almost always more expensive and more fragile than the diagram suggests — coupling grows, and one stuck participant slows the rest.
Saga (orchestration/choreography)
The operation splits into local steps, each with its own compensating action. There is no atomicity — in return, the global lock and the coordinator as a single point of failure both disappear.
Transactional outbox
The event is written in one local transaction together with the business change, and a separate worker publishes it later. That removes the risk of the database write and the message drifting apart.
Idempotent commands + reconciliation
When a retried command is safe, a partial failure stops being a catastrophe: background reconciliation drives the system back to a consistent state instead of a manual cleanup.
Domain redesign
The cheapest way to handle a distributed transaction is to not have one. Move aggregate boundaries so the invariant lives inside a single service.
Practical checklist
- The boundary is drawn explicitly: where strict atomicity is truly needed, and where eventual consistency is enough.
- The coordinator has a recovery strategy and a durable log — after a restart it knows which transactions are left in-doubt.
- Timeout policies are tested not on the happy path but under network partitions and delays.
- A repeated COMMIT or ABORT does not break a participant — the handling is idempotent.
- For disputed cases there is a business mechanism for compensation and manual resolution, not just hope that the automation gets it right.
Frequent anti-pattern: introducing 2PC between services as “just a transaction,” without first costing the blocking, the retry model, and the recovery — and then learning about them in production.
References
Related chapters
- Consistency and idempotency - Idempotency is required to safely handle retries and recovery after failures.
- Event-Driven Architecture - Saga and asynchronous coordination as a practical alternative to a shared distributed transaction over the XA standard.
- Fault Tolerance Patterns - Timeout, retry, and bulkhead strategies define how a transaction behaves under failures.
- Leader Election: patterns and implementations - A transaction coordinator is essentially a leader: its unavailability and re-election decide how long participants stay blocked.
- Testing Distributed Systems - Reproduce and break partial commit, timeout, and recovery in tests — otherwise their scenario plays out for the first time in production.
