Introduction #
A High Concurrency flash sale system is a system that is designed to handle a large number of concurrent users who are trying to purchase a limited number of items in a short period of time.
1. Understanding the Requirements #
Let’s consider the core functional requirements for our flash sale system:
- Admins can create, schedule, pause and stop flash sale events with limited inventory per SKU and discount rules.
- Users can browse sale listings, see real time-ish product availability and countdown timers, and attempt purchases during the sale window.
- System must accept purchase attempts, validate inventory, apply discounts, and record orders.
And now for the non-functional requirements:
- Handle sudden 10-100x spikes vs normal traffic; peak may be hundreds of thousands to millions of requests per minute at the edge.
- Low Latency for “hot path” APIs (inventory check / reservation) - sub 100ms p95 at the app layer.
- Very high availability during the event, strong consistency for inventory and orders (no double-selling the same item) and graceful degradation instead of crashes.
2. High-Level Design #
At a high level, you want a layered design that pushes load handling to the edges and keeps the “critical section” (inventory decrement + order creation) as small and isolated as possible.
Typical Components #
- CDN: Static pages, product images etc can be served from CDN.
- API Gateway: Handles authentication, rate limiting, request routing to appropriate services. (See API Gateway for further details).
- Rate Limiter: Implements Token bucket algorithm to handle sudden spikes in traffic.(See Rate Limiter for further details).
- Flash Sale Service: Validates sale window, user eligibility, and interacts with Inventory + Queue.
- Inventory Service: Maintains hot inventory in an in-memory store like Redis.
- Message Queue (Kafka, MQ): Buffers accepted purchase attempts and decouples spikes from downstream systems.
- Order Service: Consumes from the queue, creates orders, coordinates with Payment Service and persists to DB.
- Payment Service: Integrates with payment gateways; updates order state.
- DB: Relational DB(Postgres, MYSQL) as system of record for orders, payments and inventory.
Sample Component Diagram using Redis for inventory #
3. Data Modelling for a Flash Sale #
The data model should make the “inventory-critical path” small and easily protected, while the rest uses standard e-commerce schemas.
Key Entities #
- Product: Basic product info, SKU, images, description etc.
- FlashSaleEvent: Defines the sale window, start time, end time, status (upcoming, active, ended) etc.
- FlashSaleProduct: Links products to a sale event, with sale-specific price and inventory.
- Reservation: Temporary hold on inventory for a user during the purchase flow, contains fields like
reservation_id,user_id,product_id,quantity,expires_at,status(active, expired, converted). - Order: Final order record, created after successful payment. Contains fields like
order_id,user_id,product_id,quantity,price,status(pending, paid, failed).
During the sale, the SSOT for inventory is Redis with DB updated asynchronously, the DB remains the source of truth for orders and payments.
4. Inventory Oversell Prevention Strategies #
Preventing oversell is the heart of any flash sale system, the design should ensure that no path can decrement the inventory to below zero.
a. Database Only #
Keep remaining_quantity in the DB and use row-level locks or optimistic locking to prevent oversell.
(Optimistic Locking is a strategy where we assume that the data is not going to be contested and we don’t use locks. Instead, we use a version column (or timestamp, or checksum) to detect if the data has been modified by another transaction. If it has, we abort the transaction and retry.)
b. Redis as the Hot Inventory Store #
For high concurrency, maintain inventory counters in Redis and use atomic operations / Lua scripts. Lua script runs atomically within Redis, ensuring that no two requests can decrement the inventory below zero.
c. Queue-Based Strict Serialization #
For extreme fairness / robustness, you can treat inventory as a ticket dispenser behind a queue. Enqueue each validated purchase attempt to a per-event queue. A small pool of consumers dequeue and decrements inventory, and create reservations/orders. This serializes all inventory writes and is extremely robust, but adds latency and requires a separate “inventory worker” process.
5. Reservation vs Direct Order Creation #
You need to choose whether to reserve stock when a user clicks “Buy Now”, then finalize after payment or create the order directly.
Reservation Model (Common) #
For every Buy Now request, you atomically decrement inventory in Redis, create a Reservation record in Redis with a short TTL (e.g. 10 minutes) or in DB, and wait for user to complete payment. If the payment succeeds before expiry, the reservation is converted to a confirmed order. If not, the reservation expires and inventory is released. You could run a recovery job that scans expired reservations and adds stock back.
Direct Order Model #
Immediately create an order with Pending_Payment state and decrement inventory. If payment fails or times out, you mark the order cancelled and release the inventory. This can be done using a cron job that scans for orders in Pending_Payment state and releases the inventory. It’s simple conceptually but requires a robust recovery mechanism to handle failures and prevent dead inventory.
When reservation is better: when conversion rate is uncertain and you expect many failed/abandoned attempts (typical in flash sales), reservations keep “trash” out of the main order table. It prevents the order table from filling up with failed orders and keeps the critical path clean.