Home › High-Level Design › Two-Phase and Three-Phase Commit
T

Two-Phase and Three-Phase Commit

When one transaction has to span two or more databases and still be all-or-nothing, you need an atomic commit protocol. 2PC is the one you will actually be asked about. I have interviewed plenty of people who confidently describe "distributed transactions" and then fall apart the moment I ask what happens when the coordinator dies right after a node votes yes. That exact gap is what this guide closes, and I will do it with a real transfer and real numbers so the failure modes stay concrete.

High-Level DesignAdvanced11 min read

If you remember five things

The whole answer in one glance. Everything below defends these five calls.

  1. 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.
  2. Prepared is a promise. Once a participant writes prepared to disk, it cannot abort or commit on its own. It holds locks and waits.
  3. 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.
  4. 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.
  5. 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.
The one sentence to carry

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 prepared record 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 no and releases its locks immediately.

Phase 2, the decision (commit). The coordinator tallies the votes and tells everyone.

  • Every vote yes: the coordinator writes commit to its own log (this write is the actual point of no return for the whole transaction) and sends commit to everyone.
  • Each participant makes its changes permanent, releases locks, and acknowledges.
  • Even one vote no: the coordinator sends abort instead and everyone rolls back.
sequenceDiagram participant C as Coordinator participant P1 as Participant A participant P2 as Participant B C->>P1: prepare C->>P2: prepare P1-->>C: yes P2-->>C: yes Note over C: write commit to log C->>P1: commit C->>P2: commit P1-->>C: ack P2-->>C: ack
The happy path. Notice the "write commit to log" note on the coordinator: that durable write, not any participant's ack, is the moment the transaction truly commits.

The dangerous window is between the yes and the decision.

  • A participant that has written prepared cannot 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.
Pause before you read on: kill the coordinator right after Participant A votes yes, and say its disk died too. What is A's situation, and can anyone recover it?
Reveal the answer
A sits there with locks held, waiting for a message that may never arrive. It cannot decide alone, because committing or aborting unilaterally could contradict the group. And if the coordinator's disk died before the decision was durably written, nobody on earth knows what the outcome should have been, so there is no safe recovery. This is the blocking problem: 2PC stays safe (no split decision) at the cost of liveness (it can hang indefinitely).

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 in us-east-1b: same region, measured round-trip about 1 ms between zones.
  • Each participant's local prepare work (execute plus fsync the prepared record): about 4 ms on commodity NVMe.
  • The coordinator's own commit log fsync: another 4 ms.

Trace the happy path for the 500 dollar transfer:

  • Coordinator sends prepare to A and B in parallel: 0.5 ms to reach them.
  • A and B each do 4 ms of local work and fsync, then reply yes: 0.5 ms back. Phase 1 is roughly 5 ms, paced by the slower participant.
  • Coordinator fsyncs its commit record: 4 ms.
  • Coordinator sends commit to A and B, they apply and release locks, then ack: 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.

~100x
Lock hold time vs a single-node commitThose rows stay locked for the entire Phase 1 plus Phase 2 window, about 9 to 10 ms, versus tens of microseconds for a local commit. That 100x to 1000x jump is the hidden cost of 2PC, and it is what quietly caps your write throughput.

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.

flowchart TD A[Coordinator sends prepare] --> B[Collect votes] B --> C[All yes so send pre-commit] C --> D[Participants ack pre-commit] D --> E[Coordinator sends do-commit] E --> F[Participants commit and ack]
The extra middle round. The point of 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-commit means 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.
Red flag that fails you here

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.
What teams actually ship
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.
The textbook trap
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 TRANSACTION and COMMIT 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.
From the interviewer's chair

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.

Follow-ups the interviewer will ask (and your answers)
"The coordinator crashes after a participant votes yes. What exactly happens?"
That participant is in-doubt: it has written prepared, so it cannot commit or abort on its own, and it holds its locks until it learns the decision. If the coordinator's decision log was durable and replicated, a replica resumes and tells it the outcome. If the log was lost with the coordinator, nobody can safely recover, which is the blocking problem in its purest form. That is why the decision log must be replicated.
"When exactly does the transaction commit?"
The instant the coordinator durably writes commit to its own log, before any participant hears about it. That write is the point of no return. Everything after it (sending commit, participants applying, acks) is just propagation of a decision already made.
"Does 3PC solve the blocking problem? Should I use it?"
Only partly, and no. 3PC is non-blocking under a crash-stop, no-partition model. Under a real network partition it can let two sub-groups reach different decisions, which breaks the safety 2PC always keeps. It also adds a third round trip on every transaction. I would instead make the 2PC coordinator fault-tolerant with Raft or Paxos, which Spanner effectively does.
"Why is 2PC bad for throughput?"
Locks are held across the full vote-plus-decide window, not microseconds but about 10 ms same-zone. A hot row locked 10 ms per transaction is capped near 100 tps regardless of CPU. Cross a region (roughly 80 ms round trip) and the window grows to around 170 ms, dropping that ceiling to about 6 tps. The latency becomes a hard throughput ceiling on contended rows.
"When would you pick a Saga over 2PC?"
When participants span services or continents, when throughput is high, or when loose consistency is acceptable. A Saga is a sequence of local transactions with compensating actions, so it never blocks and it scales. The cost is giving up isolation and strict atomicity. In microservices, Saga is my default and 2PC is the exception I justify for a few participants in one region where atomicity is non-negotiable.

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.

Thanks. Check your inbox, your Two-Phase and Three-Phase Commit prep plan is on the way.