Low-Level Design: Flash Sale System — Inventory Lock, Queue-based Checkout, and Oversell Prevention

The Core Challenge

A flash sale sells a limited quantity (e.g., 1000 units) at a discounted price for a short window (15-60 minutes). The challenge: at sale start, thousands of concurrent buyers attempt to purchase simultaneously. The system must: sell exactly the available quantity (no oversell, no undersell), handle the burst without data corruption, and provide a fair user experience.

Core Entities

FlashSale: sale_id, product_id, quantity_available, quantity_sold, price_cents, start_time, end_time, status (SCHEDULED, ACTIVE, ENDED, SOLD_OUT). SaleOrder: order_id, sale_id, user_id, quantity, status (RESERVED, PAID, CANCELLED, EXPIRED), reserved_at, expires_at. UserSaleEligibility: (user_id, sale_id) — one purchase per user per sale.

Inventory Reservation with Redis

At sale start, load available quantity into Redis: SET sale:{id}:stock 1000. For each purchase attempt: DECRBY sale:{id}:stock 1. If the result >= 0, the reservation succeeded. If the result is negative, INCRBY back (compensate) and reject with “Sold Out.” This Redis DECRBY is atomic — no race conditions. At very high concurrency (100K requests/second), Redis handles this easily (single-threaded, 1M+ ops/second).

Dual write: after Redis DECRBY succeeds, write a RESERVED SaleOrder to the database. Use a checkout TTL (10 minutes): if the user does not complete payment, a background job cancels the order and INCRBY the Redis stock counter. This returns the slot to the pool for other buyers.

Queue-based Checkout for Fairness

When demand far exceeds supply (10K buyers, 1K units), queue buyers at sale start instead of letting all hit Redis simultaneously. At T-0: accept buyers into a virtual queue (Redis LIST: RPUSH sale:{id}:queue {user_id}). A worker pops from the queue (LPOP) and processes purchases in order. Buyers are shown their queue position. Top N buyers (N = available quantity) get purchase slots; the rest are notified “sold out.” This is fairer than pure first-come-first-served race conditions where network proximity to the server determines success.

Preventing Oversell

Three layers of protection:

Layer 1 (Redis): atomic DECRBY gate — rejects requests when stock reaches 0. Fast, in-memory, first line of defense.

Layer 2 (Database): after inserting the RESERVED order, verify: UPDATE flash_sales SET quantity_sold = quantity_sold + 1 WHERE sale_id = X AND quantity_sold < quantity_available. If 0 rows updated, the database count confirms no stock — rollback the order and restore Redis counter.

Layer 3 (Audit): background reconciliation job runs every 5 minutes during the sale. Counts RESERVED+PAID orders and compares to quantity_sold. Alerts if discrepancy exceeds 0 (should never happen if layers 1 and 2 are correct).

Per-User Purchase Limits

One purchase per user per sale. Check: INSERT INTO user_sale_eligibility (user_id, sale_id) ON CONFLICT DO NOTHING RETURNING *. If no row returned, user already purchased — reject. The unique constraint (user_id, sale_id) enforces this atomically across concurrent requests. For quantity limits (max 2 per user): store quantity in user_sale_eligibility and use a SELECT FOR UPDATE check before allowing additional units.

Pre-sale Warm-up

Before the sale starts: pre-populate Redis cache with product details, stock counter, and user eligibility blacklists (accounts flagged for abuse). Pre-warm CDN for product page assets. Send countdown emails 1 hour and 5 minutes before sale. At T-5 minutes: enable the waitlist page (to capture user intent without hitting the DB). Scale up application servers and Redis cluster to handle the burst. Alert the on-call team if auto-scaling takes more than 2 minutes before the sale starts.

Post-sale Processing

After all units are reserved: transition sale status to SOLD_OUT. Process payments asynchronously: for each RESERVED order, charge the user via the payment gateway. On payment success: PAID. On failure (card declined, timeout): CANCELLED, restore Redis stock, offer to the next buyer in queue (if any). After the TTL window (10 minutes): cancel all remaining RESERVED orders (users who did not complete checkout).

Asked at: Shopify Interview Guide

Asked at: Stripe Interview Guide

Asked at: DoorDash Interview Guide

Asked at: Snap Interview Guide

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

See also: Airbnb Interview Guide 2026: Search Systems, Trust and Safety, and Full-Stack Engineering

Scroll to Top