</> MAANG.io
system design Β· 101

Foundations

Master system design interviews with scalable architecture patterns, distributed systems, and real-world design challenges.

0/81 solved 0% complete

Part 1 β€” ACID Transactions: Foundations and Fundamentals

maang.io System Design Series


What is this?

Picture a bank teller moving $100 from your checking account to your savings. There are really two steps hiding in that single action: subtract $100 here, add $100 there. Now imagine the lights go out β€” a power cut β€” right in the gap between those two steps. The money has left checking but never arrived in savings. It has simply vanished. You'd be furious, the bank would be liable, and an auditor would have a very bad day.

A transaction is the database's promise that this can never happen. It wraps those two steps into one indivisible bundle and says: either both happen, or neither does β€” and once I tell you it's done, it stays done, even if the building catches fire one millisecond later. ACID is the four-part name for the exact guarantees that make that promise trustworthy: Atomicity, Consistency, Isolation, Durability.

The one-line idea: a transaction turns a messy pile of individual reads and writes into a single, all-or-nothing unit that moves the database from one valid state to another β€” and ACID is the four guarantees that make it safe to bet money on.

The properties were first formalized by Theo HΓ€rder and Andreas Reuter back in 1983, distilled from a decade of building systems where "the money disappeared" was an unacceptable answer. Let's build the intuition for each one, then see exactly which disasters they prevent.


1. Why transactions exist: the disasters they prevent

Before the four letters, understand the enemy. Transactions exist to defend against two classes of chaos: other people touching the same data at the same time, and the machine dying mid-write. Everything in ACID is a response to one of those two threats.

1.1 The concurrency demons

The instant two transactions touch the same row at the same time, weird things become possible. Three classics show up again and again β€” memorize them, because interviewers love to name-drop them.

The lost update is the most expensive. Two transactions read the same value, both modify their local copy, both write back β€” and the second write silently stomps the first:

Lost update: two transactions overwrite each other

The dirty read is reading someone's uncommitted scribble. Transaction B reads a value Transaction A wrote but hasn't committed β€” then A rolls back, and B is now holding data that never officially existed. Any decision B makes on it is built on a lie.

The inconsistent read is seeing a half-finished change. Transaction A reads X, then Y, but in between, Transaction B updated both and committed. A now holds the old X and the new Y β€” two values that were never true at the same instant. If X and Y are supposed to sum to a constant (two account balances), A's view violates the invariant.

πŸ’‘ Notice the pattern: every concurrency demon is about a transaction seeing the wrong slice of time. The fix β€” Isolation β€” is really about giving each transaction a clean, consistent view of the world.

1.2 The failure demons

The other threat is the machine itself failing at the worst possible moment. Different failures, different blast radii, different defenses:

Failure What breaks The ACID answer
Transaction error (constraint violation, app bug) A half-applied change Atomicity β€” roll the whole thing back
System crash (power loss, kernel panic) In-memory buffers vanish Durability via write-ahead logging (Part 2)
Media failure (disk corruption) Committed data on disk is gone Replication + recovery (Parts 3–4)
Network failure (distributed systems) One node commits, another doesn't Distributed consensus / 2PC (Part 3)

Keep this table in your head β€” it's the skeleton the whole topic hangs on, and we'll return to each row.


2. The four letters, one at a time

Here's the lifecycle every transaction follows. The four ACID properties are guarantees layered onto this skeleton β€” Atomicity owns the begin-to-commit-or-rollback arc, Isolation governs what other transactions see while it runs, Consistency is checked at the validation gate, and Durability kicks in the instant commit returns.

Transaction lifecycle from BEGIN to COMMIT or ROLLBACK

2.1 Atomicity β€” all or nothing

Atomicity says a transaction is indivisible: it either completes entirely or has zero effect. There is no "we got halfway." Our power-cut bank transfer is the canonical example β€” without atomicity, money evaporates in the gap.

The mechanism is the undo log: before changing anything, the database records the old value. If the transaction aborts β€” by a constraint violation, an app-level ROLLBACK, or a crash mid-flight β€” those old values are replayed to erase every trace.

Atomicity via write-ahead log and rollback

That logging machinery is the heart of Part 2 β€” for now, just hold the picture: the old values are always written down first, so any half-step is reversible.

2.2 Consistency β€” only valid states

Consistency says a transaction moves the database from one valid state to another, never leaving it in a state that breaks the rules. "The rules" are your constraints and invariants: foreign keys, unique constraints, check constraints, and business rules like account balance must never go negative or order total must equal the sum of its line items.

Consistency: only valid states survive

