Back to Library
system_design

Two-Phase Commit (2PC)

2PC coordinates an atomic transaction across multiple participants using a prepare phase and a commit phase.

distributed-systemsconsensusdatabases

What is two-phase commit (2PC)?

Two-Phase Commit (2PC) is a distributed protocol that ensures an atomic transaction across multiple participants (databases, services). Either all participants commit the transaction or all abort - no partial commits.

The Protocol

There are two roles: a Coordinator (the node initiating the transaction) and Participants (the nodes that will commit or abort).

Phase 1: Prepare

  1. Coordinator sends PREPARE to all participants
  2. Each participant:
    • Locks all resources needed for the transaction
    • Writes the transaction to its WAL (durable even if it crashes)
    • Responds YES (ready to commit) or NO (cannot commit)
  3. Participants that reply YES are now in a prepared state - they've promised to commit if asked

Phase 2: Commit or Abort

If coordinator receives YES from all participants:

  • Sends COMMIT to all participants
  • Participants commit, release locks, acknowledge

If coordinator receives any NO (or timeout):

  • Sends ABORT to all participants
  • Participants rollback, release locks

The Blocking Problem

2PC's fundamental flaw: it blocks when the coordinator fails.

Scenario:

  1. All participants respond YES (prepared state, locks held)
  2. Coordinator crashes before sending COMMIT or ABORT
  3. Participants are stuck: they promised to commit but can't proceed without the coordinator's decision
  4. They hold their locks - blocking other transactions

Participants cannot unilaterally commit (might violate atomicity) or abort (coordinator might have sent COMMIT to some participants before crashing).

Resolution: a new coordinator must be elected and query participants' state, or wait for the original coordinator to recover.

Three-Phase Commit (3PC)

3PC adds a PRE-COMMIT phase between Prepare and Commit to reduce the blocking window:

  1. PREPARE -> participants respond YES
  2. PRE-COMMIT -> coordinator confirms all said yes; participants acknowledge
  3. COMMIT -> all commit

If coordinator fails after pre-commit, participants know all others said YES and can safely commit. But 3PC is more complex and still has edge cases in network partitions - not widely used in practice.

XA Transactions

XA is the standard specification for distributed transactions using 2PC. Most relational databases support XA: PostgreSQL, MySQL, Oracle. Java applications use JTA (Java Transaction API) to coordinate XA transactions.

XA works across heterogeneous systems (different databases in the same transaction) but carries all 2PC limitations.

2PC vs Saga Pattern

2PCSaga
AtomicityTrue ACID across participantsEventual consistency via compensation
BlockingYes (coordinator failure)No
AvailabilityLower (locks held)Higher
ComplexityLower (single protocol)Higher (compensating transactions)
Use whenSame-org databases, low latencyMicroservices, high availability needed

When to Use 2PC

✅ All participants are within your control and on low-latency links ✅ True atomicity is required (financial ledger, inventory) ✅ Failure scenarios are rare and acceptable downtime is tolerable

❌ Microservices across organisational boundaries ❌ High-availability requirements (blocking is unacceptable) ❌ High-latency links between participants

Interview Angle

For "how do you handle distributed transactions?", explain both 2PC and Saga and their tradeoffs. 2PC gives true atomicity but blocks on coordinator failure. Saga gives high availability but requires compensating transactions and accepts eventual consistency. For microservices -> prefer Saga. For same-database multi-table -> use native transactions. 2PC is the middle ground most people never actually need.

Recommended Resources