4.4.16 · D4Databases

Exercises — Transactions — ACID properties (each one in detail)

2,576 words12 min readBack to topic

L1 — Recognition

Exercise 1.1

Match each ACID letter to the failure it defends against.

Letter Defends against…
A ?
C ?
I ?
D ?

Options: (a) another transaction running at the same time; (b) a crash losing already-committed work; (c) a crash leaving a transaction half-done; (d) a transaction breaking a declared rule.

Recall Solution 1.1
  • Atomicity → (c) crash leaving a transaction half-done. All-or-nothing on disk.
  • Consistency → (d) a transaction breaking a declared rule (a CHECK, foreign key, invariant).
  • Isolation → (a) another transaction at the same time (concurrency).
  • Durability → (b) a crash losing committed work.

The one-line separators: A = my own crash, C = my own rules, I = other people, D = crash after I said "done".

Exercise 1.2

Which log does each of these use — the undo log (stores old values) or the redo log (stores new values)?

  1. Reversing an uncommitted transaction during rollback.
  2. Re-applying a committed change lost from RAM after a crash.
Recall Solution 1.2
  1. Undo log. To reverse a change, you must know the old value to put back. Rollback replays undo records backwards.
  2. Redo log. To re-apply a committed change, you need the new value. Recovery replays redo records forwards.

Memory hook: undo = old = Atomicity, redo = new = Durability. See Undo and Redo Logs.

Exercise 1.3

True or false: "Once I run an UPDATE statement, that row is durably on disk."

Recall Solution 1.3

False. Durability is only promised at COMMIT, and even then via the log first (WAL/fsync), not necessarily the data page. Before commit the write lives in a RAM buffer and can be lost. See Write-Ahead Logging (WAL).


L2 — Application

Exercise 2.1

Alice has bal = 500. A transfer of ₹100 to Bob runs but crashes after debiting Alice and before crediting Bob. The undo log holds one record: Alice.bal = 500.

After recovery, what is Alice.bal? What is Bob.bal (Bob started at 200)?

Recall Solution 2.1

The transaction never committed, so recovery treats it as failed and replays the undo log backwards:

  • Undo record Alice.bal = 500 is applied → Alice.bal = 500.
  • Bob was never touched → Bob.bal = 200.

Final: Alice.bal = 500, Bob.bal = 200. Total money = , exactly what we started with. Why? Atomicity conserves the system — partial work is erased.

Exercise 2.2

Same transfer, but it commits successfully. Before the crash, the redo log flushed Bob.bal: 200→300 and Alice.bal: 500→400, but the data pages were never written. The crash happens right after COMMIT returned "OK".

After recovery, what are the two balances?

Recall Solution 2.2

The transaction committed, so recovery replays the redo log forwards:

  • Alice.bal := 400
  • Bob.bal := 300

Final: Alice.bal = 400, Bob.bal = 300. Total = . Why? Durability guarantees committed work is never lost — the redo log is the durable proof, even though the data pages were stale.

Exercise 2.3

A constraint CHECK (bal >= 0) is declared. Alice (bal = 50) tries to send ₹100.

Step through what happens. What is Alice's balance afterwards?

Recall Solution 2.3
  1. Transaction tries Alice.bal := 50 - 100 = -50.
  2. This violates CHECK (bal >= 0).
  3. Consistency rejects it → the whole transaction rolls back (Atomicity does the undo).
  4. Alice.bal stays 50.

Final: Alice.bal = 50, transfer denied. Note how C detects the violation and A undoes the partial work — the two cooperate. See Database Constraints.


L3 — Analysis

Exercise 3.1

Two transactions run concurrently:

  • T1: sets Alice.bal := 0, then (later) rolls back.
  • T2: reads Alice.bal while T1 is still open, sees 0, and denies a withdrawal.

Which anomaly is this? What is the weakest isolation level that prevents it?

Recall Solution 3.1

This is a dirty read: T2 read T1's uncommitted value that was later rolled back — a value that "never really existed". T2 acted on a phantom of reality.

Weakest level that stops it: Read Committed — it only ever shows committed values. See Isolation Levels.

Exercise 3.2

Order the three read anomalies from the one prevented at the lowest isolation level to the highest, and name the exact level that first stops each.

Recall Solution 3.2
Anomaly First stopped at
Dirty read Read Committed
Non-repeatable read Repeatable Read
Phantom read Serializable

The ladder is cumulative: each higher level stops everything below it too. Look at the staircase in the figure — each step blocks one more anomaly.

Figure — Transactions — ACID properties (each one in detail)
  • Dirty read: you read uncommitted data.
  • Non-repeatable read: you re-read a row and its value changed (someone updated+committed between your reads).
  • Phantom read: you re-run a range query and new rows appear (someone inserted). See Concurrency Control.

Exercise 3.3

A transaction does the same SELECT COUNT(*) FROM acct WHERE bal > 100 twice. The first returns 5, the second returns 6 — a new matching row appeared in between.

Name the anomaly. At which isolation level does it disappear?

Recall Solution 3.3

A new row appearing in a range/predicate query is a phantom read (not a non-repeatable read — no existing row changed value; a new one entered the range).

It disappears at Serializable, the only level that prevents phantoms. See Locking and 2PL for how range/predicate locks (or MVCC snapshots) achieve this.


L4 — Synthesis

Exercise 4.1

