Two-Phase Commit: The Protocol Behind Every All-or-Nothing Guarantee
Introduction
You are standing at an ATM abroad. You request $200. Behind the scenes, two banks must coordinate: your home bank needs to debit your account, and the local bank needs to authorise the cash dispense. If your account is debited but the machine jams, you lose $200. If the machine dispenses cash but the debit fails, the local bank eats the loss. Neither outcome is acceptable. Both sides must succeed together, or neither proceeds.
That is the problem Two-Phase Commit (2PC) solves. When a transaction spans multiple systems, 2PC ensures that they all commit or roll back together—all or nothing.
First described in Jim Grey’s 1978 paper, 2PC is one of the oldest protocols in distributed computing. It powers ATM networks, airline booking systems, distributed databases, and enterprise platforms. Whenever money moves, inventory is reserved, or seats are booked, 2PC is probably involved. In this post, we will examine how the protocol operates, the trade-offs it makes, the real-world systems that depend on it, and the modern patterns that support it.
Let’s get started!
How Two-Phase Commit Works
The protocol involves a coordinator (the node managing the transaction) and multiple participants (the nodes holding the data). It unfolds in two phases.
Phase 1: Prepare (The Vote)
1. The coordinator sends a PREPARE message to every participant.
2. Each participant executes the transaction locally, acquires locks, writes undo/redo information to its write-ahead log, and ensures it can commit if asked.
3. Each participant responds with YES (I can commit) or NO (I cannot).
4. This is the critical moment: once a participant votes YES, they have made a binding promise. It cannot unilaterally back out.
Phase 2: Commit or Abort (The Decision)
1. If every participant voted YES, the coordinator writes a COMMIT record to its log and sends COMMIT to all participants.
2. If any participant voted NO, the coordinator sends ABORT to everyone.
3. Each participant executes the decision, releases its locks, and acknowledges back to the coordinator.
In the normal case, this takes 2 network round-trip messages and 3N messages for N participants (N prepare messages, N votes, N commit/abort messages). In a same-datacenter deployment, this adds roughly 2-5ms of overhead per transaction.
The Trade-Off: Blocking
Every engineering decision has a cost. For 2PC, the cost is the potential for blocking.
Consider this scenario: all participants vote YES in Phase 1. The coordinator receives every vote and decides to commit. But before it can broadcast that decision, it crashes.
Now the participants are stuck. They voted YES, so they surrendered their right to abort. But they never received the commit message, so they cannot commit either. Their locks are secured. Their resources are frozen. They must wait indefinitely for the coordinator to recover.
This is not a theoretical concern. It has been formally proven by Bernstein, Hadzilacos, and Goodman that no commit protocol can simultaneously handle arbitrary failures *and* allow independent recovery. Blocking is baked into the math.
The dangerous window is typically very short (on the order of milliseconds in a healthy system). But when a coordinator failure coincides with that window, cascading lock contention can bring down significant portions of a database.
Why Not Three-Phase Commit?
Three-Phase Commit (3PC), proposed by Dale Skeen in 1981, adds an intermediate “pre-commit” phase to eliminate blocking. In theory, it works. In practice, 3PC assumes a fail-stop model with no network partitions. Real networks have partitions. This means 3PC can violate safety guarantees in production environments.
3PC also requires 3 round-trips instead of 2, adding latency to every transaction, even in the normal case. The result: virtually no production system uses 3PC. The industry moved in a different direction entirely.
2PC in the Wild
2PC is far more pervasive than most engineers realise. You interact with it every day.
Financial Systems
When you withdraw cash from an ATM that belongs to a different bank, 2PC coordinates the transaction. Your bank must debit your account, and the ATM’s bank must authorise the dispensing of cash. Both sides must agree, or neither proceeds. Interbank transfers, payment processing networks, and stock trading settlement systems all rely on the same all-or-nothing guarantee. In finance, “partial success” is not an option.
Travel and Booking Systems
Book a vacation package with a flight, hotel, and rental car. The booking system requires all three reservations to succeed. If the flight is confirmed but the hotel is sold out, you don’t want to be stuck with a flight to a city with nowhere to stay. Travel aggregators use 2PC (or 2PC-like protocols) to coordinate across multiple reservation systems.
Enterprise Systems
Enterprise Resource Planning (ERP) systems frequently coordinate transactions across inventory, billing, and shipping databases. When an order is placed, inventory must be decremented, an invoice must be created, and a shipment must be scheduled. XA transactions (the standardised 2PC interface) have been the backbone of Java enterprise systems for decades, coordinating database writes with JMS message queue publishes atomically.
Distributed Databases
Inside modern distributed databases, 2PC is the standard for cross-shard transactions. The database hides the complexity from you, but the protocol is running under the hood.
Google Spanner: 2PC at Global Scale
Spanner is the canonical example. It uses 2PC on top of Paxos groups. When a transaction spans multiple shards, the Paxos group leaders coordinate via 2PC. Each Paxos group independently replicates data, so if a coordinator fails, Paxos elects a new leader and the 2PC can continue.
The breakthrough is TrueTime, a clock system based on GPS receivers and atomic clocks that provides globally bounded clock uncertainty (typically under 7ms). Spanner uses TrueTime to assign commit timestamps that guarantee external consistency, then waits (”commit-wait”) until the uncertainty window passes before declaring the transaction committed.
CockroachDB: Parallel Commits
CockroachDB, inspired by Spanner but without specialised hardware, faced a problem: traditional 2PC required two sequential rounds of consensus writes, doubling latency for cross-range transactions.
Their solution, Parallel Commits, introduces a STAGED transaction status that can be written in parallel with the final batch of writes. Once all writes have succeeded, the commit cannot fail, so the client receives success immediately. This cuts the final-batch commit latency in half.
Dropbox Edgestore: Modified 2PC at 10M Requests/Second
Dropbox’s metadata store handles over 10 million requests per second using a modified 2PC. Their key optimisation: only 5-10% of transactions actually cross shard boundaries (thanks to careful data colocation). For those that do, an external durable transaction record serves as the source of truth, and a strongly-consistent cache absorbs 95%+ of reads.
When 2PC Shines
2PC is the right tool when:
Strong consistency is non-negotiable: financial transactions, inventory systems where overselling is catastrophic, and regulatory compliance
Participants are co-located: same data centre, sub-millisecond network latency, where the 2-5ms overhead is negligible
The participant count is small: 2-5 participants is the sweet spot
You control all participants: a single database system, a tightly coupled set of internal services, or systems that all support XA
2PC becomes more expensive when transactions are long-running (locks held for extended periods), participants are geographically distributed (100ms+ round-trip times), or the number of participants grows large. In those cases, the alternatives below can help.
Complementary Patterns
When 2PC is not the right fit, these patterns fill the gaps.
Saga Pattern
The most popular approach for coordinating across independent microservices. Each service executes a local transaction and publishes an event. If a step fails, compensating transactions undo previous steps. Sagas come in two flavours: choreography (event-driven, with no central coordinator) and orchestration (where a saga orchestrator directs each step). The trade-off is that sagas provide eventual consistency, not strong consistency. Other transactions can see intermediate states.
Transactional Outbox
Solves the specific “dual write” problem (updating a database and publishing a message atomically). The service writes both the business data and an outbox message to its local database in one transaction. A separate process reads the outbox and publishes to the message broker. No distributed transaction needed.
Deterministic Databases (Calvin)
Daniel Abadi’s research takes a different approach: eliminate 2PC by making execution deterministic. All nodes agree on the order of transactions before executing them. Because execution is deterministic, all replicas arrive at the same state without coordination. The catch: you need to know the full read/write set of every transaction in advance, which does not fit all workloads.
Conclusion
Two-Phase Commit is 47 years old and still foundational. The protocol’s math has not changed: it guarantees atomicity at the cost of potential blocking. What has changed is how we layer other techniques on top of it.
Google wraps 2PC with Paxos and TrueTime to make it work at a global scale. CockroachDB halves its latency with Parallel Commits. ATM networks and booking systems rely on it every second of every day. And when the trade-offs don’t fit, sagas and outbox patterns step in as complements, not replacements.
The next time you withdraw cash from a foreign ATM, book a multi-leg trip, or watch a distributed database commit across shards, remember: 2PC is doing the heavy lifting. Forty-seven years in, the protocol is as relevant as ever.

