DEV Community

umesh kushwaha
umesh kushwaha

Posted on

πŸš€ Designing a Flash Sale System That Never Oversells</h1> <h3>From 1 User to 1 Million Users (Without Crashing Redis)

Example: Flash sale for 1,000 iPhones with 1,000,000 users clicking β€œBuy” at the same time.


🧠 Why Flash Sales Are Hard

Flash sales look simple:

β€œWe have 1,000 items. When inventory reaches zero, stop selling.”

But in production, flash sales are one of the hardest problems in distributed systems.

  • Millions of concurrent requests
  • Multiple app servers
  • Eventually consistent caches
  • Databases that cannot absorb spikes
  • Redis that can still melt under pressure

The real challenge is not inventory.
It is correctness under extreme contention.


🎯 Core Requirements

  • ❌ No overselling (ever)
  • ⚑ Low latency
  • 🧱 Handle massive traffic spikes
  • πŸ”₯ Protect Redis & database
  • πŸ›‘οΈ Graceful failure & recovery

🧩 Core Insight

Flash sales are load-shedding problems disguised as inventory problems.

If only 1,000 users can succeed, then 999,000 users must fail fast β€” cheaply and safely.


1️⃣ Naive Database Approach (Incorrect)

SELECT stock FROM inventory WHERE product_id = 1;

IF stock > 0:
  UPDATE inventory SET stock = stock - 1;
  CREATE order;

❌ What goes wrong

  • Two requests read stock = 1
  • Both decrement
  • Inventory oversold

Verdict: ❌ Incorrect even at low scale.


2️⃣ Database Locking (Correct but Not Scalable)

SELECT stock
FROM inventory
WHERE product_id = 1
FOR UPDATE;

βœ… Pros

  • Strong consistency
  • No overselling

❌ Cons

  • Requests serialized
  • Database becomes bottleneck
  • Throughput collapses

Use only for very low traffic.


3️⃣ Atomic SQL Update (Better, Still Limited)

UPDATE inventory
SET stock = stock - 1
WHERE product_id = 1 AND stock > 0;

βœ… Pros

  • Simple
  • Correct

❌ Cons

  • Database still hot
  • Does not scale for flash sales

4️⃣ Redis as Inventory Gatekeeper

DECR inventory:iphone

If result < 0 β†’ rollback and reject.

Redis + Lua (Atomic)

local stock = redis.call("GET", KEYS[1])
if tonumber(stock) > 0 then
  redis.call("DECR", KEYS[1])
  return 1
else
  return 0
end

βœ… Pros

  • Very fast
  • No overselling

🚨 Hidden Problem: Redis Can Still Go Down

Scenario:

  • Inventory: 1,000
  • Users: 1,000,000
  • All users hit the same Redis key

Even Redis has limits:

  • Single hot shard
  • Network saturation
  • CPU contention
  • Timeouts and failures

Correctness without traffic control is failure.


5️⃣ Mandatory Defense: Early Rejection

Local In-Memory Gate

Each app instance keeps a small local counter.

if localRemaining == 0:
  reject immediately

βœ… Pros

  • Protects Redis massively
  • Very cheap

❌ Cons

  • Soft limits
  • Needs reconciliation

6️⃣ Batch Inventory Allocation (High Impact)

DECRBY inventory:iphone 20

Serve 20 users locally before hitting Redis again.

βœ… Pros

  • Redis calls β‰ˆ inventory count
  • Huge throughput improvement

❌ Cons

  • Over-allocation needs give-back logic
  • Slight fairness skew

7️⃣ Redis Hot Shard Problem & Striping

Single key β†’ single shard β†’ overload.

Solution: Bucketed inventory.

inv:iphone:0
inv:iphone:1
...
inv:iphone:49

βœ… Pros

  • Load distributed across shards

❌ Cons

  • Retry logic near tail

8️⃣ Token / Permit Model

1 token = 1 purchase. Pre-generate 1,000 tokens.

βœ… Pros

  • Impossible to oversell
  • Clean mental model

❌ Cons

  • Token cleanup on failure

9️⃣ Admission Control

Only allow slightly more users than inventory to proceed.

INCR sale:attempts
reject if > 1500

Effect

  • Redis protected
  • Fast rejection

πŸ”Ÿ Queue-Based Flash Sale (Extreme Scale)

  • Users enqueue buy requests
  • Workers process sequentially
  • Stop after inventory exhausted

Trade-off

  • Higher latency
  • Excellent stability

πŸ”„ Failure Handling

  • Payment failure: TTL + release inventory
  • Duplicate clicks: Idempotency keys
  • Redis crash: Reload from DB
  • Cache drift: Reconciliation job

βš–οΈ Trade-off Summary

Approach Scale Redis Load Complexity
DB Lock Low None Low
Redis DECR High High Medium
Batch Allocation Very High Low High
Queue-Based Extreme Minimal High

🏁 Final Thought

A successful flash sale is not about selling fast.
It is about rejecting users correctly while protecting shared state.

If you enjoyed this, follow for more system design deep dives.

Top comments (0)