You are building an inventory system. Design a transaction that decrements stock and inserts an order row, and state which ACID property guards each risk below:

  1. The process crashes after decrementing stock but before inserting the order.
  2. Stock could go negative.
  3. A second buyer reads stock between your read and your write.
  4. The server loses power one second after you told the customer "order placed".
Recall Solution 4.1
BEGIN;
  UPDATE stock SET qty = qty - 1 WHERE item = 'X' AND qty >= 1;
  INSERT INTO orders(item, buyer) VALUES ('X', 'Bob');
COMMIT;
  1. Crash mid-way → Atomicity: undo log rolls back the decrement; no orphan half-order.
  2. Negative stock → Consistency: a CHECK (qty >= 0) constraint (or the qty >= 1 guard) rejects the transaction.
  3. Concurrent buyer reading between read & write → Isolation: an appropriate level (Repeatable Read + locking, or Serializable) prevents the lost-update/dirty-read.
  4. Power loss after "order placed" → Durability: WAL flushed the redo log at commit; recovery replays it.

All four letters map cleanly onto the four risks — that is the whole point of ACID.

Exercise 4.2

A bank invariant states: total money across all accounts is constant. A transfer is atomic, consistent (no negative balances), and durable, yet a reporting query still sums the balances to ₹100 short. No data was lost. Which property was missing, and give the interleaving that causes it.

Recall Solution 4.2

The missing property is Isolation. Interleaving:

  1. Transfer: Alice.bal := 400 (debited), not yet committed.
  2. Report reads Alice = 400 and Bob = 200 → sum = 600.
  3. Transfer: Bob.bal := 300, commits.

The report saw a half-completed transaction: Alice's debit but not Bob's credit → 100 appears missing. Atomicity, Consistency, Durability were all fine — the transfer did finish and conserve money. The report simply observed an intermediate state, which only Isolation forbids. A serializable read would see either (500, 200) or (400, 300), both summing to 700.


L5 — Mastery

Exercise 5.1

An engineer sets every transaction in a high-traffic app to Serializable, reasoning "safest = best." Throughput collapses under load. Explain the trade-off precisely, and give a rule for choosing the level.

Recall Solution 5.1

Stronger isolation is bought with more locking and more aborts/retries. Serializable must block or abort transactions to forbid phantoms; under high concurrency this serialises work that could have run in parallel, causing lock waits, deadlocks, and retry storms → throughput falls.

The rule: pick the weakest isolation level that still preserves your invariants. Enumerate the anomalies your business logic actually cannot tolerate, find the lowest level that blocks exactly those, and stop there. Serializable is correct only when you genuinely need phantom-freedom. See Isolation Levels and MVCC (which gives snapshot reads without read locks, softening this trade-off).

Exercise 5.2

Explain why ACID-Consistency and CAP-Consistency are unrelated, then state which CAP guarantee a single-node ACID database trivially satisfies and why the CAP trade-off only bites when you replicate.

Recall Solution 5.2
  • ACID-C = the database obeys its declared integrity rules on a single logical state (CHECK, foreign keys, invariants). It is a property of one database.
  • CAP-C = all nodes return the same data at the same time — a replication agreement property across many nodes.

They share a word but measure different things: rule-keeping vs read-agreement.

A single-node ACID database has nothing to replicate, so CAP-C is trivially satisfied (there is only one copy — every read sees it). The CAP trade-off (choose Consistency or Availability under a partition) only appears once you have multiple replicas that a network partition can split. See CAP Theorem and Two-Phase Commit (which extends atomicity across nodes, a distributed-Atomicity problem, not distributed-Consistency).

Exercise 5.3

Reconstruct the durability proof chain: at the moment COMMIT returns "OK", state the exact ordering guarantee WAL enforces, and prove that a crash immediately after cannot lose the transaction.

Recall Solution 5.3

WAL's invariant is "log first, data later": before COMMIT returns success, the redo log record is flushed to durable storage (fsync completes). Only after that does the client hear "OK". The data pages may still be stale in RAM.

Proof a crash right after cannot lose the commit:

  1. "OK" was returned ⟹ the redo record is already on durable storage (by the invariant).
  2. Crash wipes RAM (possibly stale data pages) but not durable storage.
  3. On restart, recovery scans the redo log, finds the committed record, and replays it forward onto the data pages.
  4. ⟹ the change is reconstructed. The commit survives.

The linchpin is step 1: "OK" is never returned before the log is durable, so a durable "OK" always has a durable log record behind it. See Write-Ahead Logging (WAL) and the flow below.

Figure — Transactions — ACID properties (each one in detail)

Recall Feynman recap
  • Undo = old values = Atomicity = my own crash. Redo = new values = Durability = crash after "done".
  • Isolation anomalies climb a staircase: dirty (Read Committed) → non-repeatable (Repeatable Read) → phantom (Serializable).
  • Pick the weakest isolation that keeps your invariants — strength costs throughput.
  • ACID-C ≠ CAP-C; 2PC ≠ CAP-C. Same words, different worlds.

Quiz yourself:

Which log restores old values on rollback?
The undo log, replayed backwards.
Which log re-applies committed changes after a crash?
The redo log, replayed forwards.
A concurrent report sums balances ₹100 short with no data lost — which property failed?
Isolation.
The weakest isolation level that stops a dirty read
Read Committed.
The only isolation level that stops phantom reads
Serializable.