ExerciseChallenge
Challenge
Pick a level for six workloads
25 minintermediate1–15 yrs
Edge cases. You have to reason, and two valid fixes differ.
What this teaches
- The isolation level is the right tool for read consistency and the wrong one for read-modify-write
- Two of the six need a lock rather than a level, and saying so is the answer
- Write skew survives repeatable read, which is the case serializable exists for
- Raising a level globally changes behaviour for code written under a different assumption
Starter
Starter.java
import java.util.*;
/**
* Challenge: six real workloads. Choose an isolation level for each, or argue
* that the level is not the right tool.
*
* Two of the six are answered with a lock rather than a level. One is wrong at
* every level including repeatable read. Getting those three right is the
* exercise; the other three are there so the answers are not all the same.
*/
public class Starter {
// ── 1 ─────────────────────────────────────────────────────────────────
// A nightly reconciliation report. Reads eleven tables, takes 40 seconds,
// writes nothing. Totals must agree with the rows they summarise.
// ── 2 ─────────────────────────────────────────────────────────────────
// Applying a promotional credit:
// balance = SELECT balance FROM account WHERE id = ?
// UPDATE account SET balance = <balance + 500> WHERE id = ?
// Two agents can run this for the same customer at the same time.
// ── 3 ─────────────────────────────────────────────────────────────────
// An on-call roster. Going off duty is allowed only if at least one other
// engineer is still on duty:
// SELECT count(*) FROM oncall WHERE on_duty = true
// -- if count >= 2 then
// UPDATE oncall SET on_duty = false WHERE engineer = ?
// Two engineers click at the same instant.
// ── 4 ─────────────────────────────────────────────────────────────────
// Recording a page view: UPDATE page SET views = views + 1 WHERE id = ?
// Thousands per second, exactness not critical.
// ── 5 ─────────────────────────────────────────────────────────────────
// Issuing the next invoice number:
// SELECT max(number) FROM invoice
// INSERT INTO invoice (number, ...) VALUES (<max + 1>, ...)
// Numbers must be unique and gapless for the tax authority.
// ── 6 ─────────────────────────────────────────────────────────────────
// A customer-facing "your recent orders" screen. One query, fast, and a
// slightly stale result is fine.
public static void main(String[] args) {
// TODO 1: for each workload, name the anomaly that would bite at read
// committed. If you cannot name one, that is your answer — read
// committed is fine and you should say so rather than reaching higher.
// TODO 2: choose a level for each. Write one sentence of justification
// that names the anomaly, not the vibe.
// TODO 3: two of the six are read-modify-write. Find them. For each,
// give the three fixes in order of preference — single statement,
// pessimistic lock, optimistic version — and say which you would
// actually use here and why.
// TODO 4: one workload is broken at read committed AND at repeatable
// read, even on PostgreSQL, because the two transactions read rows the
// other is about to write and neither sees the other's write. Name the
// anomaly, and say why a row lock on what you read does not save you.
// TODO 5: for workload 5, does serializable actually give you gapless
// numbering? Consider what happens when a transaction aborts after
// taking number 41. Propose something better than an isolation level.
// TODO 6: which of your six answers change if the database is MySQL
// InnoDB rather than PostgreSQL? There are at least two.
// TODO 7: workload 1 wants a 40-second snapshot. On PostgreSQL, what
// does holding that snapshot open cost the tables it reads? Say
// whether you would still do it.
// TODO 8: someone proposes setting the whole application to
// serializable so none of this needs thinking about. Write the two
// sentences you would say in that review.
}
}Run it locally:
cd exercises/java/transactions/isolation-levels/02-challenge
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterHints
Hint 1
For each one, ask what would go wrong at read committed and name the anomaly. If you cannot name one, the level is probably not the problem.
Hint 2
Two of the six are read-modify-write. Look for a read whose value is used to compute the write, and reach for a lock rather than a level.
Hint 3
One is correct at every level and still wrong. Both transactions read rows the other is about to change, and neither reads the other's write.
Hint 4
For the report, ask what a snapshot costs while it is held open — the answer is different on PostgreSQL than you might expect.
Done when
- Each workload has a level, and a named anomaly justifying it
- The two read-modify-write cases are answered with a lock or a single statement, not a level
- The write skew case is identified and serializable is justified specifically
- You said which workloads would behave differently on MySQL than on PostgreSQL
- You argued against at least one plausible-sounding choice
← Back to What are the isolation levels, and what does each permit?