Two-phase commit (2PC) is an atomic commitment protocol that ensures all participants in a distributed transaction either all commit or all abort. In databases, it extends the atomicity of ACID transactions across several resource managers, such as two database servers or a database and a message broker, using a coordinator that collects votes in a prepare phase and announces the outcome in a commit phase.
2PC guarantees agreement on the outcome but not availability: if the coordinator fails at the wrong moment, participants can be blocked while holding locks. This trade-off explains both its long use in enterprise systems and the search for alternatives in large distributed architectures.
How the two phases work
The protocol separates the decision from its execution. In the first phase, the coordinator asks every participant whether it can commit; in the second phase, it tells all participants the single global decision. Each participant must write its state to a durable log before answering, so that it can keep its promise even after a crash.
- Prepare request: the coordinator sends a prepare message to every participant.
- Vote: each participant makes its changes durable, keeps its locks, force-writes a prepared record to its write-ahead log and replies yes, or replies no and rolls back.
- Decision: if all votes are yes, the coordinator force-writes a commit record; any no vote or timeout leads to abort.
- Outcome: the coordinator sends commit or rollback to all participants.
- Acknowledgment: participants apply the outcome, release locks and acknowledge, after which the coordinator can forget the transaction.
The moment a participant votes yes, it enters the in-doubt state: it has given up the right to abort on its own and must wait for the coordinator. This loss of autonomy is the price of guaranteed atomicity.
Participant states and failure handling
Correct recovery depends on knowing which state each node reached before a failure. The durable log records make this possible, and recovery logic acts according to the last logged state.
| State at failure | Who failed | Recovery action |
|---|---|---|
| Before voting | Participant | Coordinator times out and aborts |
| Prepared, in doubt | Participant | On restart, asks the coordinator for the outcome |
| Prepared, in doubt | Coordinator | Participants block, locks held until coordinator returns |
| Decision logged | Coordinator | On restart, resends the decision to all participants |
| Committed locally | Participant | Redo from the log, then acknowledge |
The blocking case is the central weakness of 2PC. While participants wait in doubt, their locked rows are unavailable to other transactions. Operators can resolve such transactions manually with a heuristic decision, but if that guess differs from the coordinator’s real decision, the result is a heuristic hazard: data that is no longer atomic across systems.
Implementations and standards
Most production use of 2PC follows the X/Open XA specification, which defines the interface between a transaction manager and resource managers. Application servers expose it through APIs such as the Java Transaction API, while databases offer explicit prepare and commit commands for external coordinators.
| System | 2PC mechanism | Notes |
|---|---|---|
| PostgreSQL | PREPARE TRANSACTION and COMMIT PREPARED | Disabled until max_prepared_transactions is raised above 0 |
| MySQL | XA START, XA PREPARE, XA COMMIT | Also used internally between the binary log and InnoDB |
| SQL Server | Distributed transactions through a coordinator service | Common with linked servers |
| Java platforms | JTA with XA-capable drivers | Spans databases and message queues |
| Distributed SQL databases | 2PC across shards with replicated coordinators | Consensus keeps the coordinator available |
Optimizations and variants
Basic 2PC needs two message round trips and several forced log writes per transaction, so implementations use optimizations that reduce messages and disk flushes without weakening atomicity.
- Presumed abort: the coordinator does not log aborts; missing information means abort, which saves writes for failed transactions.
- Presumed commit: the inverse optimization, reducing cost for the common successful case.
- Read-only optimization: a participant that changed nothing votes read-only and skips the second phase.
- One-phase commit: with a single participant, the coordinator skips the prepare phase entirely.
- Last resource commit: one non-XA resource is committed last, with its result acting as the decision.
Three-phase commit (3PC) adds a pre-commit phase to avoid blocking, but it is only safe under assumptions of bounded delays and no network partitions, so it is rarely used. Modern systems instead replicate the coordinator with a consensus protocol such as Paxos or Raft, which removes the single point of blocking.
Two-phase commit compared with alternatives
2PC provides strong atomicity at the cost of latency and availability. Architectures that cannot accept blocking or cross-service locks use patterns that give up immediate atomicity in exchange for independence.
| Approach | Consistency | Main trade-off |
|---|---|---|
| Two-phase commit | Atomic, immediate | Blocking and held locks on coordinator failure |
| Three-phase commit | Atomic under strict timing assumptions | Unsafe with network partitions |
| Consensus-replicated 2PC | Atomic, highly available | More replicas and complexity |
| Saga | Eventual, with compensating actions | Intermediate states visible to others |
| Transactional outbox | Eventual between database and messages | Requires idempotent consumers |
Microservice architectures usually avoid 2PC because it couples service availability and requires every service to support XA. Within a single distributed database, however, 2PC remains the standard way to commit transactions that span shards.
Operational pitfalls
The most common operational problem is the orphaned prepared transaction: a participant left in the prepared state after a coordinator was lost or misconfigured. It keeps locks indefinitely and, in PostgreSQL, also prevents VACUUM from removing old row versions. Monitoring views of prepared transactions and alerting on their age are essential wherever 2PC is enabled.
The simple explanation
Two-phase commit works like a wedding ceremony. The officiant first asks each partner whether they agree. Only when both say yes does the officiant declare the marriage. If either says no, nothing happens at all.
Once a partner has said yes, they cannot walk away and must wait for the declaration. That is the weak spot: if the officiant disappears at that moment, everyone is stuck waiting. Computers use the same idea to make sure changes in several systems succeed together or not at all.
How it is used in practice
A bank runs its account ledger in one database and its payment message queue in another system. When a customer sends a transfer, the ledger must be debited and the outgoing payment message must be queued, and neither may happen without the other.
The bank’s transaction manager coordinates both resources with XA two-phase commit. Both resources prepare, log their state and vote yes; only then does the coordinator log the commit decision and tell both to commit. When a server crashes after the prepare phase, recovery asks the coordinator for the outcome and completes the transfer.
The result is that customers never see money debited without a payment sent. The operations team monitors in-doubt transactions and keeps the coordinator on redundant infrastructure to limit the time locks can be held.