DBRaven
Pattern · consistency

Two-Phase Commit (2PC)

mature

Summary

Commit one transaction atomically across several independent database participants using a prepare phase (every participant votes that it can commit) followed by a commit phase (the coordinator tells all to commit or all to abort). It buys all-or-nothing across nodes at the cost of availability: a coordinator failure leaves prepared participants blocked.

Problem

Business operations that write to multiple independent databases need an atomic commit guarantee a single-database transaction cannot provide; partial commits (one database commits, another fails) leave data inconsistent with no clean recovery.

Description

Two-phase commit is the standard protocol for committing one logical transaction across multiple independent participants atomically. Without it, a transaction spanning two databases (debit account A on database 1, credit account B on database 2) can partially commit: the debit succeeds and the credit fails, destroying or creating money. 2PC makes the outcome all-or-nothing.

The protocol has two phases. In the prepare phase the coordinator sends PREPARE to every participant. Each participant does all the work needed to guarantee it can commit, writes that intent durably to its own log, and votes yes or no. A yes vote is a promise: the participant has given up the right to abort on its own and must now commit if told to. In the commit phase, once the coordinator has collected the votes it decides, writes that decision durably to its own log, and only then sends COMMIT or ABORT to all. The commit point is the moment the coordinator's decision becomes durable; everything after it is just carrying the decision out.

Prepared-transaction lifecycle in PostgreSQL. PREPARE TRANSACTION persists an otherwise-normal transaction as a named, durable, suspended transaction that survives a server restart and outlives the session that created it, holding its locks the whole time. This requires max_prepared_transactions to be greater than 0; it defaults to 0, so 2PC is disabled until an operator enables it. Prepared transactions appear in pg_prepared_xacts and are completed with COMMIT PREPARED or ROLLBACK PREPARED. One that is never completed holds its locks indefinitely, which is the primary day-to-day hazard.

Coordinator decision durability and in-doubt transactions. The coordinator must fsync its commit-or-abort decision before sending it; a coordinator that loses its decision log cannot tell participants what it chose. If the coordinator crashes after participants have prepared but before they receive the decision, those participants are in-doubt: they have promised to commit, so they cannot unilaterally abort, and they have no decision, so they cannot commit. They hold their locks and wait for the coordinator to recover. This is the blocking problem, and it is inherent: 2PC is not fault-tolerant, and the coordinator is a single point of failure. Rows touched by an in-doubt transaction are locked and unavailable for the whole window.

Heuristic decisions. To escape a long in-doubt block, an operator (or an XA transaction manager) can force a prepared participant to COMMIT PREPARED or ROLLBACK PREPARED to release its locks. If that forced guess disagrees with the coordinator's eventual decision, atomicity is violated: some participants commit while others abort. XA names these heuristic decisions and treats them as a hazard to be reported, not a normal recovery path.

What 2PC does and does not give. It gives atomic commit across participants: all commit or all abort. It does not give isolation between concurrent transactions, which remains each participant's local isolation level, and it does not give linearizability, since it enforces no global real-time order across participants. Buying atomicity this way moves cost onto latency and availability: two round trips to each participant instead of one (under a 20ms WAN RTT, 2PC adds 40ms minimum per transaction), and a coordinator failure blocks everyone who prepared.

Tradeoffs

Consistency
+1.0

True atomic commit across participants; no partial commits possible

Latency
-0.7

Double the write latency of a single-database transaction (two phases)

Availability
-0.8

Coordinator failure leaves prepared participants blocked until recovery

Throughput
-0.6

Lock hold time spans both phases; concurrent transactions on the same rows block

Operational complexity
-0.7

In-doubt recovery and coordinator HA require dedicated tooling and runbooks

When to use

All participants are ACID-capable databases supporting the 2PC protocol

2PC requires durable prepare support from every participant; document stores and most NoSQL databases do not provide it.

Atomic commit across databases is a hard requirement

If eventual consistency is acceptable, Saga is a less blocking alternative with lower operational complexity.

Transaction scope is narrow (2 to 3 participants, bounded duration)

2PC latency and the blocking window scale with participant count and RTT; wide or long-running transactions amplify both.

Coordinator failure recovery is operationally managed

