Classic distributed transaction: Coordinator + 3 Participants, PREPARE/VOTE/COMMIT/ROLLBACK phases
Two-Phase Commit is an atomic commitment protocol used to coordinate distributed transactions across multiple databases or resource managers. It guarantees that either all participants commit the transaction or all abort it -- achieving atomicity in a distributed environment.
2PC is a foundational protocol in distributed systems, used in:
A central process responsible for orchestrating the protocol. The Coordinator assigns a transaction ID, sends protocol messages to all participants, collects votes, makes the commit/abort decision, and logs the decision to durable storage before broadcasting it.
Database instances that hold the data being modified. Each participant independently evaluates whether it can commit the transaction, writes to its Write-Ahead Log (WAL), acquires and holds locks on affected rows, and votes YES or NO.
A durable log maintained by the Coordinator that records the transaction state and the final decision. This log is critical for crash recovery -- if the Coordinator restarts, it consults the log to determine whether to commit or abort in-doubt transactions.
The Coordinator sends a PREPARE message to every participant. Each participant performs all transaction work (constraint checks, WAL writes, lock acquisition) and responds with either YES (ready to commit, locks held) or NO (cannot commit). A YES vote is a binding promise: the participant guarantees it can commit the transaction if asked.
If all participants voted YES, the Coordinator writes a COMMIT decision to its log and sends COMMIT to all participants. If any participant voted NO or timed out, the Coordinator writes an ABORT decision and sends ROLLBACK. Participants apply the final decision, write the outcome to their WAL, and release locks.
The moment the Coordinator writes COMMIT to its durable log is the point of no return. After this, the Coordinator will retry COMMIT to every participant until all acknowledge, regardless of crashes or network failures. Before this point, any failure defaults to ABORT.
2PC is a blocking protocol. After a participant votes YES, it holds locks and waits for the Coordinator's decision. If the Coordinator crashes between Phase 1 and Phase 2, participants remain blocked -- holding locks indefinitely until the Coordinator recovers. This is the fundamental weakness of 2PC.
3PC was proposed as a non-blocking improvement. It adds a PRE-COMMIT phase between voting and committing, allowing participants to time out and make progress without the Coordinator. However, 3PC is rarely used in practice because it cannot handle network partitions correctly (it may violate safety under partition) and adds extra round trips, increasing latency. Most systems prefer 2PC with heuristic recovery or move away from distributed transactions entirely.
2PC is appropriate when you need strong atomicity across a small number of databases within the same data center, with low-latency, reliable networks. Common use cases include XA transactions spanning two databases, prepared transactions in PostgreSQL, and cross-shard commits in distributed databases.
2PC is a poor fit for microservices communicating over HTTP, cross-datacenter transactions (high latency amplifies the blocking window), high-throughput systems where lock contention is unacceptable, or any environment where participants or the network are unreliable. In these cases, the Saga pattern or event-driven architectures with eventual consistency are preferred.