Design a flash sale backend
Design the backend for a high-concurrency limited-availability sale event, covering inventory locking, queue-based fairness, and surviving 100x normal traffic.
The Problem Statement
Interviewer: "Let's say you are designing the backend for a flash sale. Maybe it is 10,000 concert tickets that go on sale at noon, or 6 million free burgers from a fast-food chain's promotion. Hundreds of thousands of users hit the buy button at the exact same second. How do you prevent overselling, keep things fair, and make sure the system does not just fall over?"
This question tests four things at once. First, can you absorb a traffic spike that is 50-100x your normal load? Second, can you prevent overselling when thousands of concurrent requests try to claim the same inventory? Third, do you understand fairness under contention (first-come-first-served is harder than it sounds at scale)? And fourth, can you design a payment flow that handles timeouts and failures without leaking inventory?
The design spans the full stack: CDN-level traffic absorption at the front, atomic inventory operations in the middle, and payment-timeout cleanup at the back. A solution that covers only one layer leaves the other bottleneck exposed.
Estimating the Scale
Before choosing components, estimate the demand and the inventory. Those numbers shape every decision.
Traffic profile:
- 500,000 concurrent users on the sale page at T-0 (the moment the sale opens)
- Each user refreshes or clicks "Buy" within the first 5 seconds
- Peak request rate: 500K / 5s = ~100,000 requests per second
- Normal platform traffic: 2,000 req/s. This is a 50x spike.
Inventory:
- 10,000 concert tickets (or 6 million burger vouchers, but let's use the harder case)
- 10,000 items means 99.98% of users will not get one
- Every single claim must be atomic. Selling ticket 10,001 is unacceptable.
Payload sizes:
- Sale page HTML/JS: served from CDN, ~200KB, cached. Zero backend load.
- "Buy" request: ~200 bytes (user ID, item ID, auth token)
- Response: ~100 bytes (success/sold-out/queued)
Latency budget:
- User clicks Buy to receiving confirmation: under 3 seconds
- Inventory check + reservation: under 50ms (must be in-memory)
- Payment processing: 2-10 seconds (external payment provider)
- Total with queue wait: up to 30 seconds for late arrivals
Infrastructure math:
- 100K req/s at the API layer. A single app server handles ~2K req/s, so 50 servers minimum.
- Redis single shard handles 100K+ ops/s. One shard can handle the inventory counter.
- The bottleneck is not compute. It is the thundering herd hitting one inventory counter simultaneously.
The critical insight from the math: 10,000 items and 500,000 users means you should reject 98% of requests as early as possible. Every request that reaches the inventory layer but cannot be fulfilled is wasted work. The architecture is really about building progressively narrower funnels.
30-second design summary
Use three layers, each narrowing the traffic funnel:
- Virtual waiting room and traffic shaping: Before the sale opens, absorb all users into a queued waiting room. When the sale starts, release users in controlled batches so the backend never sees 100K req/s at once.
- Atomic inventory management: Use Redis with atomic decrement operations to guarantee exactly 10,000 tickets are sold. No race conditions, no overselling.
- Payment flow with reservation timeout: When a user claims a ticket, reserve it for 5 minutes while they complete payment. If payment fails or times out, release the ticket back to the pool.
The design principle is to push rejection as far upstream as possible. The CDN serves the static page. The waiting room admits users at a controlled rate. The inventory layer sees only users who have a real chance of getting a ticket.
The Architecture
Five-minute end-to-end flow
Here is how the full flow works:
- Before the sale: All 500K users load the sale page from CDN. Zero backend load. The page shows a countdown timer.
- At T-0: Users click "Buy." The request hits the WAF, which blocks bots and rate-limits per IP. Valid requests enter the virtual waiting room.
- Waiting room releases batches: Instead of 100K users slamming the backend at once, the queue releases 2,000 users every second. The backend sees a steady, manageable stream.
- Inventory check: Each released user hits the Inventory Service, which runs a Redis DECR. If the counter is > 0, the ticket is reserved. If 0, the user gets "Sold Out" immediately.
- Payment window: Reserved users have 5 minutes to complete payment. If they fail or time out, a background job releases the reservation back to the pool.
- Confirmation: Successful payments write to PostgreSQL and emit a Kafka event. The user sees their ticket.
Virtual Waiting Room and Traffic Shaping
This is the most important component in the entire design. Without it, you are trying to serve 100K req/s on infrastructure sized for 2K req/s. The waiting room converts a spike into a steady stream.
The waiting room assigns each user a position based on their arrival timestamp. A background process advances a "release pointer" at a controlled rate (2,000 users per second). When the pointer passes your position, you get a time-limited token to proceed to the actual purchase.
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with SDEpedia Premium.