# Two-Phase Commit vs Consensus — Distributed Systems & Consensus (Raft / Paxos)

Source: https://www.skillbyai.com/en/distributed-consensus/dc-2pc

> Trace two-phase commit, explain why it blocks on coordinator failure, and contrast it with majority-based consensus.

## Atomic commit is a different problem

**Two-phase commit (2PC)** makes a transaction across several databases all-or-nothing. In phase one the **coordinator** asks every participant to **prepare**; a participant that votes yes writes the change durably and promises it can commit, holding its locks. In phase two the coordinator records its decision and tells everyone to commit (if all voted yes) or abort. 2PC needs a vote from **every** participant, not a majority, because each one owns different data. Its weakness: if the coordinator crashes after participants voted yes, they are **in doubt**. They cannot commit alone (another may have voted no) and cannot abort alone (the coordinator may have decided commit), so they block with locks held until it recovers. Consensus solves a different problem (agreeing on one value among replicas of the same data) and survives a minority of failures. Modern systems such as Spanner and CockroachDB make 2PC fault tolerant by replicating each participant and the coordinator's state with Paxos or Raft.

## A toy 2PC showing the blocking case

If the coordinator disappears after the prepare phase, prepared participants are stuck.

```python
def two_phase_commit(votes, coordinator_crashes_after_prepare=False):
    # Phase 1: every participant votes and, if yes, holds its locks.
    if not all(votes.values()):
        return {p: "aborted" for p in votes}
    if coordinator_crashes_after_prepare:
        # Participants voted yes; they may not decide alone.
        return {p: "BLOCKED (prepared, holding locks)" for p in votes}
    # Phase 2: coordinator broadcasts the decision.
    return {p: "committed" for p in votes}

print(two_phase_commit({"db1": True, "db2": True}))
print(two_phase_commit({"db1": True, "db2": False}))
print(two_phase_commit({"db1": True, "db2": True}, coordinator_crashes_after_prepare=True))
```

Output:

```
{'db1': 'committed', 'db2': 'committed'}
{'db1': 'aborted', 'db2': 'aborted'}
{'db1': 'BLOCKED (prepared, holding locks)', 'db2': 'BLOCKED (prepared, holding locks)'}
```

## A wedding with two families

Both families must say yes before the wedding is fixed. Once both have said yes, they wait for the elder (coordinator) to announce the date. If the elder becomes unreachable at that point, nobody can call it off or book the hall: everyone is stuck waiting.

**Quiz:** Why can a 2PC participant that voted yes not decide on its own after the coordinator crashes?

- [x] It does not know whether every other participant voted yes
- [ ] It has already released its locks
- [ ] Its vote was not written to disk
- [ ] 2PC uses majority voting, so it must wait for a quorum

*Answer:* It does not know whether every other participant voted yes. Another participant may have voted no (so the result is abort) or all may have voted yes and the coordinator decided commit. The prepared participant cannot tell which.
