Part III — Distributed Transactions & Coordination · Chapter 2

Three-phase commit (3PC)

Hook

2PC's fatal flaw: a participant that voted yes and then hears nothing has no safe move. It can't commit (someone else might have voted no) and can't abort (everyone else might commit). It's simply stuck. What single piece of extra information would let it decide for itself?

Intuition
PHASE 1Coordinator → P1: VOTE-REQUEST — Coordinator asks P1 to vote.
Step 1 of 18

Step through this trace and notice the middle phase: PRE-COMMIT. It's sent only after every participant has voted yes — so by the time a participant receives it, that fact alone tells it something the coordinator's silence never could.

Formalize

A stuck participant's safe default depends entirely on what it has already heard:

default={commitif PRE-COMMIT was receivedabortotherwise\text{default} = \begin{cases} \text{commit} & \text{if PRE-COMMIT was received} \\ \text{abort} & \text{otherwise} \end{cases}
  • Uncertain — voted, but no PRE-COMMIT has arrived yet.
  • Prepared — PRE-COMMIT has arrived and been acked.
  1. Why 'uncertain' can safely default to abort

    PRE-COMMIT is the coordinator's signal that every vote came back yes. If a participant is still uncertain, that broadcast hasn't gone out yet — which means no participant anywhere could have committed. Aborting is always safe.

  2. Why 'prepared' can safely default to commit

    PRE-COMMIT only ever goes out after a unanimous yes. If one participant received it, every participant voted yes — so every other participant is at least as far along as "prepared," never stuck back at "voted no." Committing is always safe.

  3. The trade

    That extra fact costs an extra round trip: the coordinator must now wait for PRE-COMMIT-ACKs before it can even send the final commit.

Play
PHASE 1Coordinator → P1: VOTE-REQUEST — Coordinator asks P1 to vote.
Step 1 of 18

Switch between the two branches. On the abort path, 3PC costs exactly what 2PC costs — there was never anything to pre-commit. Only the commit path pays the extra round trip, and in exchange, no participant on that path can ever get permanently stuck.

Worked example

All three participants vote yes.

  1. Phase 1 — vote

    Coordinator asks P1, P2, P3; all three vote YES. Decision so far: pending, but headed to commit.

  2. Phase 2 — pre-commit

    Coordinator sends PRE-COMMIT to all three. Each acks. Every participant is now "prepared" — if the coordinator vanished at this exact moment, all three would independently default to commit and reach the same answer.

  3. Phase 3 — commit

    Coordinator sends GLOBAL-COMMIT; all three ack. Total: 3 (vote request) + 3 (vote) + 3 (pre-commit) + 3 (pre-commit-ack) + 3 (commit) + 3 (ack) = 18 messages, versus 2PC's 12 for the same outcome.

Checkpoint

For each stuck participant below, pick the outcome it can safely default to on its own, with no word from the coordinator.

P1 voted yes, then heard nothing — no PRE-COMMIT ever arrived.
P2 already received and acked PRE-COMMIT before the coordinator vanished.
P3 received PRE-COMMIT — every participant, by definition, must have voted yes.
P1 just sent its vote and the coordinator went silent immediately after.
Answer all four scenarios
Summary
default={commitif PRE-COMMIT receivedabortotherwise\text{default} = \begin{cases} \text{commit} & \text{if PRE-COMMIT received} \\ \text{abort} & \text{otherwise} \end{cases}

3PC removes 2PC's single blocking point by inserting one more phase: PRE-COMMIT tells a participant that unanimous agreement already happened, which is exactly the fact it needs to safely default on its own if the coordinator disappears. It costs one extra round trip on the commit path — a fair trade for never blocking indefinitely (so long as the network itself eventually delivers messages; a true network partition is a harder case 3PC does not solve).