Two-Phase Commit (2PC): Coordinator, Participants, Failure Modes
What This Concept Is
Two-phase commit is the canonical protocol for atomic commit across multiple nodes. A coordinator drives the protocol, and a set of participants (each one a transactional resource, e.g., a database) are asked to agree on commit or abort.
The two phases:
- Prepare phase: coordinator sends
PREPAREto every participant. Each participant must either replyPREPARED(guaranteeing that it can commit or abort the transaction no matter what happens) orNO(must abort).PREPAREDis durable: the participant logs it before replying so a crash does not lose the vote. - Commit/Abort phase: if all voted
PREPARED, coordinator decidesCOMMIT, durably logs the decision, and sendsCOMMITto every participant. If any votedNOor timed out, coordinator decidesABORT. Participants apply the decision, log it durably, and replyACK.
The key property: once any participant has replied PREPARED, it cannot unilaterally abort (it is "in doubt"). It must wait for the coordinator's decision, even if that takes minutes or hours.
Why It Matters Here
2PC is the textbook answer to "how do I commit atomically across two databases." It also has well-known failure modes that motivate 3PC, Paxos Commit, and, more pragmatically, the move away from distributed transactions toward sagas. Understanding 2PC is a prerequisite to understanding why distributed transactions are expensive and why a microservice team should usually not use them.
Concrete Example
Coordinator C, participants P1 and P2. Normal path:
time C P1 P2
1 PREPARE ->
2 PREPARE ------------->
3 log PREPARED
reply PREPARED ->
4 log PREPARED
reply PREPARED ->
5 log DECISION=COMMIT
COMMIT ->
6 COMMIT ------------->
7 apply; log COMMITTED
reply ACK ->
8 apply; log COMMITTED
reply ACK ->
9 log COMPLETE
Failure mode 1: Participant crashes after voting PREPARED. On recovery, it reads its log, sees PREPARED for this txn, and asks the coordinator for the decision. Blocks until coordinator answers.
Failure mode 2: Coordinator crashes after collecting all votes but before logging a decision. On recovery, coordinator must redrive the protocol. Participants are "in doubt" and blocked until coordinator returns.
Failure mode 3: Coordinator crashes after logging COMMIT but before sending COMMIT to all. On recovery, coordinator reads its log and resumes: sends COMMIT to remaining participants.
Failure mode 4: Coordinator dies permanently while a participant is in doubt. This is the famous blocking case. The participant holds its locks, cannot commit, cannot abort, until a human operator issues a heuristic decision (commit or abort out-of-protocol) which breaks atomicity if the other participants made a different heuristic call.
Common Confusion / Misconception
"2PC is always correct, just slow." 2PC is correct under the assumption that the coordinator eventually recovers. If it does not, 2PC blocks participants indefinitely. This is exactly what 3PC tries to fix.
"Participants can time out and abort." Before PREPARED, yes. After PREPARED, no - unilateral abort would break atomicity because the coordinator might already have committed elsewhere.
"2PC is only about databases." Any resource that implements the XA interface (JTA transactions, MSMQ, some file systems, some message queues) can participate. Heterogeneous 2PC across a DB and a message queue was a common pattern in enterprise Java; it is now largely discouraged because of exactly the failure modes above.
"We can use 2PC across microservices." Technically yes, practically no. The operational cost of participants held in doubt while a coordinator is being debugged has convinced most teams that a saga with compensations is the better pattern, even though it sacrifices atomicity.
How To Use It
When 2PC might be appropriate:
- Small number of trusted, colocated participants.
- A coordinator that is itself made highly available (e.g., running as a replicated group, or on a consensus-backed log).
- Workloads where atomic commit across resources is worth the latency (at minimum: the slowest participant's round-trip twice, plus fsyncs).
- Short transactions, so that "in doubt" windows are small.
When 2PC is a bad fit:
- Cross-cloud or cross-organizational boundaries (coordinator availability cannot be assumed).
- Long-running workflows (blocking on in-doubt participants is catastrophic).
- Microservice architectures where ownership is distributed. Use sagas.
Check Yourself
- Why can a participant not unilaterally abort after voting
PREPARED? - What must the coordinator do before sending any
COMMITmessage? - Sketch the recovery of an in-doubt participant after a crash.
- What is a heuristic decision, and what is its cost?
- Why is 2PC sometimes called a blocking protocol?
Mini Drill or Application
For each failure scenario, state whether atomicity is preserved, and what recovery must happen:
- P1 crashes before sending PREPARE vote.
- P1 replies PREPARED, C logs COMMIT, then C crashes before sending COMMIT to P2.
- P1 replies PREPARED, then dies forever. C has not yet decided.
- C logs COMMIT, sends to P1 and P2, both apply, C crashes before logging COMPLETE.
- Network partition isolates C from P2 for 30 minutes after P2 replied PREPARED.
Read This Only If Stuck
- DDIA: Atomic commit and two-phase commit (part 1)
- DDIA: Atomic commit and two-phase commit (part 2)
- DDIA: Distributed transactions in practice (part 1)
- Database Internals: Two-phase commit
- Database Internals: Cohort failures in 2PC
- Distributed Systems: Atomic commit protocols (part 1)
- Database System Concepts: Commit protocols (part 1)