Extra legroom
$49
Extra legroom
$49
Why the second run breaks
Both scenarios run the identical two operations — check whether 14C is free, then claim it. The only difference is whether anything stops another request from slipping between those two steps.
In the protected run the system takes a lock, so check-and-claim behave as one indivisible step. B's request is evaluated against the record as it actually is, and is correctly refused.
In the racing run there is no lock. B reads the record at t₂, before A commits at t₃. B's answer was true when it was given and false by the time it was used — the classic time-of-check to time-of-use gap. Two confirmations are issued for one seat.
The fix is not "check again" — a second check has the same gap. It is to make the check
and the write atomic: a row lock, a conditional update that only succeeds while
the seat is still free (UPDATE … WHERE status = 'available'), or a uniqueness
constraint that makes the second booking impossible to store.
For the operating-system form of the same bug — a privileged binary racing a symlink swap
between stat() and open() — see
Race Conditions.