Why this matters

The bug. Under READ COMMITTED (the Postgres default), the SELECT sees the row as it was at statement start. Two concurrent transactions both read quantity = 1, both see their own order is <= 1, both UPDATE — final quantity is -1 and you've sold the last item twice.

The fix. SELECT … FOR UPDATE acquires a row-level lock; the second transaction blocks until the first commits, then sees the *new* quantity and correctly rejects. If you can move the check into SQL — UPDATE … WHERE quantity >= $2 RETURNING … — the database does the atomic compare-and-set in one statement and the puzzle goes away.

Adjacent pattern. Same shape applies to ticket reservations, balance debits, slot bookings. Whenever the app reads a value to decide whether to write, the read must be a write-intent read.

Review heuristic

Every closure that captures a value from a render or a setup phase needs an honest answer to the question: is this value allowed to change between when I closed over it and when I run? If yes, the closure is wrong; switch to a ref, a functional updater, or a fresh read.

Classic TOCTOU; Jepsen 'inventory' pattern.