Preventing Oversells in Flash Sales with Redis Atomic Reservations
How to guarantee you never sell the 101st unit of a 100-unit flash deal, using Redis atomic operations and TTL-based reservations instead of database locks.
Flash sales break naive inventory systems. When 5,000 people hit 'buy' on 100 units in the same second, a read-then-write against your database will happily let hundreds of them through. The fix is to make the reservation a single atomic operation.
Why the database check fails
The classic bug is a race condition: two requests both read stock = 1, both decide they can proceed, and both decrement. Row locks can help but they serialize your hottest path and fall over under real load. Redis does this better because a single-threaded in-memory decrement is atomic by nature.
// Atomic reserve: DECR only succeeds if stock remains >= 0
const remaining = await redis.decr(`stock:${productId}`);
if (remaining < 0) {
await redis.incr(`stock:${productId}`); // give it back
throw new Error("Sold out");
}
// Hold the unit for 10 minutes; auto-releases if unpaid
await redis.set(`hold:${productId}:${userId}`, "1", "EX", 600);Auto-releasing abandoned carts
A reservation that never expires leaks inventory. Set a TTL on each hold and subscribe to Redis keyspace notifications, when a hold key expires, an event fires and you return the unit to the pool. No cron sweep, no stale locks, no manual cleanup.
- DECR/INCR give you overselling-proof reservations with no locks.
- TTL + keyspace notifications release abandoned holds automatically.
- Persist the confirmed order to your primary database once payment clears.
I built exactly this pattern into a flash-sale reservation API, Express, Redis, MongoDB, all containerized. It handles the concurrency that would sink a database-only approach.
Want this built into your product?
I take on freelance work across AI, voice, backend and full-stack. Tell me what you're building.
Start a project