Yueser ← Back to library

AI / Coding and Debugging

Concurrency and race condition review

Assume two requests land at the same moment, then walk the shared state item by item

Review the code below for concurrency safety. Method: assume two requests arrive in the same millisecond and walk every piece of shared state. Please: 1) list all shared mutable state (in-memory variables, caches, database rows, files); 2) point out where there is a gap between checking and using (read-then-write); 3) say at what concurrency it surfaces, and what it looks like when it does; 4) give the minimal fix (lock, unique constraint, atomic operation, make it idempotent) and the cost of each.

```
[paste your code]
```
How to use

Worth running even on a low-traffic project — a read-then-write hole usually only blows up once the traffic shows up.

Good for

  • There is check-then-write logic: stock decrement, coupon claim, idempotent order creation.
  • Single-node tests always pass, but duplicates start appearing after multi-instance deployment or a load test.

Not for

  • Single process, no shared state, pure functions: there is nothing to review.
  • You already know it is isolation level or lock waits: read the locks and transactions, not the whole file.

Example

Input
def claim_coupon(user_id, coupon_id):
    if db.exists("select 1 from claims where user_id=? and coupon_id=?", user_id, coupon_id):
        return "already"
    if db.count("select count(*) from claims where coupon_id=?", coupon_id) >= 100:
        return "sold_out"
    db.insert("claims", user_id=user_id, coupon_id=coupon_id)
    return "ok"
What you get
The shared mutable state is the claims table and the stock count; there are two check-then-act gaps — two concurrent requests from the same user both pass exists, and at 99 claims both requests pass count<100. It surfaces with two instances at roughly 10 QPS during a coupon rush, as duplicate claims and overselling. Minimal fix: a unique constraint on (user_id, coupon_id) as the backstop plus a single atomic `INSERT ... SELECT ... WHERE count < 100`, at the cost of retrying on conflict.

Common mistakes

  • Saying only 'check for concurrency issues' without the deployment shape (how many instances, Redis lock or not) gets single-node-safe code flagged as dangerous.
  • Taking 'add a lock' literally and adding an in-process lock, which does nothing across instances.

Try tweaking it like this

  • Add 'assume 3 instances and no shared cache' before it counts in-process variables as shared state.
  • Ask for 'the outline of a load test that reliably reproduces it (concurrency, request order)' to turn the risk into an experiment.

More in this topic