Home/Labs/Inventory Oversell Race
All 280 Labs
INTERACTIVE LAB📦

Inventory Oversell Race Lab (Interactive)

Race buyers on guarded vs naive stock updates; idempotency keys stop double charges. Contrast a WHERE stock >= 1 guarded decrement against a read-then-write race that oversells, with idempotency keys absorbing retries.

Black Friday Checkout: Oversell Races & Idempotent Settlement

Fire a flash-sale wave at the last 10 units and compare SELECT-then-UPDATE against an atomic conditional decrement.

Units sold this wave10
Oversold (fulfillment nightmare)0
Cleanly rejected (409/410)790
Double-charges (160 retries)0
WAVE LOG:

Run a wave. Flip to SELECT→UPDATE and widen the same-window readers — every reader sees stock>0 before any decrement lands.

The single defense against overselling is letting the database decide: UPDATE inventory SET stock = stock - 1 WHERE sku = ? AND stock >= 1 — affected_rows=0 means the buyer lost the race cleanly, no lock convoy and no negative stock. Downstream, the Saga orchestrator (payment → warehouse → notification) compensates with a refund + restock if any step fails, and every money-moving POST carries an Idempotency-Key so the 20% of clients who retry on timeout are charged exactly once.

How It Works Under the Hood

Checkout correctness hinges on two races. The first is inventory: decrementing after a read — SELECT stock then UPDATE — lets concurrent buyers all see stock equals one and all succeed, overselling. A guarded UPDATE SET stock = stock-1 WHERE stock >= 1 makes the database enforce the floor, so attempts beyond stock return zero affected rows and fail cleanly. The second is retries: flaky clients resend the charge and double-bill unless every request carries an Idempotency-Key the server dedups. Turn on roughly 20% retries with and without idempotency to watch duplicate charges, and compare guarded updates to the racy path to see phantom units sold.

Core Architectural Principles

  • Naive read-then-write oversells: sold = min(buyers, stock + concurrent_readers - 1).
  • A guarded UPDATE WHERE stock >= 1 returns 0 affected rows when stock is exhausted.
  • Without an Idempotency-Key, retried requests double-charge and double-decrement stock.
Interview Round Script

Name both races explicitly: oversell, fixed by an atomic guarded update or DB-level constraint, and duplicate submission, fixed by idempotency keys scoped to the payment intent. Explain reserve-then-confirm two-phase inventory to avoid holding stock indefinitely and the role of a payment-gateway idempotency layer. Quantify oversell under a concurrent window to show you grasp the check-then-act bug.

Key Trade-Offs

Strong guarded updates prevent oversell but serialize on the hot row; optimistic reservation scales better yet needs reconciliation.

Related Curriculum Chapter

Design an E-Commerce Checkout & Inventory System

Read Full Chapter Blueprint

Explore More Interactive Labs

View All 280 Labs