If you remember five things
The whole answer in one glance. Everything below defends these five calls.
- Two rounds: vote, then decide. Phase 1 asks every participant "can you commit?", Phase 2 tells them the one answer. That is all 2PC is.
- Prepared is a promise. Once a participant writes prepared to disk, it cannot abort or commit on its own. It holds locks and waits.
- Safe but not live. 2PC never produces a split decision, but a coordinator crash after a yes vote can hang a participant forever. That is the blocking problem.
- Locks are the real bill. Rows stay locked for the full vote-plus-decide window (about 10 ms same-zone), which caps a hot row near 100 tps no matter how many CPUs you add.
- Nobody ships 3PC. Make the 2PC coordinator fault-tolerant with Raft instead. You get non-blocking recovery without losing safety under partition, which is what Spanner effectively does.
1The core idea#
Start with the smallest version of the problem, a transfer across two databases.
- A 500 dollar transfer: the debit lives in database A (your account), the credit in database B (your landlord's account).
- You cannot have the debit commit and the credit fail, or you have burned 500 dollars into the void.
- Either both land or neither does.
- A single database gives you this for free: one write-ahead log, one commit record.
- Across two databases nobody is in charge by default, so you appoint one.
The two roles.
- The appointee is the coordinator. Everyone else is a participant (sometimes called a cohort).
- Mental model: a wedding officiant asking each party "do you take this transaction?" before anyone is actually married.
- Nobody is committed until every single yes is in. One hesitation and the whole thing is called off.
The two protocols in one breath.
- 2PC is that ceremony in two rounds of messages: ask everyone if they can commit, then tell everyone the final decision.
- 3PC adds a middle round to survive coordinator crashes without freezing, at the cost of more latency and more messages.
- That trade is the whole story, and it is where most interview conversations go.
2PC is safe (it never produces a split decision) but not live (it can hang forever waiting on a dead coordinator). Everything else in this guide is a consequence of that.
2How it works#
Two-Phase Commit#
Phase 1, the vote (prepare). The coordinator sends prepare to every participant, and each one does the risky part.
- Executes the work, writes it durably to its own log, grabs the locks it needs.
- Writes a
preparedrecord to disk. That record is a promise: "I can no longer change my mind on my own. If you tell me to commit, I will, even after a crash and restart." - Votes
yes. - If anything goes wrong (a constraint violation, a lock it cannot get, a disk error), it votes
noand releases its locks immediately.
Phase 2, the decision (commit). The coordinator tallies the votes and tells everyone.
- Every vote yes: the coordinator writes
committo its own log (this write is the actual point of no return for the whole transaction) and sendscommitto everyone. - Each participant makes its changes permanent, releases locks, and acknowledges.
- Even one vote no: the coordinator sends
abortinstead and everyone rolls back.
The dangerous window is between the yes and the decision.
- A participant that has written
preparedcannot abort on its own and cannot commit on its own. - It is stuck holding locks until the coordinator tells it what happened. We call this being in-doubt.
Reveal the answer
A worked example with real numbers#
Make the cost concrete instead of hand-waving "it adds latency."
- Database A is in
us-east-1a, database B inus-east-1b: same region, measured round-trip about 1 ms between zones. - Each participant's local
preparework (execute plusfsyncthepreparedrecord): about 4 ms on commodity NVMe. - The coordinator's own
commitlogfsync: another 4 ms.
Trace the happy path for the 500 dollar transfer:
- Coordinator sends
prepareto A and B in parallel: 0.5 ms to reach them. - A and B each do 4 ms of local work and
fsync, then replyyes: 0.5 ms back. Phase 1 is roughly 5 ms, paced by the slower participant. - Coordinator
fsyncs itscommitrecord: 4 ms. - Coordinator sends
committo A and B, they apply and release locks, thenack: another round trip of about 1 ms plus a small apply cost.
End to end you are at roughly 10 to 12 ms wall clock. Here is the number that matters for capacity planning.
Now push that into throughput:
- If every transfer touches the same hot row (a shared fee account, say) and that row is locked 10 ms per transaction, your ceiling is 1000 / 10 = 100 transactions per second on that row. Full stop. No amount of CPU helps.
- Cross a region boundary instead of a zone (us-east to eu-west, roughly 80 ms round trip) and the lock window blows up to around 170 ms, dropping the same hot-row ceiling to about 6 transactions per second.
- This is why "just add 2PC" silently caps your writes, and it is the practitioner detail I listen for.
Three-Phase Commit#
3PC tries to kill the blocking by splitting Phase 2 in two. The new middle step is pre-commit.
pre-commit is that once a participant has it, the whole group is known to have agreed to commit, so a replacement coordinator can finish the job even if the original dies.Why the middle round helps.
- Once a participant has received
pre-commit, everyone knows the group agreed to commit, even though nobody has committed yet. - If the coordinator dies now, a replacement can safely infer the outcome from the surviving states: any live node that reached
pre-commitmeans push everyone to commit; none reached it means abort. - Headline property: 3PC is non-blocking under a crash-stop failure model, given a reliable way to elect a new coordinator.
The catch is the fine print in that last sentence.
- 3PC only keeps its promise if the network does not partition and messages are not arbitrarily delayed.
- Under a real partition, 3PC can produce two sub-groups that reach different decisions. That is exactly the inconsistency 2PC never allows.
- So 3PC trades a safety property for a liveness property.
- It also adds a full third round trip. In the same-zone example that pushes the lock window from about 10 ms toward 15 ms, cutting the hot-row ceiling from 100 to about 67 transactions per second.
- In most production environments I would not make that trade.
Reaching for 3PC as the obvious upgrade the moment I mention a coordinator crash. It signals you read a textbook but never thought about CAP. 3PC breaks safety under partition, and networks partition. The senior move is to make the 2PC coordinator fault-tolerant, not to swap in a protocol that can split-brain.
3Trade-offs#
Here is how I weigh them in a design review.
| Dimension | 2PC | 3PC |
|---|---|---|
| Message rounds | 2 (plus acks) | 3 (plus acks) |
| Latency (same-zone example) | ~10 ms | ~15 ms |
| Coordinator crash | Participants block, hold locks | Can recover without blocking |
| Network partition | Stays safe, may block | Can split-brain, unsafe |
| Lock hold time | One extra round | Two extra rounds, worse |
| Hot-row throughput ceiling (10 vs 15 ms) | ~100 tps | ~67 tps |
| Real-world usage | Common | Essentially never in production |
What you give up with 2PC.
- Availability and throughput. Every participant holds locks across at least one network round trip, so your hottest rows are locked for tens of milliseconds instead of microseconds (we just measured it: about 100x worse).
- The coordinator is a single point of failure for liveness.
- The whole system runs at the speed of its slowest participant.
What you give up with 3PC (worse).
- Correctness under partition.
- An extra round trip on every single transaction.
- A more complicated recovery protocol that is genuinely hard to get right.
- You pay that latency always, to buy protection against one failure mode (coordinator crash with no partition) that a highly-available coordinator already addresses more cheaply.
2PC with a fault-tolerant coordinator
- Safety under partition
- Preserved: never a split decision.
- Coordinator crash
- Non-blocking: decision log is replicated via Raft/Paxos, so a replica continues.
- Latency
- Two rounds plus the consensus write on the decision.
- Real-world fit
- Spanner, CockroachDB: 2PC across shards on top of Paxos/Raft groups.
Plain 3PC
- Safety under partition
- Lost: two sub-groups can decide differently.
- Coordinator crash
- Non-blocking, but only in a no-partition model.
- Latency
- Three rounds on every transaction.
- Real-world fit
- Essentially never shipped in production.
So the honest interview answer: nobody ships 3PC.
- Make the 2PC coordinator fault-tolerant instead.
- Put the coordinator's decision log on a replicated, consensus-backed store (Raft or Paxos), so "the coordinator" is really a replicated group that does not lose its decision when one machine dies.
- You get the non-blocking recovery 3PC wanted, without giving up safety under partition.
- This is roughly what Google's Spanner does: Paxos groups back each participant, and 2PC runs on top of those groups.
4Where it is used#
2PC shows up everywhere, usually without the name attached.
- The XA standard (and JTA in the Java world) is 2PC: an application server coordinates a commit across, say, a relational database and a message broker, so a record and its event are written atomically. Classic enterprise setup.
- PostgreSQL exposes it directly with
PREPARE TRANSACTIONandCOMMIT PREPARED. - Spanner and CockroachDB use 2PC across shards, layered on Paxos/Raft so the coordinator state survives failures.
- Many message systems push a transactional outbox pattern precisely to avoid XA-style 2PC, because teams learned the hard way how much it costs (the throughput math above is exactly why).
When would I actually reach for 2PC?
- A small number of participants.
- Inside one datacenter or region, with low and predictable latency (the 1 ms same-region example, not the 80 ms cross-region one).
- A strict atomic guarantee is a hard business requirement.
- The transaction rate is modest. Moving money between two internal ledgers is the textbook fit.
When any of those break, I lean to a Saga.
- Participants across continents, high throughput, or loose consistency acceptable: use a Saga instead, a sequence of local transactions with compensating actions to undo.
- Sagas give up isolation and atomicity-as-you-know-it, but they do not block and they scale.
- In a microservices design, Saga is usually the right default and 2PC is the exception you justify.
5Common pitfalls and gotchas#
Treating 2PC as free atomicity.
- It is not free. It is atomicity bought with locks, latency, and a liveness risk.
- Candidates who say "just use 2PC" without naming the blocking problem or the lock-hold cost have not actually run it.
Forgetting the coordinator's own log write.
- The transaction commits the instant the coordinator durably records
commit, before any participant hears about it. - If that write is not durable and replicated, the coordinator becomes a single point of data loss, not just availability loss.
Ignoring in-doubt transactions.
- A participant that restarts after a crash in the prepared state must not guess.
- It has to ask the coordinator (or scan the recovery log) for the outcome. Until it gets an answer, those locks stay held.
- Real operators keep runbooks for manually resolving stuck in-doubt transactions, and it is miserable work at 3 a.m.
Assuming 3PC fixes everything.
- It fixes coordinator-crash blocking under a no-partition assumption, and it breaks safety under partition.
- Networks partition. Proposing 3PC as the obvious upgrade signals textbook knowledge without CAP thinking.
Unbounded lock hold times.
- If one participant is slow or paused (a 200 ms GC pause, a noisy neighbor), everyone else holds locks longer and your hot-row ceiling drops with it.
- At scale this creates convoys and timeouts cascade.
- Always have a timeout and a default action (usually abort) on the participant side for the vote phase.
Heuristic decisions.
- Some XA implementations let an operator force a stuck participant to commit or abort out of band to free locks.
- If that guess disagrees with the coordinator, you have silently broken atomicity.
- These "heuristic outcomes" are a known footgun.
I am not testing whether you can recite the two phases. I am watching for whether you reach for the failure modes on your own: the in-doubt participant, the coordinator's durable log as the real commit point, the lock cost in numbers. Name those unprompted and you have told me you have operated this, not just read about it.
6How it shows up in interviews#
Interviewers probe 2PC to see if you understand failure modes, not happy paths. Expect some version of "walk me through what happens when X dies at step Y."
Interviewer: Walk me through 2PC, then tell me what happens if the coordinator crashes right after the first participant votes yes.
Candidate (weak): The coordinator sends prepare, everyone votes, then it sends commit and everyone commits. If the coordinator crashes, it retries when it comes back up. If that is a problem, you use 3PC.
Candidate (strong): 2PC guarantees safety, it never produces a split decision, but it is not live. If the coordinator crashes after a participant votes yes, that participant is in-doubt and holds its locks until it learns the outcome. The real commit point is the coordinator's durable log write, so that log has to be replicated or it is a single point of data loss. In my 500 dollar transfer example the hot row stays locked for the full prepare-plus-commit window, about 10 ms same-zone, which caps that row near 100 tps. 3PC removes the blocking only under a crash-stop, no-partition model and sacrifices safety under partition, so I would not ship it. Instead I make the coordinator fault-tolerant with Raft, which is what Spanner effectively does. And for cross-service work at scale I would prefer a Saga with compensations, reserving 2PC for a few participants in one region where strict atomicity is non-negotiable.
See the difference. The weak answer treats the protocol as a recipe and jumps to "then use 3PC" as a magic fix. It never mentions locks, liveness, or partition. The strong answer:
- Names the blocking problem unprompted.
- Ties the commit point to the coordinator's durable write.
- Quantifies the lock cost.
- Rejects 3PC for the right reason.
- Closes with the judgment of when not to use the tool.
Knowing when a tool is wrong is the senior signal, not reciting how it works.
One more thing interviewers love: connect 2PC to CAP. During a partition, 2PC chooses consistency over availability (it blocks). Saying that in one sentence puts you ahead of most candidates.
"The coordinator crashes after a participant votes yes. What exactly happens?"
"When exactly does the transaction commit?"
"Does 3PC solve the blocking problem? Should I use it?"
"Why is 2PC bad for throughput?"
"When would you pick a Saga over 2PC?"
What you can now explain in the room
- Why crossing two databases needs an appointed coordinator and a two-round vote-then-decide protocol.
- Why a prepared participant is in-doubt, and why that is the whole blocking problem.
- Why 2PC is safe but not live, and that the commit point is the coordinator's durable log write.
- How to put real numbers on the lock cost: about 10 ms same-zone, a 100 tps ceiling on a hot row, and how a region hop drops it to about 6 tps.
- Why 3PC trades safety for liveness and why nobody ships it, versus a Raft-backed coordinator.
- When to reach for a Saga instead, and how 2PC maps onto CAP during a partition.
Want to see this applied end to end? Try the System Design Question walkthrough on building a distributed payments ledger, where choosing between 2PC and a Saga is the central decision.
Get a prep plan built around your Two-Phase and Three-Phase Commit interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.