Patterns of Distributed Systems
Based on: Catalog of Patterns of Distributed Systems by Unmesh Joshi (published on martinfowler.com, Nov 2023)
Table of Contents
Group H — Distributed Transactions
Sometimes you need to update data on multiple nodes atomically — all updates succeed or all fail.
H1. Two-Phase Commit
The classic protocol for atomic distributed transactions.
Problem
A transaction updates data on two or more nodes (e.g., debit $100 from Account A on Node 1, credit $100 to Account B on Node 2). If Node 2 crashes after Node 1 debits, you have inconsistent state: money vanished.
You need atomicity across nodes: either both updates commit, or neither does.
Solution
Divide the commit into two phases, coordinated by a transaction coordinator:
Phase 1 — Prepare (Voting): The coordinator asks each participant: "Are you ready to commit?" Each participant:
- Acquires locks on affected data
- Writes the proposed changes to its WAL (but does NOT commit yet)
- Votes YES (ready) or NO (cannot commit)
Phase 2 — Commit or Abort:
- If all participants voted YES → coordinator sends COMMIT to all; participants commit and release locks
- If any participant voted NO (or timed out) → coordinator sends ABORT to all; participants roll back
How it works
Coordinator: "Prepare tx-123: debit A=$100, credit B=$100"
Node 1 (has Account A): locks A, writes to WAL, votes YES
Node 2 (has Account B): locks B, writes to WAL, votes YES
Coordinator: all YES → "Commit tx-123"
Node 1: commits → Account A -= $100, releases lock
Node 2: commits → Account B += $100, releases lock
Transaction coordinator: writes commit record
--- If Node 2 votes NO ---
Coordinator: "Abort tx-123"
Node 1: rolls back WAL changes, releases lock
Node 2: rolls back WAL changes, releases lock
The blocking problem: If the coordinator crashes after Phase 1 but before Phase 2, participants are stuck holding locks — they voted YES but don't know if the coordinator decided to commit or abort. This is the fundamental limitation of 2PC.
Real-world analogy
A marriage ceremony. The officiant (coordinator) asks each party "Do you take...?" (Phase 1 — prepare). Only after both say "I do" does the officiant declare "I now pronounce you married" (Phase 2 — commit). If anyone says "No," the ceremony is cancelled.
Example systems
XA transactions (JTA in Java EE), distributed SQL databases (CockroachDB, Spanner use 2PC variants), some message queues
Trade-offs
| Pro | Con |
|---|---|
| Atomic updates across multiple nodes | Blocking protocol — coordinator failure leaves participants locked |
| Well-understood, widely implemented | Latency: 2 round trips minimum |
| Works with heterogeneous systems (via XA) | Locks held for the entire 2PC duration — poor for high contention |
| Not fault-tolerant on its own — needs a coordinator that can recover |
Mitigations for blocking:
- Use a persistent coordinator that can recover from WAL
- Use Consistent Core + Replicated Log as the coordinator for fault tolerance
- Use saga patterns or eventual consistency instead for long-running transactions
Related patterns
→ Write-Ahead Log (both coordinator and participants log their state) → Consistent Core (often hosts the coordinator for fault tolerance) → Clock-Bound Wait (Spanner uses this to order 2PC transactions)
0 Comments