Available-to-promise (ATP) is the number that answers “can I commit this order right now?” On a quiet day, almost any implementation works. On a flash-sale day, when hundreds of orders hit the same SKU in the same second, a naive ATP quietly oversells, and you spend the next week apologising and cancelling. The cause is almost always the same: the reservation logic races.
The race
The classic ATP check is read-then-write:
available = on_hand - reserved # read
if available >= qty: # decide
reserved = reserved + qty # write
Run that under concurrency and two requests both read reserved, both see enough stock, and both
write their reservation. The second write is based on a stale read, so you have promised more than
you have. This is the same lost-update bug that haunts any read-modify-write without isolation, and
it is exactly the order-management / inventory-management
handoff failing under load.
The patterns that fix it
1. Atomic conditional update. Make the check and the decrement one indivisible operation, so the database, not your application, enforces the invariant:
UPDATE stock
SET reserved = reserved + :qty
WHERE sku = :sku
AND on_hand - reserved >= :qty; -- only succeeds if still available
-- rows affected = 0 -> reject the order; = 1 -> reserved
No row is updated when stock is gone, so two racing requests cannot both succeed. This is the single highest-leverage fix and often the only one a mid-size operation needs.
2. Reservations with a TTL. Hold stock for the duration of checkout with an expiry, so abandoned carts release inventory automatically instead of locking it forever. The hold is itself an atomic conditional update against available stock.
3. Serialize per SKU. Route all reservation requests for one SKU through a single queue or a per-SKU lock. Correct, but it caps throughput on the hot SKU, which on a flash sale is the one SKU everyone wants, so use it when the atomic update is not available.
4. A deliberate oversell buffer. Some businesses intentionally allow a small oversell on high-velocity items, betting on cancellations and restocks. That is a business decision, not an accident, and it should be an explicit number, not a race condition you discovered in production.
The thing to measure
Watch your oversell rate during peak events, not on average. An ATP that reads correct on a Tuesday can still be losing you money every sale, because the failure only appears under the concurrency that sales create. And remember the invariant is only as good as the underlying stock accuracy: atomic logic on a wrong on-hand figure just promises the wrong number reliably.
Implementing this at your scale?
The walkthrough above comes from production work. AvanSaber’s inventory practice has implemented variations of this pattern across multiple customer engagements.
If you are building this and want expert review of your design, or would rather have the team that built this build yours, book a discovery conversation or describe your situation at [email protected].