Mandatory, not merely preferred: in-doubt transactions from a coordinator crash require managed recovery; without it, 2PC is unsafe in production.

When not to use

Participants span multiple services with independent databases

Cross-service 2PC couples services at the transaction level and requires every service to expose prepare support; Saga is the standard alternative.

Network latency between participants is high (>10ms)

At 20ms RTT 2PC adds 40ms minimum per transaction; at 100ms WAN latency it adds 200ms, which is unacceptable for interactive workloads.

High availability is required and coordinator failure cannot be tolerated

Coordinator failure blocks prepared participants; if availability is the primary constraint, eventual-consistency patterns are safer.

Operational Requirements

mandatory

Enable and bound prepared transactions (max_prepared_transactions > 0) and monitor pg_prepared_xacts

Prepared transactions are disabled by default (max_prepared_transactions = 0). Once enabled, an in-doubt prepared transaction holds row locks indefinitely; alert on any older than 30 seconds and drive it to completion.

mandatory

Persist the coordinator's commit/abort decision durably before sending it

A coordinator that crashes having lost its decision log leaves permanent in-doubt transactions; the decision must be fsynced before any participant is told.

mandatory

Set statement_timeout on 2PC transactions to bound the lock-hold window

Unbounded transaction duration widens the in-doubt window; bounding duration bounds the worst-case blocking.

mandatory

Build and document an in-doubt recovery runbook, including heuristic-decision policy

On-call engineers must know how to identify in-doubt transactions and when, if ever, a heuristic commit or rollback is permitted, given it can violate atomicity.

Characteristics

Scales on
Implementation complexityhigh
Operational complexityvery high
Scaling ceiling2PC throughput scales inversely with participant count and latency. At 3 participants with 5ms RTT each, minimum transaction latency is about 30ms (two phases across three participants). Locks held across the prepare-to-commit window block concurrent transactions on the same rows, and the in-doubt window under coordinator failure extends that block indefinitely. High-throughput systems (>10,000 TPS) cannot use multi-participant 2PC; the protocol is built for correctness, not throughput, and the coordinator needs its own HA and durable decision log.

Technologies

Canonical

postgresql

Alternatives

xa transactionscockroachdbspanneryugabytedb

Relationships

Evolves to

saga pattern

Conflicts with

database per serviceevent sourcingsaga pattern

Basis

Precisely specified protocol with a 40-year operational history; the blocking behavior under coordinator failure, the prepared-transaction lifecycle, and heuristic-decision hazards are well documented and are the primary reasons teams move to Saga.

Sources & Claims

2PC provides atomic_multi_object: coordinates an atomic distributed transaction across multiple independent database participants, so a partial commit (debit succeeds, credit fails) cannot occur.

approved

ddia or accepted reference · provided-guarantees-red-team.md, precise-consistency-model archive

Withheld on purpose: serializable, linearizable. 2PC is an atomic-commit protocol, not an isolation level or a global recency guarantee; those depend on the surrounding isolation and read model, which 2PC does not set.

Related Architecture Knowledge

Outbound: this entity affects

Introduces RiskFailure Mode
split brain
Grounded

Two-phase commit's coordinator is a single point of failure. If the coordinator crashes after sending the prepare phase but before completing the commit phase, participants are left in an uncertain state: some may have committed and some not, creating a split-brain condition that requires manual operator intervention.

Tradeoffs

  • ·2PC is blocking: if any participant cannot respond, the entire transaction is blocked indefinitely
  • ·Locks held during 2PC are held across the network: lock duration includes network latency
  • ·The coordinator is a scalability bottleneck: all distributed writes serialize through it
Full relationship →

Inbound: affects this entity

MitigatesPattern
saga pattern
Grounded

Saga replaces two-phase commit with a sequence of local transactions and compensating transactions, eliminating the blocking distributed lock problem of 2PC at the cost of eventual consistency and more complex failure handling.

Tradeoffs

  • ·Saga does not provide atomicity: intermediate states are visible between steps
  • ·Compensating transactions add implementation complexity: every step needs a corresponding undo operation
  • ·{'Eventual consistency': 'a saga rollback takes time: the system is briefly inconsistent during compensation'}
Full relationship →

Used In Architecture Scenarios