Blog 15 — Shopify: Surviving Flash Sales

C

Qubits of DPK

March 21, 2026

Core Case Studies
Core Concept: Distributed locks, Optimistic locking, Queue-based purchase flow, Preventing overselling
Why SDE-2 Critical: Directly relevant for Flipkart, Meesho, Amazon India — flash sale design is a real interview question
Status: Draft notes ready

Quick Revision

  • Problem: Millions try to buy limited inventory at the same instant.
  • Core pattern: Queue requests, reserve inventory atomically, confirm after payment.
  • Interview one-liner: Flash-sale systems are throughput problems wrapped around correctness constraints.

️ The Core Problem

javascript
QUBITS OF DPK
1Flash sale: iPhone 15 at 50% discount
210M users click "Buy" at 12:00:00.000
3
4Inventory: 1000 units
5
6Naive approach:
7  10M concurrent requests → all check inventory > 0 → all decrement
8  Result: -9,999,000 inventory (oversold by 9.99M units) 💥
9
10Challenge: Prevent overselling while maximizing throughput

Core Concepts

Optimistic Locking (Check-and-Set)

java
QUBITS OF DPK
1// Read inventory with version
2SELECT inventory, version FROM products WHERE id = 123
3// Returns: inventory=1000, version=5
4
5// Attempt purchase
6UPDATE products
7SET inventory = inventory - 1, version = version + 1
8WHERE id = 123 AND version = 5  // version check!
9
10// If version changed by another request → 0 rows updated → retry
11// If version matches → 1 row updated → success
  • No DB locks held between read and write
  • High throughput for low-contention scenarios
  • Problem: Under 10M concurrent requests, retry storm = thundering herd again

Redis Atomic Decrement (Better for Flash Sales)

javascript
QUBITS OF DPK
1Store inventory in Redis:
2  SET inventory:product_123 1000
3
4Purchase attempt (atomic):
5  DECR inventory:product_123
6  IF result >= 0 → proceed to checkout
7  IF result < 0 → sold out, increment back (compensate)
8
9Why Redis works here:
10  Single-threaded → DECR is atomic by nature
11  No race conditions possible
12  1M+ ops/sec → handles flash sale traffic

Queue-Based Purchase Flow (Shopify's Actual Solution)

javascript
QUBITS OF DPK
1All 10M purchase requests → message queue (Kafka/Redis Queue)
234Consumer pool (e.g., 100 workers)
5  Each worker:
6    1. Pop request from queue
7    2. Check inventory (Redis atomic)
8    3. If available: reserve → create order → notify user
9    4. If sold out: notify user "sold out"
10
11Benefit:
12  DB receives at most 100 requests/sec (worker count)
13  Queue absorbs 10M spike gracefully
14  Users get "you're in queue" feedback immediately

Inventory Reservation (2-Phase Commit)

javascript
QUBITS OF DPK
1Phase 1: Reserve
2  When user clicks Buy:
3    Reserve 1 unit (decrement Redis inventory)
4    Hold for 10 minutes (payment window)
5    Start payment flow
6
7Phase 2: Confirm or Release
8  If payment succeeds → confirm reservation → create order
9  If payment fails/timeout → release reservation → +1 inventory back
10
11Why? Don't actually deduct until payment confirmed
12  Prevents: item stuck as "sold" when payment failed

Shopify's Infrastructure Preparation

javascript
QUBITS OF DPK
1Before flash sale:
2  ├── Pre-warm caches (product data fully in Redis)
3  ├── Disable non-essential features (recommendations, reviews)
4  ├── Scale up workers ahead of time (not during spike)
5  ├── Set strict rate limits per user (1 purchase per user)
6  └── Dark launch test (simulate 10x expected load)

Scale Achieved

5 Interview Questions This Blog Unlocks

Q1. Design Amazon's Great Indian Festival flash sale

Answer: Queue all purchase requests. Workers process at controlled rate. Redis atomic DECR for inventory (single-threaded = no race conditions). 2-phase reservation: reserve on click, confirm on payment success, release on timeout. Pre-warm caches. Rate limit per user. Disable non-critical features under load.

Q2. What is the difference between optimistic and pessimistic locking?

Answer: Pessimistic: lock the row before reading, hold until transaction completes. Safe but slow — every other request waits. Optimistic: read without lock, include version in update condition. No locks held. If version mismatch → retry. Best for low-contention. For 10M concurrent flash sale requests, optimistic causes retry storm — use queue instead.

Q3. How does Redis atomic operations prevent overselling?

Answer: Redis is single-threaded. DECR command is atomic — read-modify-write happens as one operation, no other command executes between them. Even with 10M concurrent clients, Redis processes DECR one at a time. If result >= 0, sale proceeds. If result < 0, sold out. No race condition possible.

Q4. What is inventory reservation and why is it important?

Answer: Don't permanently deduct inventory when user clicks Buy. Instead, reserve (soft-lock) for N minutes. If payment succeeds → confirm. If payment fails or times out → release reservation. This prevents: sold-out items when payments fail, inventory stuck in limbo, users being charged for items that can't be delivered.

Q5. How would you prevent a single user from buying 1000 units in a flash sale?

Answer: Rate limiting per user + per session. Redis SET user_purchased:userId with TTL. Check before allowing purchase. Limit: 1 unit per userId per flash sale event. Also: add CAPTCHA for bot detection, device fingerprinting, purchase history analysis for suspicious patterns.

Key Engineering Lessons