Skip to content
matthewcm.dev

    ↑ ↓ move · ↵ open · tab scopeopen graph →

    Two-Phase Commit

    A coordinator asks every participant to prepare, then tells them all to commit or abort. It gives atomic distributed transactions, but blocks if the coordinator fails at the wrong moment.

    Growingtended planted 2 min read
    On this page

    Two-phase commit (2PC) makes a transaction that spans several databases or services atomic: either every participant commits, or none does.

    The protocol

    Phase 1: prepare (voting). The coordinator sends prepare to every participant. Each one does the work, makes it durable without making it visible, holds its locks, and votes yes (“I can definitely commit”) or no.

    Phase 2: commit or abort. If every vote was yes, the coordinator writes its decision to its log and sends commit. If anyone voted no or timed out, it sends abort. Participants act and acknowledge.

    The crucial promise is the yes vote: once a participant votes yes, it has given up the right to abort on its own. It must wait for the coordinator’s decision.

    The weakness: blocking

    If the coordinator crashes after participants voted yes but before they hear the decision, they’re stuck. They can’t commit (someone might have voted no) and can’t abort (the coordinator might have decided commit). They hold their locks until the coordinator recovers. That’s why 2PC is called a blocking protocol, and why it isn’t a full answer to The Consensus Problem: one failure can halt progress.

    Fixes exist: three-phase commit (which assumes bounded delays), or making the coordinator itself fault-tolerant by replicating its log with consensus (Raft). That’s roughly what modern distributed databases do.

    An example

    Transferring money between accounts held in two databases: debit one, credit the other. Both prepare (funds reserved, credit staged), the coordinator records “commit”, both apply. If the second bank’s database says no, the reservation is released. No money appears or vanishes.

    Alternatives in microservices

    2PC couples services tightly and holds locks across the network, so many microservice architectures avoid it:

    • Sagas: a sequence of local transactions, each with a compensating action to undo it if a later step fails.
    • Transactional outbox plus idempotent consumers (Message Delivery Semantics).

    Both trade immediate atomicity for availability and looser coupling, the same trade-off as in the CAP Theorem.

    In practice

    I’ve never hand-rolled 2PC, and I hope I never will. But knowing how it works explains why “just update both services in one request” is a much harder problem than it sounds, and why workflow platforms model long-running processes as steps with compensation rather than one big transaction.

    • #distributed-systems
    • #transactions
    • #consensus
    • #databases

    ↪ Links to (4)