Skip to main content

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:

  1. Prepare phase: coordinator sends PREPARE to every participant. Each participant must either reply PREPARED (guaranteeing that it can commit or abort the transaction no matter what happens) or NO (must abort). PREPARED is durable: the participant logs it before replying so a crash does not lose the vote.
  2. Commit/Abort phase: if all voted PREPARED, coordinator decides COMMIT, durably logs the decision, and sends COMMIT to every participant. If any voted NO or timed out, coordinator decides ABORT. Participants apply the decision, log it durably, and reply ACK.

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:

  1. Small number of trusted, colocated participants.
  2. A coordinator that is itself made highly available (e.g., running as a replicated group, or on a consensus-backed log).
  3. Workloads where atomic commit across resources is worth the latency (at minimum: the slowest participant's round-trip twice, plus fsyncs).
  4. Short transactions, so that "in doubt" windows are small.

When 2PC is a bad fit:

  1. Cross-cloud or cross-organizational boundaries (coordinator availability cannot be assumed).
  2. Long-running workflows (blocking on in-doubt participants is catastrophic).
  3. Microservice architectures where ownership is distributed. Use sagas.

Check Yourself

  1. Why can a participant not unilaterally abort after voting PREPARED?
  2. What must the coordinator do before sending any COMMIT message?
  3. Sketch the recovery of an in-doubt participant after a crash.
  4. What is a heuristic decision, and what is its cost?
  5. 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:

  1. P1 crashes before sending PREPARE vote.
  2. P1 replies PREPARED, C logs COMMIT, then C crashes before sending COMMIT to P2.
  3. P1 replies PREPARED, then dies forever. C has not yet decided.
  4. C logs COMMIT, sends to P1 and P2, both apply, C crashes before logging COMPLETE.
  5. Network partition isolates C from P2 for 30 minutes after P2 replied PREPARED.

Read This Only If Stuck