SysDesignPrep.com
Interactive concepts

Two-phase commit (2PC): an interactive sequence diagram

Two-phase commit (2PC) makes one transaction commit on several databases together, or on none of them. A coordinator first asks every participant to prepare, to make its changes durable and promise to commit, and only if every one votes yes does it tell them all to commit. The price is that a participant that has voted yes must wait for the decision with its locks held, and if the coordinator fails at the wrong moment, it waits until the coordinator comes back.

The playground runs one order across three databases: orders, payments and stock. Pick what goes wrong and read the sequence diagram from top to bottom: arrows are messages, and the bar beside each line shows what that database is doing.

Coordinator down forapplies to the coordinator crashes

All three databases vote YES, so the coordinator logs COMMIT 46 ms in and tells them; each commits and releases its locks, the last 62 ms after the start. Between voting YES and hearing the outcome a participant is in doubt: it has promised to commit if told and cannot back out, here for up to 39 ms. Two round trips and 12 messages for one transaction.

CoordinatorOrders DBPayments DBStock DBCoordinator: coordinatingOrders DB: workingOrders DB: prepared, in doubtOrders DB: committedPayments DB: workingPayments DB: prepared, in doubtPayments DB: committedStock DB: workingStock DB: prepared, in doubtStock DB: committedPREPAREPREPAREPREPAREYESYESYESCOMMITCOMMITCOMMITACKACKACK
Outcomecommittedon every database that answered
Decided after46 mswhen the coordinator logs it
Locks held up to62 mslongest, from the start
Messages12none lost
working, locks heldprepared, in doubtcommittedaborteddown

How two-phase commit works

  1. Execute: each participant does its part of the transaction and takes its locks, without committing.
  2. Prepare: the coordinator sends PREPARE. Each participant writes its changes and a prepare record to its log, durably, then votes YES, or votes NO and rolls back.
  3. Decide: if every vote is YES the coordinator writes COMMIT to its own log; one NO, or a vote that never comes, means ABORT. Writing the decision down is the moment the transaction commits.
  4. Complete: the coordinator sends the decision; each participant commits or rolls back, releases its locks and acknowledges. Once all have acknowledged, the coordinator can forget the transaction.

Why two-phase commit blocks

A participant that votes YES gives up the right to decide. It cannot commit on its own, because another participant may have voted NO, and it cannot abort, because the coordinator may already have logged COMMIT and told someone else. Until it hears the decision it is in doubt, and it keeps its locks, so every other transaction that needs those rows waits too.

Normally that lasts about a round trip, 30 to 40 ms in the first preset. But if the coordinator crashes after the votes, the participants are stuck for as long as it is down: 3 seconds or 30 seconds in the playground, hours if it needs an operator. A crash after deciding is subtler: one database has committed and the others are holding their locks waiting to find out. Three-phase commit tries to remove the blocking with timeouts, but it is only safe if message delays are bounded, which real networks do not promise. Systems that need 2PC to be available instead make the coordinator itself fault tolerant: Google Spanner runs each participant and the coordinator as a Paxos-replicated group, so a crashed machine is replaced rather than waited for.

The cost when nothing fails

  • Latency: two round trips to every participant, plus a durable log write on each and on the coordinator.
  • Contention: locks are held across both round trips, so hot rows serialise transactions for longer.
  • Availability: every participant must be up to commit; with five participants each up 99.9% of the time, a transaction that needs all of them can commit about 99.5% of the time.

Two-phase commit vs sagas

Two-phase commitSaga
AtomicityAll commit or none doEach step commits; failures are undone by compensating steps
IsolationOthers never see a half-done transactionIntermediate states are visible
LocksHeld across the network until the decisionOnly within each local transaction
A participant is downNothing can commitThe saga waits or compensates
Used forDatabases that share a transaction manager; inside distributed SQL databasesWorkflows across services and APIs

Across microservices, and across third parties such as a payment provider that will never take part in your protocol, sagas with idempotent steps and an outbox are the usual answer. Two-phase commit lives inside systems that control every participant: XA transactions across databases, PostgreSQL’s PREPARE TRANSACTION, distributed SQL databases such as Spanner and CockroachDB, and Kafka’s transactions, whose coordinator writes commit markers to every partition involved.

Two-phase commit in SQL

-- PostgreSQL: each participant prepares, and the coordinator finishes it later
BEGIN;
UPDATE accounts SET balance = balance - 40 WHERE id = 7;
PREPARE TRANSACTION 'order-1842';      -- phase 1: durable, locks kept, vote YES

-- ... the coordinator collects every vote and logs its decision ...

COMMIT PREPARED 'order-1842';          -- phase 2, if every vote was YES
-- or: ROLLBACK PREPARED 'order-1842';

-- after a crash, prepared transactions are still there, still holding locks:
SELECT gid, prepared FROM pg_prepared_xacts;

In a system design interview

When a transaction spans services, name the choice: “Placing an order touches the orders, payments and inventory services, each with its own database. I would not use two-phase commit across them: it holds locks across the network and blocks if the coordinator fails, and the payment provider cannot take part anyway. A saga reserves stock, charges the card and confirms the order, each step idempotent, and if the charge fails it releases the reservation.” If the data lives in one distributed database, say that it runs a form of two-phase commit for you internally, and what that costs in latency.

Read the guide to distributed transactions and idempotency, see Raft for how a coordinator can be made fault tolerant, or watch a strong candidate design a payment system.

Questions people ask

What is two-phase commit?

A protocol that makes a transaction spanning several databases commit on all of them or on none. A coordinator asks every participant to prepare, making its changes durable and promising to commit; if all vote yes it logs COMMIT and tells them to commit, and if any votes no or does not answer it tells them to abort.

What are the two phases of two-phase commit?

The prepare phase, in which the coordinator sends PREPARE and each participant writes its changes and a prepare record durably and votes yes or no; and the commit phase, in which the coordinator logs its decision and sends COMMIT or ABORT, and each participant applies it, releases its locks and acknowledges.

Why is two-phase commit called a blocking protocol?

Because a participant that has voted yes cannot decide on its own: another participant may have voted no, or the coordinator may already have committed. If the coordinator crashes after collecting votes, prepared participants keep their locks until it recovers, blocking other transactions on those rows, for as long as the outage lasts.

What happens if the coordinator fails in two-phase commit?

If it fails before logging a decision, prepared participants wait in doubt; when it recovers and finds no decision in its log it aborts. If it fails after logging COMMIT, some participants may have committed while others wait; on recovery it re-sends COMMIT to every participant that has not acknowledged, so applying a commit twice must be harmless.

What is the difference between two-phase commit and a saga?

Two-phase commit gives atomic, isolated commits across participants but holds locks across the network and blocks if the coordinator fails. A saga runs a local transaction per step and undoes completed steps with compensating actions if a later one fails, so nothing is locked across services, but intermediate states are visible. Sagas are the usual choice across microservices.

Does two-phase commit guarantee consistency?

It guarantees atomicity: all participants commit or all abort, as long as every participant keeps its promise after voting yes and the coordinator’s log survives. It does not by itself make the system available during failures, and it says nothing about the isolation of concurrent transactions, which each participant’s locks provide.

Where is two-phase commit used?

In XA transactions across databases and message brokers, in PostgreSQL’s PREPARE TRANSACTION and COMMIT PREPARED, inside distributed SQL databases such as Google Spanner and CockroachDB, and in Kafka transactions, whose coordinator writes commit markers to every partition a transaction touched.