Module 17: Transactions & Concurrency Control · Lesson 7 of 7
Lessons 01–06 gave you the mechanisms and the failure modes. This closing lesson gives you an actual decision process for designing a new transaction boundary from scratch, so the earlier material becomes something you apply deliberately rather than recall after the fact.
Without an explicit process, transaction design defaults to whatever the ORM or framework does automatically — which is sometimes fine and sometimes exactly how Lesson 06’s anti-patterns end up in production. A short, repeatable decision sequence makes the choice deliberate.
What is the smallest set of statements that must all succeed or all fail together, as one business fact? Not “everything this request touches” — the minimum. (Lesson 01, §4, §8)
Will another session plausibly read or write the same rows while this operation runs? If genuinely no (truly private data, no other writer exists), isolation/locking choices matter less. If yes, continue.
based on that read?
If yes, this is the Lesson 03 read-then-write shape. Decide: can it be
a single atomic statement instead (preferred), or does it genuinely
need a locking read (SELECT ... FOR UPDATE)?
Default to MySQL’s REPEATABLE READ unless you can name a specific
reason it’s insufficient (true cross-row consistency needs that
SERIALIZABLE specifically provides) or a specific reason a weaker
level is acceptable and beneficial (a high-throughput, staleness-
tolerant read path). “Default habit” is not a reason in either
direction — name the actual requirement. (Lesson 02, §6–7)
If this operation (or any operation that could run concurrently against the same resources) locks more than one row/table, confirm a consistent acquisition order is enforced across every code path that touches those same resources. (Lesson 04, §7)
Does the application layer catch and retry deadlock errors (Lesson 04)? Does it catch lock-wait-timeout errors (Lesson 03, §7) separately, with different handling (that one is not necessarily retryable the same way)? Decide this before shipping, not after the first production incident.
Run the finished design against Lesson 06, §4’s seven patterns
explicitly. Any external call inside the boundary? Any statement that
could run alone under autocommit when it needs a partner? Any
unbounded-scope UPDATE?
Scenario: “When an employee is promoted, update their salary and insert a promotion-history record, and this must never leave the two out of sync.”
UPDATE + the history INSERT.
Nothing else needs to be in this boundary.SET salary = <new
value>, not computed from a prior read of the current salary in
application code — so this is NOT a read-then-write race by itself.
If the new salary were computed as “current salary + 10%,” that
would reintroduce the Lesson 03 race and need a locking read or an
atomic salary = salary * 1.10.REPEATABLE READ (MySQL default) is sufficient — no
named requirement for SERIALIZABLE here.START TRANSACTION;
UPDATE employes SET salary = 98000.00 WHERE emp_id = 12;
-- (promotion_history table not in this handbook's 00_Schema;
-- shown here as the shape of the second statement this transaction protects)
-- INSERT INTO promotion_history (emp_id, new_salary, effective_date)
-- VALUES (12, 98000.00, CURRENT_DATE);
COMMIT;
The workflow itself is engine-agnostic; Step 4’s “default” differs by
engine (MySQL: REPEATABLE READ; PostgreSQL/SQL Server: READ
COMMITTED) — always state which engine’s default you’re reasoning
against, per Lesson 02, §8.
If a production incident traces back to a transaction design issue, re-running this workflow against the actual code (not the intended design) is usually the fastest way to identify exactly which step was skipped or answered incorrectly.
Revisit Step 4 (isolation level) and Step 1 (atomic unit scope, Lesson 06 §4.3) specifically as load grows — a design that was correct at low concurrency can need its atomic unit narrowed or its isolation level reconsidered purely due to contention, even with zero change in business logic correctness.
Not applicable as a new section — see §7 above; all dialect-specific detail was established in Lessons 01–06.
A repeatable seven-step workflow — atomic unit, concurrent access check, read-then-write check, isolation level choice, lock ordering check, failure-mode planning, anti-pattern audit — turns Lessons 01–06 from reference material into an applied design process. The worked promotion example shows each step resolving concretely against a realistic scenario.
(Some challenges are scaffolded in this lesson’s .sql file. The cross-lesson problem set, with full solutions, is in PRACTICE_PROBLEMS.md and SOLUTIONS.sql.)
Previous: Lesson 06 — Transaction Anti-Patterns · Back to: Module Home