What Is Two-Phase Commit (2PC)? Distributed Transactions
Two-phase commit coordinates a transaction across multiple databases with a prepare phase and a commit phase, trading availability for strong consistency.
Two-phase commit (2PC) is a protocol that lets a transaction spanning multiple databases or services either commit everywhere or nowhere at all — no participant is left half-updated. It’s the classic answer to a hard problem: once you split data across more than one node, an ordinary transaction can’t just fail on one machine and succeed on another without leaving the system in an inconsistent state.
The problem: a transaction that spans two databases
A single-database transaction is straightforward — the database’s own write-ahead log and isolation mechanisms guarantee that a transaction either fully applies or fully rolls back. But once a transaction needs to touch two separate systems — say, debit an account in one database and credit an account in another — there’s no single log that spans both. If the debit succeeds and the credit fails, money disappears. If it’s the other way around, money is created out of nothing. Two-phase commit exists to make that scenario impossible by coordinating the two systems through an explicit protocol rather than hoping both operations happen to succeed together.
Phase 1: prepare
A designated coordinator node sends a “prepare” message to every participant (each database or service involved in the transaction). Each participant does everything needed to guarantee it can commit — validating constraints, acquiring locks, writing the change to its own durable log — but does not yet make the change visible or release any locks. It then replies to the coordinator with either yes (“I can commit this”) or no (“I cannot”).
Critically, once a participant votes yes, it has made a durability promise: even if it crashes and restarts before hearing back from the coordinator, it must be able to resume and eventually commit if told to. This is why the prepare phase writes to durable storage rather than just holding the change in memory.
Phase 2: commit or abort
Once the coordinator has collected votes from every participant:
- If all participants voted yes, the coordinator sends a commit message to everyone. Each participant applies the change permanently and releases its locks.
- If any participant voted no (or didn’t respond in time), the coordinator sends an abort message to everyone. Each participant rolls back and releases its locks.
Only after this second round does the transaction actually become visible anywhere — which is why it’s called two-phase: nothing commits until every participant has both agreed it can and been told to.
The blocking problem
Two-phase commit’s well-known weakness is what happens when the coordinator crashes after participants have voted yes but before it sends the final commit or abort decision. Every participant that voted yes is now holding locks and waiting — it can’t unilaterally decide to commit (the coordinator might have decided abort) or abort (the coordinator might have decided commit), because doing the wrong thing breaks the all-or-nothing guarantee. Those participants are stuck blocked until the coordinator recovers and tells them what happened, and while blocked, they’re holding resources (locks) that other transactions may be waiting on.
This is the central tradeoff: 2PC gives you strong, atomic consistency across multiple nodes, but at the cost of availability during a coordinator failure — a direct instance of the tension described by the CAP theorem. Three-phase commit (3PC) and making the coordinator itself fault-tolerant through a separate consensus mechanism reduce, but don’t eliminate, this blocking window in practice.
2PC vs the alternative: sagas
Because of the blocking risk, many distributed systems avoid 2PC for long-running or cross-service transactions and use a saga instead — a sequence of local transactions, each with a corresponding compensating action that undoes it if a later step fails.
| Two-phase commit | Saga | |
|---|---|---|
| Consistency model | Strong — atomic across all participants | Eventual — intermediate states are visible |
| Failure handling | Coordinator-driven rollback before commit | Compensating transactions after the fact |
| Locks held during coordination | Yes, across all participants | No — each local transaction commits independently |
| Availability under coordinator failure | Can block indefinitely | Each step proceeds independently |
| Typical use | Tightly coupled systems, same organizational boundary | Microservices, long-running business processes |
A saga trades strong consistency for availability: instead of holding locks across every participant until a global decision is made, each step commits locally and immediately, and if a later step fails, the saga runs compensating actions (e.g., “refund the payment”) to undo the completed steps. This fits eventual consistency models and microservice architectures better than 2PC’s tightly synchronized locking, at the cost of the system briefly being in a state that wouldn’t be valid if viewed from a strict transactional lens.
Where 2PC still shows up
Despite its drawbacks, 2PC remains common in contexts where the participants are within a tightly controlled boundary and strong atomicity genuinely matters:
- Distributed SQL databases use 2PC-style coordination internally to commit transactions that span multiple storage nodes or shards, often layered with consensus protocols to make the coordinator itself fault-tolerant.
- XA transactions, a standard supported by many relational databases and message queues, implement essentially the 2PC protocol for coordinating a transaction across a database and a message broker.
- Multi-shard writes in a sharded database, where a single logical transaction needs to update rows that live on different physical shards.
The takeaway
Two-phase commit guarantees that a transaction spanning multiple databases either commits everywhere or nowhere, by splitting the work into a prepare phase (where every participant promises it can commit) and a commit phase (where the coordinator tells everyone to follow through). The cost is a blocking window if the coordinator fails mid-protocol, which is why systems that value availability over strict cross-node atomicity — particularly microservices — often reach for sagas instead. Choose 2PC when you control every participant and need genuine all-or-nothing guarantees; choose a saga when the participants are loosely coupled and can tolerate a brief, correctable inconsistency.
Keep reading
The Lycoris Team · · 5 min read The Raft Consensus Algorithm, Explained
Raft is a consensus algorithm that lets a cluster of servers agree on a shared state even when some nodes fail. How leader election and log replication work.
The Lycoris Team · · 4 min read The Saga Pattern Explained: Distributed Transactions Without Locks
The saga pattern coordinates a multi-step transaction across services using local commits and compensating actions instead of a distributed lock.
Chisato · · 4 min read What Is Eventual Consistency in Distributed Systems?
Eventual consistency guarantees that replicas converge over time, not instantly. How it differs from strong consistency and when it's acceptable.