⚠️ A subtle point worth getting right in an interview: the database guarantees the consistency of constraints you declared β€” it can't invent your business rules. If you forget to add a CHECK (balance >= 0), the database will happily let a transaction drive the balance negative and still call it "consistent." Consistency is a contract you co-author with the engine, not magic it performs alone. (This is also the "C" that means something completely different from the "C" in CAP β€” a classic trap we'll defuse in Part 3.)

2.3 Isolation β€” concurrent transactions don't trip over each other

Isolation is the guarantee that fights the concurrency demons from Section 1.1. Ideally, every transaction runs as if it were the only one on the system β€” full isolation. But full isolation is expensive (it means serializing everything), so the SQL standard offers a dial: weaker levels run faster but permit specific anomalies.

Isolation level Prevents Still allows
Read Uncommitted nothing dirty, non-repeatable, phantom reads
Read Committed dirty reads non-repeatable reads, phantoms
Repeatable Read dirty + non-repeatable reads phantom reads
Serializable everything nothing

A quick bridge so the vocabulary lines up: the "inconsistent read" we met in Section 1.1 is the umbrella; the table splits it into its two precise sub-species. A non-repeatable read is the same row giving two different answers within one transaction; a phantom read is new rows appearing that match a query you already ran. Same family β€” "I saw the wrong slice of time" β€” just different shapes, and the dial stops each at a different setting.

The whole point is that you choose where to sit on this dial per transaction, trading correctness for throughput. We'll go deep on how each level is actually implemented β€” locks vs. MVCC β€” in Part 2, and walk through each anomaly with real SQL in Part 3.

2.4 Durability β€” committed means committed

Durability says: once the database tells you "committed," that change survives anything β€” power loss, crash, the server falling off a cliff. The thank-you confirmation you got is a binding promise.

The trick is that the change is written to a durable log on disk (forced all the way down, past the OS cache) before COMMIT returns. The actual data pages can be lazily flushed later; if a crash happens first, recovery replays the log to reconstruct every committed change.

Durability: commit, fsync, and crash recovery

πŸ’‘ This is why a database fast enough to lie about durability (acknowledging before the disk flush) can quietly betray you in a power cut. The honest fsync is the price of the promise β€” and tuning how you pay it (group commit, async commit) is a real performance lever we explore in Part 2.


3. Worked example: the money transfer, end to end

Let's make it concrete with the example we opened on. Transfer $100 from account A to account B, and log it:

BEGIN TRANSACTION;
  UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
  UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
  INSERT INTO transactions (from_acct, to_acct, amount) VALUES ('A', 'B', 100);
COMMIT;

Watch all four letters work at once:

  • Atomicity β€” if the credit to B fails (or the power dies between the two UPDATEs), the debit from A is undone. No money vanishes.
  • Consistency β€” a CHECK (balance >= 0) constraint means that if A only had $50, the debit is rejected and the whole thing rolls back. The invariant holds.
  • Isolation β€” if another transfer touches A at the same moment, isolation stops the two from producing a lost update; one waits or retries.
  • Durability β€” the moment COMMIT returns, the transfer survives a crash. The customer's confirmation is real.

Now scale that idea up to an e-commerce checkout, where one logical "order" spans several tables β€” and the same all-or-nothing wrapper protects the whole thing:

Order placement as a single atomic transaction

If the inventory update would push stock below zero, or the payment insert violates a constraint, everything rolls back β€” no orphaned order, no double-sold item, no phantom charge. That's the entire value proposition of ACID in one picture: complex multi-step business operations behave like a single safe click.

A quick practical note: the spelling of BEGIN varies by engine β€” PostgreSQL and MySQL accept START TRANSACTION, SQL Server wants BEGIN TRANSACTION, SQLite just BEGIN β€” but the semantics are identical everywhere. Don't let the syntax distract you from the guarantee underneath.


4. Key takeaways

  1. A transaction bundles many reads and writes into one all-or-nothing unit that moves the database from one valid state to another β€” the abstraction that lets you treat a multi-step money transfer as a single safe operation.
  2. ACID exists to defeat two enemies: concurrency demons (lost updates, dirty reads, inconsistent reads) and failure demons (crashes, media and network failures). Every letter answers one of them.
  3. Atomicity = all-or-nothing (via undo logs); Isolation = a clean per-transaction view of the world (via a tunable dial of levels); Durability = committed survives any crash (via forced log writes).
  4. Consistency is a contract you co-author β€” the engine enforces only the constraints you actually declare, so forgotten invariants are forgotten guarantees.
  5. The four properties aren't separate features bolted on; they're four guarantees layered onto one transaction lifecycle, and they're cheapest to reason about together.

Part 2 cracks open the engine: write-ahead logging, the ARIES recovery algorithm, lock management, and MVCC β€” the actual machinery that turns these four promises into running code.

Sign in to MAANG.io

Use your Google or Microsoft account β€” no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.