Challenge

Pick a level for six workloads

25 minintermediate115 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 Starter

Hints

  1. 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.

  2. 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.

  3. 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.

  4. 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?