Knowledge Guide
HomeSystem DesignMicroservices Patterns

The Problem Traditional Transaction Models

A single-node database can promise all-or-nothing cheaply because one lock manager guards every row and one write-ahead log (WAL) sits behind every write, so a whole transaction commits with a single fsync of one commit record; the moment you split those rows across an order service, an inventory service, and a payment service on three different machines, that shared log and shared lock manager are gone, and the only way to keep atomicity is a coordinator that forces every node to first promise it can commit and then commit in lockstep — a protocol called two-phase commit (2PC), whose promise step is exactly what wedges the system when a machine dies.

Why ACID is easy on one node

ACID is not four unrelated features; on a single node it is mostly one mechanism reused four ways. Atomicity and Durability come from the WAL: the transaction's changes are appended to the log, and a single durable commit record flips the whole transaction from "nothing happened" to "everything happened." Isolation comes from the lock manager (or MVCC snapshots) inside the same process, which sequences concurrent transactions as if they ran one at a time. Consistency is the invariant those three preserve — the database only ever moves from one valid state to another. Because a single log and a single lock manager arbitrate everything, "commit" is one local, ordered, durable act.

Distribute the data and every one of those crutches disappears: there is no shared log to append a single commit record to, and no shared lock manager to serialize contenders. Atomicity now requires that three independent databases, reachable only over a flaky network, all flip together — or none do.

The concrete mechanism that tries — and blocks: 2PC

Two-phase commit introduces a coordinator (a transaction manager) and treats each service's database as a participant (resource manager). It runs in two rounds:

The trap lives between the phases. Once a participant has voted YES it is in-doubt: it is holding locks and has promised it can commit, so it must not unilaterally commit (the decision might be abort) and must not unilaterally abort (the decision might be commit). It can only wait for the coordinator.

Worked trace: checkout for order #7788, total $85

The coordinator is the checkout orchestrator; the three participants each own their own database. First the happy path succeeds atomically:

SteptActorActionResulting state
10 msCoordinatorWrites BEGIN to its log; sends PREPARE to all threeTxn open
215 msInventoryReserves SKU-42 (stock 10 → 9), locks the row, logs prepareYES — row locked
320 msOrderInserts order #7788 status=PENDING, locks row, logs prepareYES — row locked
440 msPaymentPlaces an $85 authorization hold, logs prepare (cross-region hop)YES — funds held
545 msCoordinatorAll votes YES → writes COMMIT to its log (atomic commit point)Decision durable
660 msAll threeReceive COMMIT, apply, release locks, ackCommitted — atomic

Now change one thing: the coordinator crashes at ~45 ms, after collecting all three YES votes but before the decision reaches anyone (or its decision log is lost). Every participant is prepared and in-doubt. Inventory still holds the lock on SKU-42; Payment still holds the $85 authorization; Order's PENDING row is locked. None of them may proceed. The SKU-42 row stays locked, so every other checkout touching SKU-42 now blocks behind it. Nothing frees until the coordinator reboots and replays its log — and if that log is gone, an operator must manually resolve the in-doubt transaction. That indefinite freeze is the blocking problem, and it is inherent: no atomic-commit protocol can be non-blocking when both the network and nodes can fail.

diagram
diagram

Pitfalls

When to use 2PC — and when to reach for a Saga instead

2PC is the right tool when you genuinely need immediate, isolated atomicity across a small, closed set of XA-capable resources inside one latency and trust domain — e.g., two databases plus a message broker in the same datacenter, low write volume, short transactions, and a correctness requirement that outranks availability. You gain true all-or-nothing with isolation (no one sees intermediate states). You pay for it with synchronous blocking, cross-network locks, a coordinator SPOF, throughput that falls off a cliff under contention, and unavailability during a network partition (2PC is firmly CP).

Prefer the Saga pattern when the participants are independently-owned services, transactions are long-lived, throughput is high, or you must stay available during partial failure. A Saga runs the checkout as a sequence of local transactions, each committing immediately, with an explicit compensating action (cancel the reservation, void the authorization) to undo earlier steps on failure. The cost is real: you lose isolation — other requests can observe the half-done state — and you must design and test semantic rollbacks by hand. A middle option, 3PC, adds a round trip to reduce blocking but still blocks under network partitions, so it is rarely used in practice.

Choose 2PC when a couple of XA resources must be atomically consistent, volume is low, and a brief blocking window is acceptable. Prefer a Saga when services are autonomous, transactions span teams or time, and you can trade strict isolation and immediacy for availability and scale. And before either — if the data really must be atomic and consistent together, ask whether those services should share one database at all; collapsing them back is often the honest answer.

Takeaways


Re-authored and deepened for this guide. Sources: Gray & Reuter, Transaction Processing: Concepts and Techniques (atomic commit, 2PC blocking); Bernstein & Newcomer, Principles of Transaction Processing (XA, heuristic decisions); Martin Kleppmann, Designing Data-Intensive Applications, ch. 9 (distributed transactions and the impossibility of non-blocking atomic commit); Chris Richardson, Microservices Patterns (why 2PC is avoided, the Saga alternative).

🤖 Don't fully get this? Learn it with Claude

Stuck on The Problem Traditional Transaction Models? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **The Problem Traditional Transaction Models** (System Design) and want to truly understand it. Explain The Problem Traditional Transaction Models from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **The Problem Traditional Transaction Models** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **The Problem Traditional Transaction Models** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **The Problem Traditional Transaction Models** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes