There is one cookie left on the plate. You and your sibling both see it. You both
reach at the same time. Who gets it? Nobody planned for "both hands at once," so
the result is a mess (and maybe a broken plate).
Computers do this too, and we call it a race condition.
The idea in one line
A race condition is a bug that shows up when two things run at the same time
and the result depends on who happens to get there first.
The metaphor: the last cookie
Two people, one cookie. The rule was "first to grab it gets it." But if they
grab at the exact same moment, the rule breaks. Sometimes one wins, sometimes
the other, sometimes the cookie ends up on the floor. Same start, different
endings. That randomness is the smell of a race condition.
Time ->
You: see cookie ----------- grab!
Sibling: see cookie -- grab!
^ both saw "1 cookie", both grabbed. now what?
How it looks in code
Classic example: two requests buying the last item in stock.
// Both requests run this at almost the same time
const item = await db.getItem(7) // both read: stock = 1
if (item.stock > 0) { // both see 1 > 0, both say "yes!"
await db.setStock(7, item.stock - 1) // both write stock = 0
await ship(item) // we just shipped TWO. we had ONE.
}
Both requests read stock = 1 before either one updated it. So both think
they're fine. You oversell. The bug only appears when the timing lines up, which
is why it's so sneaky: it passes every test on your laptop, then explodes on busy
days.
The fix: don't let them both reach at once
You make the "check and update" happen as one locked step, so the second
request has to wait its turn.
-- Let the database do the check-and-subtract atomically, one at a time
UPDATE products
SET stock = stock - 1
WHERE id = 7 AND stock > 0; -- only succeeds if stock is still > 0
-- if 0 rows changed, you know you were too late. don't ship.
Now the database hands out the cookie to exactly one winner. The loser is told
"sorry, sold out," instead of both walking away thinking they won.
A real case
Race conditions love:
- Selling the last ticket or item.
- Two clicks creating two accounts with the same email.
- A counter (likes, views) losing updates when many people act at once.
The pattern is always the same: read, decide, write, with someone sneaking in
between your read and your write.
Gotchas juniors hit
1. "It works on my machine."
Of course it does. You're the only user. Races need traffic. Test with the real
world in mind.
2. Adding a sleep to "fix" it.
Slowing things down just hides the bug for a while. The race is still there.
3. Trusting a read you made a moment ago.
By the time you write, the value may have changed. Let the database check the
condition at write time.
Recap
- A race condition = the answer depends on who gets there first.
- It hides during quiet times and bites under load.
- The shape is always read, decide, write with a gap in the middle.
- Fix it by making the check-and-change one locked, atomic step.
Your turn
Find a "read, check, then write" in your code (like "if username is free, create
it"). Imagine two users doing it at the exact same millisecond. Could both win?
If yes, you found a race. Tell a friend about the last cookie.
Top comments (0)