Distributed Systems · Distributed Transactions
What Three-Phase Commit Adds and What It Still Cannot Do
The pre-commit phase that removes blocking under node crashes, the assumption about network partitions that it silently depends on, and why real systems rarely deploy it.
Two-phase commit blocks when the coordinator is unreachable and some participant has voted yes. Three-phase commit is the classical answer to that, and it works by adding a state that a participant can safely act on without knowing the decision. Understanding what the extra phase buys requires being precise about which failures it covers, because the protocol’s reputation is better than its guarantee.
The insight: a state between yes and commit
The problem with a yes vote in two-phase commit is that it is irreversible and uninformative. A participant that has voted yes cannot abort, cannot commit, and cannot tell which is correct. The information it lacks is whether every other participant also voted yes.
Three-phase commit’s move is to make the irreversible step happen after the information arrives. The coordinator collects votes, then sends a pre-commit message to every participant. A participant receiving pre-commit knows that every participant voted yes, so commit is certain, but it does not yet commit. It records the pre-commit state and replies. When the coordinator has pre-commit acknowledgements from all participants, it sends commit.
The three phases are thus: can-commit (vote), pre-commit (decision known, not yet applied), and commit. The extra round trip is the price. The gain is that any participant, or any recovering coordinator, can look at the messages it has and determine the outcome without contacting anyone.
The recovery rules that make it non-blocking
The protocol is non-blocking under a specific failure model: nodes crash and restart, and messages are eventually delivered, but the network does not partition. Under that model, a recovering participant or coordinator can reconstruct the state from its own log and from whichever peers are reachable, using these rules.
If any participant is in the pre-commit state, the decision is commit. That participant has seen the coordinator’s pre-commit, which the coordinator only sends after every vote was yes. The evidence is sufficient on its own, and a recovering participant that finds a pre-commit record can commit without asking anyone.
If no participant is in pre-commit and some are in the prepared state, the transaction may still commit. The correct action is to wait, or to elect a new coordinator, and then let the new coordinator ask the prepared participants and continue. Since no participant has pre-committed, the transaction can still be aborted if the vote was not unanimous.
If every participant is in the prepared state and the coordinator is gone, a new coordinator is elected and finishes the protocol. The old coordinator’s death does not block progress because the participants’ states are enough to restart the second and third phases.
That last rule is what two-phase commit cannot offer. In two-phase commit, prepared participants have no evidence about each other, so a new coordinator cannot know whether the old one had already decided commit. In three-phase commit, the pre-commit state is a distributed copy of the decision, held by every participant that has received it, so the decision survives the coordinator.
Why the partition case breaks it
Now remove the assumption that the network does not partition. A coordinator is cut off from participants, or participants from each other, while all processes are alive.
Suppose the coordinator has sent pre-commit to a subset of participants and is partitioned from the rest. The participants that received pre-commit will, by the first rule, commit if they lose contact and decide to recover. The participants that did not receive it are prepared and, by the third rule, need a coordinator to proceed. A new coordinator elected on the other side of the partition can legitimately abort the transaction, because it sees only prepared participants and no pre-commit. Both halves follow their rules correctly, and the transaction commits on one side and aborts on the other.
This is not a bug in the protocol’s rules; it is the protocol being applied to a failure model it does not cover. Three-phase commit is a non-blocking atomic commit protocol for crash failures, and it cannot be non-blocking for partitions. The reason is fundamental rather than an implementation gap: under a partition, a participant cannot distinguish “the coordinator is dead” from “the coordinator is alive on the other side of a cut”, and those two cases require opposite actions. Any protocol that resolves the first case by proceeding must risk inconsistency in the second.
The formal statement of this is that atomic commit under partitions and crashes requires waiting, because a decision that is safe for one side is unsafe for the other. Consensus protocols avoid the dilemma by requiring a majority, which means one side of a partition has no authority to decide at all, so the minority waits. That is the same blocking, relocated from the coordinator to the minority side, and it is the better trade because it is bounded: the minority waits until the partition heals rather than until a specific process returns.
The message and state cost
Three-phase commit adds one round trip and one durable state transition per participant. In a local network, the round trip is a fraction of a millisecond and the record is a small log entry, so the absolute cost is modest. Relative to two-phase commit, the critical path grows from two message round trips (prepare and vote, then decision and acknowledgement) to three (can-commit and vote, pre-commit and acknowledgement, then commit).
There is a further cost that is easy to miss. Every participant must retain its pre-commit record until it learns that the transaction is fully resolved, and it must be able to answer queries about the transaction’s state from a recovering coordinator. The retained state per participant is one record either way, but the protocol now has an additional state that must be handled in every code path that inspects prepared transactions: the local transaction manager must know that a pre-committed transaction cannot be rolled back by any local rule, and must not expose it as a deadlock victim.
The extra phase also lengthens the window during which locks are held, since the locks are taken at prepare and released at commit, and the interval between them now contains three message hops rather than two. For a transaction holding hot rows, a longer hold is a longer period of contention, which is a throughput cost that does not appear in the message count.
Where the state machine is genuinely useful
The pre-commit idea appears in real systems in forms that are not called three-phase commit. The important one is the replicated decision log. If the coordinator’s decision is written to a log replicated across a majority, then a new coordinator can read the decision, and participants can learn it from any replica. This gives the same property three-phase commit aims at, namely that the decision survives the coordinator, without adding a phase, because the replication itself is the durable copy.
Consensus-based commit protocols such as Paxos Commit and the commit path in Raft-based systems take this route. The coordinator’s decision is a log entry, the log is replicated, and the recovery rule is “read the log”. The cost moves from an extra protocol phase to the cost of replicating every decision, which is a steady-state cost paid on every transaction rather than a latency cost paid on the critical path. Which is cheaper depends on the ratio of transactions to failures: a system with rare coordinator failures prefers the cheaper common path, and a system with frequent failures prefers the replicated log.
The other place the pre-commit state shows up is in two-phase commit variants that allow a participant to resolve its own in-doubt state after asking a quorum of peers. If a majority of participants report prepared-but-no-pre-commit, and the protocol can rule out a decision having been made, the transaction can be aborted. That is a quorum-based approximation of the same information, and it has the same limitation: it needs a majority to be reachable, which is exactly the condition under which consensus-based commit also makes progress.
The practical verdict
Three-phase commit is rarely deployed as such. The reasons are worth separating, because the protocol is correct within its model.
First, the failure model it targets, crash without partition, is not the failure model of a real network. Machines crash, but links also drop, switches fail, and routing changes. A protocol whose correctness depends on the absence of partitions is a protocol whose correctness depends on a condition the operator cannot verify.
Second, the blocking it removes is blocking that a replicated decision log removes more completely. Two-phase commit plus a majority-replicated coordinator log tolerates coordinator crashes without an extra phase and tolerates partitions by refusing to decide on the minority side. That is a strictly stronger guarantee than three-phase commit’s crash-only one.
Third, the extra phase costs latency on every transaction to protect against a failure that, in most deployments, is handled by the coordinator’s restart rather than by protocol-level recovery. The usual engineering judgement is that a coordinator that restarts in seconds does not need a protocol that avoids waiting for it.
What survives from three-phase commit is the analytical insight: the decision must be copied to more than one place before it is acted on, or the system will block on the loss of that place. That insight is implemented by log replication rather than by an extra message, which is why the protocol’s ideas are more influential than its deployments. The same reasoning about what a participant can infer from its own durable state drives the recovery rules in in-doubt transactions and blocking, and the replication of the decision itself is the subject of replicated commit decisions.
References
- Nonblocking Commit Protocols — Dale Skeen, ACM SIGMOD, 1981
- Transaction Processing: Concepts and Techniques — Jim Gray and Andreas Reuter, Morgan Kaufmann, 1992
- Concurrency Control and Recovery in Database Systems — Philip Bernstein, Vassos Hadzilacos and Nathan Goodman, Addison-Wesley, 1987