The Runtime Theory
SystemArchitecturedistributed systems

Distributed Commit: Two-Phase Prepare, Vote, and Decide

A step-by-step walk from the coordinator's prepare phase to the vote collection and commit decision that makes N participants agree atomically.

The Runtime Theory Team2 min read07 steps

layer stack

System

HWHardware
KKernel
RTRuntime
APPApplication
SYSSystem
CLIClient
NETNetwork
TLSCrypto
SRVServer

adjacent altitudes in this subsystem are still being traced

trace spine

  1. 01 coordinator sends prepare
  2. 02 participants write PREPARED
  3. 03 participants vote
  4. 04 coordinator collects votes
  5. 05 commit decision is logged
  6. 06 participants commit and ack
  7. 07 coordinator forgets the txn

A distributed commit is the problem of making N independent databases, queues, or services treat one transaction as atomic. The workhorse is two-phase commit (2PC), and here is exactly what executes, in order, with a coordinator C and participants P1…P3.

trace stepSystem
C sends PREPARE to every participant in parallel. This is one RTT to each (0.5–5ms in a datacenter). The transaction work itself — the SQL, the queue enqueue — has already run at each participant, but nothing is final.
trace stepSystem
Each participant that can commit writes a PREPARED record to its own write-ahead log and fsyncs it — this is the moment of no return on their side. It has now promised to commit if asked. The fsync is the expensive part: 0.5–2ms on NVMe, 5–10ms on spinning disks, and it is serial — every participant pays it before voting.
trace stepSystem
Each participant replies YES (I am prepared, I can commit) or NO (I cannot — abort). A NO can come from a constraint violation, a deadlock timeout, or a failed local fsync. One NO anywhere means the whole transaction dies.
trace stepSystem
C waits for all N replies — the slowest participant sets the latency. With three participants at 5ms RTT plus 2ms fsync each, the prepare round is ~7ms end to end. If a participant is down, C cannot wait forever: it has a timeout, and a timeout is treated as NO (abort) — the safe direction.
trace stepSystem
If all votes are YES, C writes COMMIT to its own log and fsyncs it. If any NO, C writes ABORT. This log record is the decision's source of truth — from now on, even if C crashes, recovery reads this record and drives the participants to the same outcome.
trace stepSystem
C sends COMMIT (or ABORT) to every participant. Each participant applies the transaction, writes the outcome to its log, and replies ACK. This is the second full RTT round. The transaction becomes visible at different participants at different milliseconds — atomicity means all eventually commit, not that they flip simultaneously.
trace stepSystem
Once C has all ACKs it can discard the transaction record. If an ACK is lost, C retries the COMMIT message — participants must be idempotent about the decision, replying from their logged outcome. The protocol is done at 2×RTT + 2×fsync per participant, minimum.
text
on PREPARE:  run txn work → fsync PREPARED → reply YES/NO
on COMMIT:   apply txn → fsync COMMITTED → reply ACK
on ABORT:    rollback → fsync ABORTED → reply ACK

The honest cost sheet: 2PC adds roughly 2×RTT + 2×fsync over a single-node commit, it blocks on the slowest participant, and its failure mode is a stuck transaction, not a wrong one. The three-phase variant adds a preCommit round to shrink the blocking window but pays an extra RTT and still cannot survive a partition between two rounds — which is the deeper reason consensus protocols like Raft replace the coordinator instead of patching it.