Signals that you’re fighting contention
- A scarce unit, many takers: the last seat
- An explicit never: “must never oversell”
- A timed rush on one row: tatkal at 10 am
- A slow step in the middle: payment
Look, then write? Or decide inside the write?
Bad: SELECT, then UPDATE
Both buyers read “free” in the gap, and both are told “booked”.
Good: One conditional UPDATE
… WHERE status = 'free'. One row changed: yours. Zero: taken.
Start at rung 1. Climb only when a number forces you.
Most designs never leave rung 1 for the claim. Each rung up handles a bigger, slower or hotter decision, still decided atomically by one authority.
200/s
claims one hot row can take
Assume a claim holds the row lock 5 ms, so one row admits 200 a second. An illustrative tatkal rush brings 1,667; a minute later, 88,000 wait.
UPI payment can take minutes. No row lock should last that long.
The trick
Hold with held_until. The next claim treats an expired hold as free, so correctness never waits on a sweeper.
Most claims must fail. Fail them early.
A waiting room admits only what you can serve, a Redis counter turns away sold-out requests at once, and one allocator per item takes claims in order.
A Redis lock for correctness?
Bad: Lease lock alone
A paused client outlives its lease, then writes alongside the new holder.
Good: Fencing token
The resource rejects stale tokens. Or skip the lock: a conditional write.
Say it like this
Make the check and the claim one atomic step that one authority decides, a conditional write first. Hold slow claims with an expiry, and fail hopeless claims early.