Three-phase commit and what it fixes
Open in the interactive app: practice, quiz, mock interview →In one minute
- Three-phase commit adds a pre-commit phase between voting and committing so that participants can finish a transaction without the coordinator.
- The key invariant: no participant commits while any other participant could still be undecided about whether everyone voted yes.
- It is non-blocking only if failures are clean crashes and message delays are bounded. With network partitions or unbounded delays it can produce split outcomes, which is worse than 2PC's blocking.
- In practice, systems get non-blocking atomic commit by replicating the 2PC coordinator with consensus instead. 3PC matters mostly for understanding why.
How it works
The problem it targets
In 2PC, a participant that voted yes and then lost the coordinator is uncertain. It cannot commit, because someone may have voted no. It cannot abort, because the coordinator may have decided commit and some participant may already have committed. The root cause is that a participant can go straight from "I voted yes" to "committed", so a survivor in the uncertain state cannot rule out that someone else committed.
The three phases
Dale Skeen introduced the protocol in 1981 as a fix for exactly this uncertainty (Skeen 1981).
- CanCommit: the coordinator asks each participant whether it can commit. Each replies yes (entering uncertain) or no (aborting).
- PreCommit: if all voted yes, the coordinator sends
PRECOMMIT. Each participant enters pre-committed, durably records it, and acknowledges. If any voted no or timed out, the coordinator sendsABORT. - DoCommit: once enough participants acknowledge the pre-commit, the coordinator sends
DOCOMMITand they commit.
The pre-committed state means "everyone voted yes and the decision is commit, but I have not committed yet". No participant ever commits while another is still uncertain, because commit is only sent after pre-commits are acknowledged.
Recovery without the coordinator
If the coordinator fails, surviving participants elect a new coordinator, which collects their states and applies a termination rule:
- If anyone has committed, commit.
- If anyone has aborted, or anyone never voted, abort.
- If anyone is pre-committed, move all to pre-committed, then commit.
- If everyone reachable is uncertain, abort. This is safe because nobody can have committed: committing requires that everyone first reached pre-committed.
Timeouts follow the same logic: a participant waiting for PRECOMMIT that hears nothing aborts; a participant waiting for DOCOMMIT that hears nothing commits.
Where it breaks
The rule "all reachable are uncertain, so abort" assumes the unreachable ones are crashed, not alive on the other side of a partition. Suppose the coordinator sends PRECOMMIT to participant A, then the network splits A from B and C. A's side sees a pre-committed node and commits. B and C's side sees only uncertain nodes and aborts. The transaction is now committed on one side and aborted on the other, which violates atomicity. 3PC is correct in a synchronous system with crash failures, but real networks are asynchronous: you cannot distinguish a crashed node from a slow or partitioned one. Skeen's original analysis explicitly assumed the network never fails (Skeen 1981), and Kleppmann's Cambridge course skips 3PC altogether because it depends on the unrealistic synchronous model (Cambridge notes). Consensus protocols escape the trap by deciding with majorities rather than by guessing who has failed.
Quorum variants close the split-outcome hole. Skeen's 1982 quorum-based commit protocol only lets a side that holds a quorum commit or abort, so the minority side waits instead of guessing. It can still get stuck with a connected quorum that cannot make progress, which Keidar and Dolev's Enhanced 3PC (E3PC, 1998) fixes so that any connected quorum can always finish (Wikipedia). Note the price: a partitioned minority blocks again, which is exactly 2PC's behaviour for those nodes.
Cost
3PC adds a full round trip and an extra durable write per participant compared with 2PC, raising lock hold time by roughly 50%, in exchange for a guarantee that holds only under assumptions most deployments do not meet.
Down the stack
Layer 1The contract
3PC promises atomicity plus non-blocking termination, but only under a stated failure model: nodes fail by crashing and stay down (fail-stop), message delay is bounded by a known Δ, and a timeout therefore means the peer really crashed. Under those assumptions, any group of surviving participants can reach the outcome without waiting for the failed ones. Outside them (partitions, long pauses, unbounded delays) the contract offers no safety at all.
Layer 2The mechanism
The message flow is CanCommit? answered by Yes or No, then PreCommit answered by Ack, then DoCommit answered by HaveCommitted. Every state change is written to the participant's log before it replies, so a restarted participant knows whether it was uncertain or pre-committed. Skeen's analysis gives the design rule behind the extra phase: a protocol is non-blocking if and only if no local state is "adjacent" to both commit and abort, meaning no state from which another participant could already be committed while this one might still abort, and no state that is not yet committable sits adjacent to a commit state (Skeen 1981). In 2PC the prepared state violates this rule; 3PC's pre-committed state exists to fix it. On coordinator failure, survivors elect a backup coordinator (for example, the lowest live id), which polls states, applies the termination rule, and drives everyone to the result. Timeouts are derived from Δ, typically waiting about 2Δ for a reply.
Layer 3The machine
The bounded-delay assumption fails on real hardware in ordinary ways. A JVM full garbage collection can pause a process for hundreds of milliseconds to several seconds. Hypervisors steal CPU and live-migrate VMs with pauses of similar size. Linux TCP retransmission timeouts start around 200 ms on a live connection and back off exponentially; the standard sets the initial timeout before any measurement at 1 second and requires it to double on each expiry (RFC 6298), so one lost packet can delay a message by seconds. A failed top-of-rack switch leaves every node healthy but unreachable, which no timeout can tell apart from a crash. The extra phase also has a hardware cost: three forced log writes per participant instead of two, and six one-way messages per participant instead of four, so lock hold time rises by about half.
Why it's built this way
- 2PC blocks because an uncertain participant cannot rule out that someone already committed.
- Inserting a pre-commit state ensures nobody commits until everybody has left the uncertain state, so survivors can decide by looking at each other.
- Deciding without the failed nodes requires knowing they really failed, which only timing can tell.
- Timing is reliable only in synchronous networks, and real networks are asynchronous, so 3PC's safety rests on a false premise.
- Majority-based protocols do not need to know who failed, which is why Paxos Commit and consensus-replicated coordinators replaced 3PC in practice. Paxos Commit matches 2PC's disk-write delay in the normal case and only adds messages (Gray and Lamport).
Diagram
stateDiagram-v2
[*] --> Init
Init --> Uncertain: vote yes
Init --> Aborted: vote no or timeout
Uncertain --> PreCommitted: PRECOMMIT
Uncertain --> Aborted: ABORT or timeout
PreCommitted --> Committed: DOCOMMIT or timeout
Committed --> [*]
Aborted --> [*]Worked example
Three inventory shards (A, B, C) take part in a reservation across warehouses. Network round trips are 1 ms and each durable write is 2 ms.
- 2PC: prepare round trip plus write (3 ms) and commit round trip plus write (3 ms), about 6 ms of lock hold. If the coordinator dies after prepare, locks stay until it recovers, perhaps 30 seconds for a process restart, or much longer if the host is gone.
- 3PC: add a pre-commit round trip plus write, about 9 ms of lock hold. If the coordinator dies, survivors elect a new one and finish within a timeout of, say, 500 ms. Throughput on a hot row drops from about 166 to 111 transactions per second.
- Partition case: a switch failure isolates shard A just after it received
PRECOMMIT. A times out waiting forDOCOMMITand commits. B and C, still uncertain, elect a coordinator, see no pre-committed node, and abort. Inventory is reserved in one warehouse and released in the others, and nothing in the protocol detects it. - Replicated 2PC: run the coordinator's log on a 3-node Raft group. Each decision write costs a majority round trip (about 2 ms), lock hold is about 8 ms, and a coordinator crash is covered by a new Raft leader within a second, with no split outcome under partitions because only a majority can decide. Production systems shave this further; CockroachDB's parallel commits overlap the transaction record write with the data writes, saving one consensus round (Cockroach Labs).
In the wild
- GitHub, 2012: a failure detector that guessed wrong, twice. Heavy migration load failed a health check, so Pacemaker moved the active MySQL role to a node with a cold buffer pool, which then performed badly and triggered another failover. The next day a Pacemaker segfault partitioned the cluster and both nodes declared themselves master. Over two days there was 1 hour 46 minutes of downtime, and because MySQL and Redis IDs drifted apart, 16 private repositories were briefly visible to non-collaborators. GitHub made failover of the active role require a human (GitHub). This is 3PC's flaw in miniature: acting on "it timed out, so it is dead".
- GitHub, 2018: 43 seconds of partition, 24 hours of repair. Maintenance cut the US East hub off from the primary data center for 43 seconds. Orchestrator promoted West Coast MySQL primaries during the gap, so writes landed on both coasts and could not simply be merged back; one cluster had 954 writes to reconcile, and service was degraded for 24 hours 11 minutes. GitHub then stopped cross-region promotion (GitHub). A partition that each side read as the other's failure produced divergent outcomes, the same shape as the 3PC partition case.
Trade-offs
| Option | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| 2PC | Simple, safe under partitions | Blocks if coordinator fails | Coordinator is reliable or replicated |
| 3PC | Non-blocking under crash-only, bounded-delay failures | Unsafe under partitions, extra round trip | Synchronous networks with fail-stop nodes, which is rare |
| 2PC with consensus-replicated coordinator | Safe and practically non-blocking | Needs a consensus layer | Distributed databases |
| Paxos Commit | Non-blocking with majority fault tolerance | Complex, more messages | Research-grade or custom transaction layers |
| Sagas | No distributed locks | Eventual consistency, compensations | Cross-service business workflows |
Pitfalls in production
- Trusting timeouts as failure detectors. A long GC pause looks like a crash, and the termination rule acts on a false assumption. Assume asynchrony and use majority-based decisions.
- Implementing 3PC to "fix" 2PC blocking. It trades blocking for possible inconsistency. Replicate the coordinator instead.
- Ignoring the extra latency. A third durable write per participant increases lock hold time and contention. Measure before adding phases.
- Recovery logic tested only for crashes. Partitions, message reordering, and duplicate messages are the real failure modes. Test with fault injection that includes partitions.
- Missing durable state for pre-commit. If the pre-committed state is not persisted, a restarted participant reports uncertain and the termination rule may abort a committed transaction. Persist every state transition.
Leadership lens
- When someone proposes 3PC, ask what failure model they assume and whether the network actually provides bounded delays.
- Steer atomic commit toward proven implementations: database-native distributed transactions, or a coordinator replicated by Raft or Paxos.
- Use 3PC as a teaching tool in design reviews: it shows why availability during partitions requires majorities, not more phases.
- Weigh whether cross-shard atomicity is needed at all; co-locating data that changes together avoids the whole problem.
Check yourself
Why is it safe for survivors that are all uncertain to abort in 3PC, but not in 2PC?
Show answer
In 3PC nobody can commit until everyone has reached pre-committed, so all-uncertain proves no one committed; in 2PC a participant may already have received commit.
How does a network partition break 3PC?
Show answer
Each side applies the termination rule to the nodes it can see, so a side with a pre-committed node commits while a side with only uncertain nodes aborts.
Why do real systems replicate the 2PC coordinator instead of using 3PC?
Show answer
Consensus decides with a majority, which stays safe under partitions and asynchrony, while 3PC's safety depends on timing assumptions that real networks violate.
Sources and further study
Primary
- Corbett et al., Spanner (OSDI 2012): non-blocking commit in practice, by running 2PC between Paxos-replicated groups instead of adding a phase.
- RFC 6298: Computing TCP's Retransmission Timer: why message delay on a real network has no useful upper bound.
Systems docs
- Parallel Commits (Cockroach Labs): a production non-blocking commit built on consensus rather than an extra phase.
- Wikipedia: Two-phase commit protocol: the baseline 3PC modifies, with its blocking cases spelled out.
Books
- Database Internals, Alex Petrov, chapter 13 "Distributed Transactions": 3PC next to 2PC, including the split-brain scenario.
- Database Internals, Alex Petrov, chapter 9 "Failure Detection": why heartbeats and timeouts can only suspect failures.
- Designing Data-Intensive Applications (1st edition), Martin Kleppmann, chapter 8 "The Trouble with Distributed Systems": unbounded delays, process pauses and unreliable clocks.
Other curricula
- Kleppmann, Cambridge Concurrent and Distributed Systems notes: mentions 3PC only to dismiss it for assuming a synchronous system, then teaches fault-tolerant 2PC.
- MIT 6.5840 lecture notes: Distributed Transactions: covers 2PC and its blocking, and moves on to replicated participants rather than 3PC.
Watch
- Virtual Synchrony, 2-Phase and 3-Phase Commit Protocols (Smruti R. Sarangi): a university lecture covering both commit protocols and their failure handling.
- Basic 2-Phase and 3-phase commit protocol (NPTEL): an IIT lecture stepping through the state machines.
Free resources
- Wikipedia: Three-phase commit protocol: the protocol and its partition weakness.
- Skeen: Nonblocking Commit Protocols (1981): the original paper defining non-blocking commit.
- The Paper Trail: Three-Phase Commit: a careful explanation of why 3PC works and where it fails.
- Gray and Lamport: Consensus on Transaction Commit: Paxos Commit, the majority-based alternative.