Mesrai
Back to blog
// essayTechnical Deep Dive

The Inventory Race Condition Mesrai Caught Before Black Friday

Real PR catch: read-then-decrement on inventory — TOCTOU oversell under concurrent load. Atomic conditional update fix.

Mesrai TeamJuly 26, 20268 min read

Inventory reservation logic. Read stock, check positive, decrement. Read-modify-write. Under any concurrent load this oversells — two requests can both pass the check before either writes. Mesrai caught it the morning before a Black Friday code freeze.

The vulnerable diff

tscheckout/reserve.ts
// checkout/reserve.ts
async function reserveItem(id: string) {
  // BUG: classic TOCTOU — gap between read and write
  const item = await db.items.findUnique({ where: { id } });
  if (!item || item.stock <= 0) throw new Error("out of stock");
  await db.items.update({
    where: { id },
    data:  { stock: item.stock - 1 },
  });
}

What is wrong

TOCTOU (time-of-check to time-of-use) is the canonical concurrency bug: condition checked, then acted on, with no atomic guarantee between the two. Under load, multiple workers read the same stock value, all pass the check, all write the decrement. Two requests both see stock=1, both write stock=0 — two customers charged for one item. The fix is to make the check-and-write a single atomic database operation: `UPDATE items SET stock = stock - 1 WHERE id = ? AND stock > 0`. The database guarantees one and only one row update.

The attack

Load test reproduces the bug in seconds:

bashexploit
# 50 concurrent reservation attempts on a single-stock item
seq 1 50 | xargs -P 50 -I{} curl -s -X POST \
  https://api.example.com/checkout/reserve \
  -d '{"itemId":"sku-123"}'
# Result: stock = -49. Forty-nine angry customers.

Production replays the same race naturally — checkout traffic concentrates on popular SKUs, dozens of parallel workers, no atomic guard.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 90 sec ago

[mesrai] [code-review] [Logic] [TOCTOU] [critical]

This reserve flow has a TOCTOU race. Concurrent requests both pass
the `stock > 0` check before either writes the decrement, so stock
can go negative under load.

Fix: collapse the check and decrement into a single conditional
update — the database guarantees atomicity per row:

  const { count } = await db.items.updateMany({
    where: { id, stock: { gt: 0 } },
    data:  { stock: { decrement: 1 } },
  });
  if (count === 0) throw new Error("out of stock");

For multi-row reservations (cart), use a transaction with
SELECT ... FOR UPDATE or use a queue (one consumer per SKU).

The fix

tscheckout/reserve.ts (fixed)
// checkout/reserve.ts — fixed
async function reserveItem(id: string) {
  const { count } = await db.items.updateMany({
    where: { id, stock: { gt: 0 } },
    data:  { stock: { decrement: 1 } },
  });
  if (count === 0) throw new Error("out of stock");
}

The `WHERE stock > 0` predicate makes the check part of the UPDATE statement itself. The database row lock during UPDATE serializes concurrent attempts — only the first to acquire the lock and pass the predicate succeeds. Zero `count` means another request already drained the stock. No oversell possible.

Why human review missed it

Read-modify-write in the application looks correct because the read and write each look correct in isolation. The bug lives in the gap — invisible without a concurrency mental model. Mesrai catches the pattern by flagging every `findUnique` → conditional → `update` sequence on the same primary key, where the field being conditioned on is also being written.

Related rules + further reading

Mesrai rule pack: logic/no-toctou-on-counter — flags read-then-write on the same row field without atomic update.

Postgres docs: Concurrency Control — Explicit Locking.

Classic distributed systems reading: Bernstein, Hadzilacos, Goodman — Concurrency Control and Recovery.

Takeaway

Read-modify-write on inventory is oversell waiting for a Black Friday. Conditional UPDATE makes the check atomic. One line.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.