How to Design a Flash Sale System
Learn how to design a flash sale system that prevents overselling with atomic inventory checks, waiting rooms, and pre-scaling.
Expected Interview Answer
A flash sale system survives a massive, near-instant demand spike on a tiny stock count by throttling traffic before it hits the database, reserving inventory atomically with a decrement-and-check operation, queueing overflow demand fairly, and pre-scaling stateless services ahead of the sale start time.
The core risk is thousands of requests trying to decrement the same inventory counter at the same instant, so naive read-then-write logic causes overselling; the fix is an atomic decrement (a Redis DECR with a floor check, or a conditional SQL UPDATE WHERE stock > 0) that either succeeds and reserves a unit or fails cleanly. In front of that, a rate limiter and a virtual waiting room absorb the initial burst so only a trickle of requests actually reach the inventory-decrement path, and a CDN or edge cache serves the largely static product page so the origin is not overwhelmed by browsing traffic. Reserved-but-unpaid units get a short hold (e.g., 5-10 minutes) with an async job releasing them back to stock if checkout is not completed, and the whole path is pre-warmed β connection pools, caches, and autoscaled instances β before the sale opens since reactive autoscaling is too slow for a spike that arrives in seconds. Idempotency keys on the purchase request prevent double-charging from client retries during the chaos.
- Atomic inventory decrement prevents overselling under extreme concurrency
- A waiting room and rate limiter protect the database from a synchronized traffic spike
- Pre-warmed, pre-scaled infrastructure avoids being caught by autoscaling lag
- Timed holds with release jobs recover inventory from abandoned reservations
AI Mentor Explanation
A flash sale system is like a stadium releasing a tiny batch of last-minute match tickets online at a fixed time, where thousands refresh the page in the same second. A virtual queue lets fans in a few at a time instead of everyone hitting the ticketing server simultaneously, the way a waiting room throttles flash-sale traffic. The actual ticket count only decrements one at a time through a single verified booth, never two clerks selling the same seat, mirroring an atomic inventory decrement. Reserved tickets not paid for within minutes are released back to the pool for the next fan in line, just like a flash saleβs timed hold and release.
Step-by-Step Explanation
Step 1
Absorb the burst before origin
Use a CDN for static product pages and a virtual waiting room / rate limiter so only a trickle of requests reach backend services.
Step 2
Reserve inventory atomically
Use an atomic decrement-and-check (Redis DECR with a floor, or conditional SQL UPDATE) so concurrent requests cannot oversell the same unit.
Step 3
Hold and release
Give a reserved-but-unpaid unit a short timed hold; an async job releases it back to stock if checkout is not completed in time.
Step 4
Pre-scale ahead of the spike
Pre-warm caches, connection pools, and autoscaled capacity before the sale opens, since reactive autoscaling is too slow for a second-scale spike.
What Interviewer Expects
- Identifies overselling as the central risk and proposes an atomic decrement/check
- Mentions a waiting room or rate limiter to shed load before it hits the database
- Covers timed holds with a release job for abandoned reservations
- Emphasizes pre-scaling/pre-warming ahead of the sale rather than reactive autoscaling
Common Mistakes
- Using read-then-write inventory checks that race under concurrency and oversell
- Relying only on autoscaling to react after the spike has already started
- Forgetting to release held-but-unpaid inventory back to stock
- Not protecting the database from the initial synchronized traffic burst
Best Answer (HR Friendly)
βA flash sale system has to handle a huge crowd of people trying to buy a very limited number of items at the exact same second. The trick is to hold most people in a virtual line so the backend is not overwhelmed, only ever let one person claim each unit through a safe atomic check, give unpaid reservations a time limit so items go back on sale if someone abandons checkout, and get all the servers warmed up and scaled before the sale even starts.β
Code Example
def reserve_unit(product_id, user_id, hold_seconds=300):
stock_key = f"stock:{product_id}"
hold_key = f"hold:{product_id}:{user_id}"
# Atomic decrement-and-check via Lua script (single round trip, no race)
remaining = redis.eval(
"""
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock and stock > 0 then
redis.call('DECR', KEYS[1])
return stock - 1
else
return -1
end
""",
keys=[stock_key],
)
if remaining < 0:
raise SoldOutError(product_id)
redis.set(hold_key, 1, ex=hold_seconds) # timed hold
return {"product_id": product_id, "held_until_seconds": hold_seconds}
def release_expired_holds():
# Background worker scans expired hold keys (via keyspace notifications
# or a sorted set of expiry timestamps) and increments stock back.
for expired in scan_expired_holds():
redis.incr(f"stock:{expired.product_id}")Follow-up Questions
- How would you design the virtual waiting room so it is fair (first-come, first-served)?
- What happens if the atomic decrement operation itself becomes a bottleneck at extreme scale?
- How would you prevent bots from grabbing all the inventory in the first millisecond?
- How do you make the purchase request idempotent if a client retries after a timeout?
MCQ Practice
1. Why does a naive read-then-write inventory check fail under flash sale concurrency?
Read-then-write is not atomic, so many requests can read stock > 0 simultaneously and all proceed to purchase, oversubscribing inventory.
2. What is the purpose of a virtual waiting room in a flash sale system?
A waiting room admits users gradually, protecting the origin and database from a synchronized spike of simultaneous requests.
3. Why pre-scale infrastructure ahead of a scheduled flash sale instead of relying on autoscaling?
Reactive autoscaling usually takes longer than the near-instant traffic surge of a flash sale, so capacity must be provisioned ahead of time.
Flash Cards
Main risk in a flash sale system? β Overselling due to concurrent, non-atomic inventory checks.
How to prevent overselling? β Use an atomic decrement-and-check operation (e.g., Redis Lua script or conditional SQL UPDATE).
Why a virtual waiting room? β To throttle the traffic burst so the database and backend are not overwhelmed at once.
Why pre-scale before the sale? β Because the spike arrives in seconds, too fast for reactive autoscaling to keep up.