Want it harder? Hide the model answer and work each phase in a scratchpad against its time budget, then reveal and score yourself. Or simply read on: the checkpoints below work either way.
How Do You Open a Shop to Six Million People in the Same Minute, and Sell Five Thousand Phones Exactly Once Each?
It is 11:58 pm on 7 October 2026 in Bhagalpur. Rohan Mandal, a final-year engineering student, has had the same phone in his wishlist for three weeks. His Plus membership lets him into the sale at midnight, a day before everyone else. He has the app open on the phone's page and his UPI app logged in. The deal says ₹9,999 and "limited stock". He doesn't know how limited: 5,000 units, for everyone in India who has the same page open.
At 12:00:00 the button changes from "Notify me" to "Buy now". Rohan taps it at 12:00:01, and so do a few hundred thousand other people, from Bengaluru flats on fibre and from towns on one bar of 4G. In the next ten seconds something has to decide which 5,000 of them get the phone, tell everyone else the truth quickly, take money only from those 5,000, and never sell unit 5,001. Rohan also adds a washing machine for his parents before Diwali. The page says "Delivery by Tue, 13 Oct", and that has to stay true after millions of orders land on the same warehouses tonight.
- 6.3 million concurrent users in a minute in the opening hour of TBBD 2026; Early Access, which opened at midnight on 8 October, drew 287 million+ sessions, with "over 6x more Indians shopping on the platform than in a regular hour" (Flipkart, 9 October 2026)
- 6–8× the business-as-usual transaction volume during the sale; the 2023 sale ran at 7× the usual transactional data (Flipkart and Google Cloud, October 2023)
- ~1,000× more payment requests at the festive peak than in normal business; biometric-authenticated transactions had a 12–15% higher success rate than OTP-based ones (Gaurav Arora, Flipkart's vice-president of payments, to Business Standard in September 2026, as reported by DQIndia)
- 95 million transactions a second sustained on Flipkart's in-house Aerospike platform during The Big Billion Days (Aerospike press release, November 2024); 90 million queries a second in aggregate from 50+ use cases, including search, pricing and inventory, across 3 data centres (Flipkart engineers' talk, as written up by Aerospike, November 2025)
- 7.3 million shipments fulfilled in a single day and 5,000+ a minute on the peak days of BBD 2025 (Flipkart, December 2025), against ~4 million on an average day (Flipkart Tech, 2026)
- 500 million registered users and 150 million products (Flipkart, 2026)
For context:
Flipkart publishes sale-day traffic, payments and supply-chain numbers and parts of its infrastructure; it has not published how it queues buyers for a deal, reserves stock or runs checkout, so those parts are labelled as our design
Every number labelled as an assumption is illustrative
This is the system we'll design today.
Flipkart reported 6.3 million concurrent users in a minute in the opening hour of The Big Billion Days 2026; the sale brings 6 to 8 times the usual transactions and about 1,000 times the payment requests. On stated assumptions that is ~3.2 million requests a second at the edge, ~630,000 reaching services, ~1,200 orders a second at the opening (designed for 5,000) and ~30,000 taps a second on one 5,000-unit deal. The sale is staged as a version of prices, offers, deal states and pages pushed to every cache and the CDN, and at 12:00:00 one pointer flips. Only a deal's Buy now button queues: admission at the edge orders the first 2 seconds of taps at random and issues tokens for units × 1.3, one per customer. Checkout holds a unit for 10 minutes, extended while money is in flight, and carries one idempotency key to the bank; a timeout is unknown, not failed, and is reconciled, the paid state and an outbox row commit in one transaction, and a breaker per bank demotes a slow issuer. The delivery date is a slot reserved in a warehouse wave, so as the 12:30 wave fills, new viewers see a later date. The deep dives end with a ₹999 typo, a fulfilment centre going dark and Day 1 a month before Diwali.
The Interview Begins#
You're interviewing at an e-commerce company. The interviewer shares two screenshots from the same phone: a product page at 12:00:00 with a red "Buy now" button and "Only a few left", and an order confirmation at 12:00:41 reading "Order placed · Delivery by Tue, 13 Oct".
"Forty seconds separate these two screens, and in those forty seconds a few million other people were doing the same thing. Design the platform that runs the sale: the opening, the deals, the cart and checkout, the payments, browsing and search, and the delivery date."
For about a week each autumn, a marketplace with hundreds of millions of users runs a sale with deep discounts. Members get in at midnight a day early, then everyone. Some offers are limited-quantity deals that go live at fixed times. Traffic, orders and payments jump to many times a normal day within minutes.
Requirements
- Open the sale at a fixed moment: prices, offers and deal pages switch for everyone together
- Sell limited-quantity deals fairly and fast, and never sell more units than exist
- Carts, checkout and payments that hold at many times the usual rate
- Browse and search 150 million products while the sale is on
- A delivery date on every page and order that the supply chain can keep
"Leave out seller onboarding, ads, the recommendation models, returns and the 10-minute grocery service. Assume a payment gateway exists; you decide how checkout uses it."
Phase 1: Requirements Clarification (5 minutes)#
"A sale is a planned spike with a hard start time, so I'll ask about the shape of the load first, then about what must never go wrong."
Your Questions#
"How big is the opening?"
"Flipkart reported 6.3 million concurrent users in a minute in the opening hour of the 2026 sale, with over six times as many people shopping as in a regular hour. Early Access opened at midnight on 8 October for Plus and Black members and drew more than 287 million sessions."
"And the work behind the traffic?"
"A Flipkart architect and Google Cloud wrote in 2023 that the sale brings a transaction volume surge of six to eight times business-as-usual. Payments are steeper: Flipkart's vice-president of payments told Business Standard in September 2026, as DQIndia reported, that it handles nearly 1,000 times more payment requests at the festive peak than in normal business."
"What's a deal, physically?"
"A product at a sale price with a fixed number of units, going live at a set time. Flipkart hasn't published how it runs them. For scale: in a 2014 flash sale on Flipkart, 40,000 phones sold out in 4.2 seconds with more than 250,000 people registered."
"Where does the delivery date come from?"
"Flipkart's engineers describe a Promise Engine that computes the date from processing capacity across its network and truck schedules. On an average day Flipkart handles about 4 million shipments across 19,000 pincodes; on a peak day of the 2025 sale it shipped 7.3 million."
"And the correctness bar?"
"Never sell what you don't have. Never charge a price other than the one shown. Never charge without an order, or twice for one. And the date you promise should be one you can keep."
Functional Requirements#
- Sale opening: each phase (Early Access, Day 1) activates at a set time; prices, offers and deal states switch together
- Deals: a start time, a unit count and a per-customer limit; buyers learn quickly whether they got one
- Inventory: held at checkout, sold on payment, released on expiry or failure; never more sold than exist
- Checkout and payments: UPI, cards, EMI, pay-later; a retry never charges twice; unknown outcomes are resolved, not guessed
- Browse and search: pages for 150 million products with sale prices, offers and stock badges
- Delivery promise: a date per product and pincode on the page, and a committed date on the order
Non-Functional Requirements#
Peak (published and estimated)
- 6.3 million concurrent users in a minute (published); ~3.2 million requests a second at the edge (estimated below)
- ~1,200 orders a second in the first minutes, designed for 5,000 (estimated)
- ~30,000 "Buy now" taps a second on one hot deal (estimated)
Latency (design targets)
- Product and search pages p99 < 300 ms at the edge
- Reserve-and-checkout p99 < 500 ms; a deal verdict ("you're in" or "sold out") in < 2 s
Correctness
- Zero units sold beyond stock; zero duplicate charges
- The price charged is the price shown at checkout, or lower
- ≥ 95% of sale-week orders delivered by the promised date (design target)
Phase 2: Back of the Envelope Estimation (5 minutes)#
"The anchors are published: 6.3 million concurrent users in a minute, 6–8× the usual transactions, 7.3 million shipments on a peak day. Flipkart doesn't publish request rates or orders a second, so the rest are my labelled assumptions."
Before the numbers: what does a concurrent user cost, and what does one deal cost?
6.3 million people are on the app in the same minute, each app making an API call every two seconds; 80% can be answered at the edge. How many requests a second reach your services? Then one deal: 5,000 units, 300,000 taps in ten seconds. How many writes a second hit that product's stock if every tap goes to the database, and how many can succeed?
One strong answer
- Requests: 6.3 million × 0.5 a second ≈ 3.2 million a second at the edge. With 80% served by the CDN and edge caches, ~630,000 a second reach services; design for 2×.
- The deal: 300,000 ÷ 10 s = 30,000 writes a second on one key, of which at most 5,000 can ever succeed.
- So the read side is huge but forgiving, the write side small but exact.
The Opening Minutes#
| Value | |
|---|---|
| Concurrent users in a minute, opening hour (published, 2026) | 6.3 million |
| API calls per open app (assumed: one every 2 s) | 0.5 a second |
| Requests at the edge | ~3.2 million a second |
| Share answered at the CDN and edge caches (assumed) | ~80% |
| Requests reaching services | ~630,000 a second; design for ~1.3 million |
| Database transactions a second during TBBD (published by Aerospike, November 2024) | 95 million |
Conclusion: if the edge estimate is right and most of that database load came from these requests, 95 million database transactions a second would be ~30 lookups per edge request, or ~150 behind each request that reaches a service (price, offers, stock, delivery date, recommendations, ads); only an inference, since the numbers come from different years and the database also serves other work. Either way, the read path must be served from memory.
Orders and Payments#
- Peak shipping day (published, BBD 2025): 7.3 million shipments
- Orders on the opening day (assumed 1.5× the peak shipping day): ~11 million
- Share in the opening hour (assumed 10%): ~1.1 million → ~300 orders a second on average
- First five minutes (assumed 4× the hour's average): ~1,200 orders a second; design for 5,000
- Checkout starts per completed order (assumed 3): abandoned carts, failed payments, sold-out deals → ~3,600 stock reservations a second at the opening peak
- Payment calls per order (assumed ~4): create, authenticate, confirm, plus retries → ~5,000 a second at the opening peak, ~20,000 at the design point
Conclusion: orders are tiny next to page views, but each touches stock, a delivery slot and a payment, and payments go to banks we don't control.
One Flash Deal#
- Units: 5,000; page open at 12:00 (assumed): ~500,000 people
- Taps in the first 10 seconds (assumed 60%): 300,000 → ~30,000 a second on one product
- Published comparison: 40,000 units in 4.2 s in 2014 ≈ ~9,500 units sold a second
- Without admission: 30,000 conditional writes a second on one stock key; ~295,000 of them fail
- With admission tokens (Deep Dive 2): ~6,500 tokens (5,000 × 1.3), so ~6,500 reservations in total
Conclusion: the deal's real load is at the front door, not in the inventory service. Turn the herd away before it reaches the stock.
Key Metrics Summary#
| Metric | Value |
|---|---|
| Concurrent users in a minute (Flipkart, 2026) | 6.3 million |
| Transaction volume vs usual (Flipkart and Google Cloud) | 6–8× |
| Payment requests vs usual (Flipkart's VP of payments, via DQIndia, 2026) | ~1,000× |
| Requests at the edge / at services (estimated) | ~3.2 million / ~630,000 a second |
| Orders at the opening peak (estimated) / design | ~1,200 / 5,000 a second |
| Taps on one hot deal (estimated) | ~30,000 a second |
| Products (Flipkart) | 150 million |
| Shipments, average / BBD 2025 peak day (Flipkart) | ~4 million / 7.3 million |
Requirements and estimation
Q1/2 6.3 million open apps each make an API call every two seconds. Roughly how many requests a second reach the edge?
Answer & explanation
Answer: B
6.3 million × 0.5 a second ≈ 3.2 million. With 80% served at the edge, ~630,000 reach services.
Q2/2 Why can't the delivery date on a product page be computed per request?
Answer & explanation
Answer: B
The page shows a cached date; the order reserves a slot. The same split as stock: a hint on the page, a reservation at checkout.
Phase 3: High-Level Design (10 minutes)#
Sketch it first: Rohan's 12:00:01 tap
Rohan taps "Buy now" one second after midnight. What has to be already in place for the page he's looking at to show ₹9,999 and not the old price? Which component decides whether he is one of the 5,000, and what does it hand him? Where is a unit taken out of stock, and when does it go back if he doesn't pay? Then he pays by UPI: what links the payment to his order so a double tap can't charge twice, and what tells the warehouse to pick his phone?
One strong answer
- Before midnight: the sale's prices, deal states and pages were staged as a new version and pushed to caches and the CDN; at 12:00:00 one pointer flips. Nothing is recomputed at midnight.
- Deal admission at the edge gives Rohan a short-lived token, one per customer, if he's within the deal's admission count; everyone else is told "in line" or "sold out" without touching stock.
- Inventory takes one unit when checkout starts, as a hold that expires (say in 10 minutes), keyed by his checkout id.
- Checkout creates the order in
PENDING_PAYMENTwith an idempotency key that the payment carries, so a second tap finds the first attempt. On success the order isCONFIRMED, the hold becomes a sale, and an event goes to fulfilment over the message bus. - The delivery date on his order is a slot reserved in a warehouse wave at checkout, not the page's cached date.
"Three ideas carry this design. A read plane that is huge, cached and allowed to be a little stale: pages, prices, search, stock badges, delivery estimates. A write plane that is small and exact: admission, reservation, orders, payments, delivery slots. And a sale clock: everything that changes at midnight is built in advance as a new version, and midnight is one switch."
System Architecture#
%% title: A sale platform: a cached read plane, a small exact write plane, and one switch at midnight
flowchart TD
subgraph L1["Layer 1 · People"]
direction LR
APP@{ icon: "sd:phone", label: "Flipkart app and site<br/>browse, deals, checkout" }
OPS@{ icon: "sd:users", label: "Sale ops and sellers<br/>deals, prices, stock" }
end
subgraph L2["Layer 2 · Edge"]
direction LR
CDN@{ icon: "sd:cdn", label: "CDN<br/>pages, images, deal states" }
GW@{ icon: "sd:gateway", label: "Gateway<br/>tiers, shedding, bot checks" }
end
subgraph L3["Layer 3 · Read plane"]
direction LR
SRCH@{ icon: "sd:search", label: "Search and browse<br/>index without prices" }
PRICE@{ icon: "sd:cache", label: "Price and offer cache<br/>versioned price books" }
PROM["Promise engine<br/>date per pincode, wave capacity"]:::core
end
subgraph L4["Layer 4 · Write plane"]
direction LR
ADM["Deal admission<br/>tokens per deal"]:::hot
INV["Inventory<br/>holds with expiry"]:::hot
CHK["Checkout and orders<br/>idempotent state machine"]:::core
PAY["Payments<br/>routing, unknown outcomes"]:::core
end
subgraph L5["Layer 5 · State and partners"]
direction LR
KV@{ icon: "sd:database", label: "Stock and order stores<br/>strongly consistent" }
BUS@{ icon: "sd:queue", label: "Message bus<br/>priority lanes" }
BANK@{ icon: "sd:bank", label: "Gateways and banks<br/>UPI, cards, EMI" }
FC@{ icon: "sd:warehouse", label: "Fulfilment centres<br/>waves and cut-offs" }
end
APP ==> CDN
APP ==> GW
OPS -.->|"staged before midnight"| PRICE
GW --> SRCH
GW --> PRICE
GW --> PROM
GW ==>|"Buy now"| ADM
ADM ==> INV
INV ==> CHK
CHK ==> PAY
PAY ==> BANK
INV --> KV
CHK --> KV
CHK -->|"reserve a slot"| PROM
CHK ==> BUS
BUS ==> FC
class APP client
class OPS external
class CDN,GW edge
class SRCH service
class PRICE cache
class KV store
class BUS queue
class BANK external
class FC storeKEY INSIGHTS:
- Two planes, two rules. The read plane may be seconds stale and is served from the CDN and memory; the write plane is exact and small, and nothing in it trusts a cached number
- The sale is a version, not an event. Prices, deals and pages for each phase are built and pushed before it starts; the start is one pointer flip
- Every hint has a commitment behind it. "In stock" on the page becomes a hold at checkout; "Delivery by 13 Oct" becomes a reserved slot in a warehouse wave; the price shown is the price charged
Data Flow: Rohan's Midnight Purchase#
%% title: Rohan buys the deal phone at 12:00:01 sequenceDiagram autonumber participant A as Rohan's app participant E as Edge and admission participant C as Checkout participant I as Inventory participant P as Payments participant B as Bank A->>E: Buy now, deal 4471, at 12:00:01 E->>E: Within admission count, one per customer E-->>A: Token t93, valid 3 minutes A->>C: Checkout with t93, key k1 C->>I: Hold 1 unit for 10 minutes, key k1 I-->>C: Held C->>C: Order PENDING_PAYMENT, slot for 13 Oct held A->>P: Pay by UPI, key k1 P->>B: Collect request B-->>P: Success P->>C: Paid, key k1 C-->>A: Order confirmed
MEANWHILE, IN PARALLEL:
- Unpaid holds expire after 10 minutes and unused tokens after 3, admitting the next people in line; when both run out, the deal flips to "sold out" on the CDN
- The confirmed order goes over the message bus to the fulfilment centre, which picks it in the wave its slot was reserved in
High-level design
Q1/2 The product page shows "In stock" from a cache that is three seconds old. Is that a bug?
Answer & explanation
Answer: B
The page may be stale; the reservation is exact. Overselling happens only if something sells from the hint.
Q2/2 Rohan's UPI app times out and he taps "Pay" again. What stops a double charge?
Answer & explanation
Answer: B
One key from checkout to payment, so a retry is a lookup, not a new charge.
Phase 4: Deep Dives (20 minutes)#
Deep Dive 1: The Midnight Spike, a Peak You Can See Coming (Week 10 Concepts: Capacity Planning; Week 4: Thundering Herd)#
"The worst minute of the year is on the calendar months ahead. It's a planning problem with a rehearsal, and a caching problem where nothing may expire at the same moment."
This builds on Week 10, Day 4 · Capacity Planning, Week 4, Day 3 · Thundering Herd and Week 4, Day 5 · Multi-Tier Caching.
11:59:59
It's one second to midnight. At 12:00:00 millions of open apps will ask for the sale version of the same home page, deal pages and prices. In 2014 Flipkart sized for 20× the traffic and was still overrun. What must be warm before midnight, and how do you know it's enough? What happens if every cached price has a TTL that ends at midnight? And if load still outruns you at 12:00:30, what do you switch off first?
One strong answer
- Size from a forecast, then rehearse it. Forecast per surface (home, search, product, checkout, payments), provision with a margin, and load-test production at that rate weeks before. 2014's lesson: a multiplier isn't a forecast; a new kind of day has its own shape.
- Warm everything read at midnight: sale pages on the CDN, prices and offers in every cache node, search caches filled by replaying top queries, connection pools open. A cold cache at 12:00 is an outage.
- Never expire at midnight. If cached prices expire together, every miss hits the price service in the same second. Build the sale's prices as a new version under new keys, push them before midnight, and flip one pointer.
- Shed by tier. Checkout, payment and order status are tier 1; product pages, search and cart are tier 2 and can serve slightly stale data; recommendations, reviews, personalised widgets and order history are tier 3 and go first.
What is published: in 2014 Flipkart "had deployed nearly 5000 servers and had prepared for 20 times the traffic growth", and traffic was still much higher. A Flipkart architect and Google Cloud wrote in 2023 that the sale brings "a transaction volume surge of six to eight times" business-as-usual, and that its data platform ingests about 60 TB of compressed messages a day normally and 500 TB a day at peak events. Aerospike reported in November 2024 that Flipkart's platform sustained 95 million transactions a second during The Big Billion Days, run on Kubernetes. A CNCF case study (2026) describes Flipkart's central chaos platform running 2,000+ experiments, finding 6+ critical misconfigurations ahead of TBBD 2024 and cutting the time to detect infrastructure failures by about 20% and to resolve them by about 25%.
- Forecast per surface, not per site. Pages multiply by the opening's concurrency; payments by far more (1,000× in Flipkart's words). Size each tier by its own multiplier, and give the banks and gateways the forecast too.
- Stateful tiers first. Databases and caches must rebalance data when they grow; grow them days ahead, then freeze.
- Rehearse failure, not just load. A load test proves capacity; a chaos test proves the fallbacks: kill a cache node or slow a bank at the forecast rate and check that shedding and breakers behave.
- Freeze changes. No deploys or config pushes in the sale window except fixes; every risky feature behind a flag.
- The app helps. It downloads the sale's assets hours ahead, counts down on the server's clock, and adds 0–3 s of random jitter to its first refresh after midnight, so 6 million phones don't ask in the same 100 ms.
%% title: The midnight switch: build ahead, push ahead, flip once
flowchart TD
STAGE["Sale ops stage version v42<br/>prices, offers, deal states"]:::service
BUILD["Build and validate v42<br/>guardrails, two approvals"]:::core
PUSH@{ icon: "sd:cache", label: "Every cache node<br/>v41 live, v42 loaded" }
EDGE@{ icon: "sd:cdn", label: "CDN<br/>v42 pages pushed, not live" }
FLIP["12:00:00 · pointer v41 to v42<br/>one write, read everywhere"]:::hot
APP@{ icon: "sd:phone", label: "Apps<br/>refresh with 0 to 3 s jitter" }
STAGE ==> BUILD ==> PUSH
BUILD ==> EDGE
PUSH ==> FLIP
EDGE ==> FLIP
FLIP ==> APP
class PUSH cache
class EDGE edge
class APP client# sale/switch.py
"""
Switching a sale phase on by version (illustrative). Every price, offer and deal
state for the phase is written under a new version before it starts; the start
is one pointer write. Readers resolve the pointer, then read version-keyed
entries, so nothing expires or is rewritten at midnight.
"""
import time
ACTIVE = "sale:active_version"
def stage(kv, version: str, prices: dict, offers: dict, deals: dict) -> None:
for sku, p in prices.items():
kv.set(f"price:{version}:{sku}", p) # new keys; the live version is untouched
for sku, o in offers.items():
kv.set(f"offer:{version}:{sku}", o)
for deal_id, d in deals.items():
kv.set(f"deal:{version}:{deal_id}", d)
kv.set(f"ready:{version}", True) # set only after validation passes
def activate(kv, version: str, at_epoch: float) -> None:
assert kv.get(f"ready:{version}"), "never flip to a version that isn't fully staged"
while time.time() < at_epoch:
time.sleep(0.01)
kv.set(ACTIVE, version) # the whole switch is this one write
Deep Dive 2: Flash Deals and the Waiting Line (Week 3 Concepts: Backpressure and Flow Control; Week 1: Rate Limiting)#
"A deal is a tiny amount of stock with an enormous queue in front of it. The store must stay open for everyone browsing, so the queue belongs to the deal, not the site. And the decision who gets in must be fast, fair across networks, and cheap to say no to."
This builds on Week 3, Day 3 · Backpressure and Flow Control, Week 1, Day 3 · Rate Limiting at Scale and Week 9, Day 5 · Security Architecture.
5,000 phones, 300,000 thumbs
At 12:00:00 the deal goes live. In the first ten seconds 300,000 people tap "Buy now"; a third of them are on slow mobile networks in small towns, and a few thousand of the taps come from scripts. If the server takes whoever arrives first, who wins? How many people should you let into checkout, given that some who get in won't pay? What do the other 290,000 see, and when? And how do you stop one reseller with 200 accounts taking 200 phones?
One strong answer
- First-come at the server favours the fastest network, not the first thumb. Fibre in a metro beats 4G in a small town by hundreds of milliseconds. Treat every tap in the first couple of seconds as simultaneous and order them randomly, then first-come after that.
- Admit a little more than the stock. Issue tokens for units × 1.3 (tuned from past drop-off), each valid for 3 minutes. A token is the only way to reach the stock.
- Everyone else gets an answer from the edge within seconds: "You're in line, position 12,400" while tokens may come back, then "Sold out". Not a spinner, not an error page.
- Limits and bots before tokens: one token per customer, per device and per delivery address for the deal; device and behaviour checks at the gateway; tokens are signed and bound to the account, so they can't be shared or replayed.
What is published: Flipkart hasn't published how it runs deal queues. On record: in 2014 Flipkart "ran out of the stock for many products within a few minutes (and in some cases, seconds) of the sale going live"; a month earlier, a flash sale on Flipkart sold 40,000 phones in 4.2 seconds to a pool of 250,000+ registered buyers (Xiaomi, reported by GSMArena, September 2014). In 2026 Flipkart reported that nearly one in two opening-hour visits came from Tier 3 towns and beyond, and about 60% of demand from outside the metros, which is why "fastest network wins" isn't a neutral rule.
First-come at the database
- Every tap is a conditional write on the stock key
- 30,000 writes a second on one row; ~98% fail
- The fastest networks and the bots win
- A slow stock store slows the whole store
A waiting room for the whole site
- Right when everything on sale is the scarce thing (a concert)
- Here it blocks 6 million people browsing to protect one page
A line per deal, tokens at the edge
- Only the deal's "Buy now" queues; the rest of the store is untouched
- Tokens ≈ units × 1.3; only token holders reach stock
- Lottery window for the first seconds, limits per customer, an honest answer for everyone else
- The line lives at the edge. Per deal: a counter of tokens issued and a set of holders, in memory with a replica. Issuing is one atomic increment-if-below-limit; refusing is a read.
- Tokens recycle. A token that expires unused, or whose payment fails, admits the next person in line, until the units are sold.
- "Sold out" is a state, published once on the CDN when tokens and holds are exhausted; later taps never reach the service.
- Bots are an economics problem. Per-customer, device and address limits make each extra phone cost a new verified account and address; signed, account-bound tokens can't be shared; risky sessions get a challenge before a token, never during payment.
- This is not the BookMyShow waiting room. BookMyShow (Bonus Problem 16) queues a whole on-sale because every seat is the scarce thing. Here the scarce thing is one product among millions, so the line wraps one button.
%% title: One tap on a deal: answered at the edge unless it can succeed
flowchart TD
TAP["Buy now on deal 4471<br/>customer, device, address"]:::service
BOT{"Limits and bot checks pass?"}
WIN{"Inside the lottery window?"}
LOT["Random order among<br/>taps in the first 2 s"]:::service
TOK{"Tokens issued below<br/>units × 1.3?"}
GIVE@{ icon: "sd:ticket", label: "Signed token<br/>3 minutes, one per customer" }
WAIT["You're in line<br/>position shown"]:::service
SOLD@{ icon: "sd:cdn", label: "Sold out<br/>published on the CDN" }
TAP ==> BOT
BOT -->|"no"| WAIT
BOT ==>|"yes"| WIN
WIN -->|"yes"| LOT ==> TOK
WIN -->|"no"| TOK
TOK ==>|"yes"| GIVE
TOK -->|"no, tokens may return"| WAIT
WAIT -.->|"all tokens used"| SOLD
class GIVE ok
class SOLD edge# deals/admission.py
"""
Admission tokens for a limited deal (illustrative). Taps in the first seconds
are ordered randomly, then first come first served; tokens are capped a little
above the deal's units and recycled when they expire or their payment fails.
"""
import hmac, hashlib, random, time
WINDOW_S = 2.0 # erase network-latency advantage in the first seconds
OVERBOOK = 1.3 # tokens per unit, tuned from past drop-off
TOKEN_TTL_S = 180
def issue(kv, deal: dict, customer: str, now: float, secret: bytes):
if kv.sismember(f"holders:{deal['id']}", customer):
return {"status": "ALREADY_IN"} # one per customer
if now < deal["starts_at"] + WINDOW_S:
kv.zadd(f"window:{deal['id']}", {customer: random.random()})
return {"status": "IN_LINE"} # drawn in random order when it closes
return _try_admit(kv, deal, customer, now, secret)
def _try_admit(kv, deal, customer, now, secret):
cap = int(deal["units"] * OVERBOOK) + kv.get_int(f"returned:{deal['id']}")
if not kv.incr_if_below(f"issued:{deal['id']}", cap): # one atomic step
kv.rpush(f"line:{deal['id']}", customer)
return {"status": "IN_LINE"}
kv.sadd(f"holders:{deal['id']}", customer)
expires = now + TOKEN_TTL_S
msg = f"{deal['id']}|{customer}|{expires}".encode()
sig = hmac.new(secret, msg, hashlib.sha256).hexdigest() # bound to this account
return {"status": "ADMITTED", "token": f"{msg.decode()}|{sig}", "expires": expires}The spike and the deal line
Q1/3 Why build the sale's prices as a new version instead of letting cached prices expire at midnight?
Answer & explanation
Answer: B
The thundering herd is caused by synchronised expiry. Pre-load under new keys, then switch.
Q2/3 A buyer on fibre in a metro and a buyer on 4G in a small town tap at the same instant. How does the deal line keep it fair?
Answer & explanation
Answer: B
A short lottery window erases a few hundred milliseconds of network advantage.
Q3/3 A 5,000-unit deal issues 6,500 tokens. Can it oversell?
Answer & explanation
Answer: B
Tokens bound the load on inventory; inventory bounds the sales.
Deep Dive 3: Holding Stock Without Overselling (Week 5 Concepts: Consistency Models; Week 1: Hot Keys; Week 2: Idempotency)#
"The line in the 2014 email that matters most to me is the cancellations: orders accepted for items that had sold out seconds earlier. That's what happens when an order is accepted from a number that was true a few seconds ago. The page can be a few seconds behind. The step that takes a unit must not be."
This builds on Week 5, Day 1 · Consistency Models, Week 1, Day 4 · Hot Keys and Skew and Week 2, Day 2 · Idempotency in Practice.
Sold out seconds earlier
Rohan's phone page says "In stock". 6,500 token holders head for checkout at once for 5,000 units. When should a unit be taken: when he adds it to the cart, when checkout starts, or when payment succeeds? How long can you hold it if he doesn't pay? His UPI request is still pending when the hold is about to expire: what happens? And the 5,000 units sit in three warehouses: does that change anything?
One strong answer
- Take it when checkout starts. At add-to-cart, millions of carts would lock stock nobody buys. At payment, two people could pay for the last unit and one gets a cancellation: the 2014 symptom.
- A hold is a conditional decrement plus a record with an expiry, keyed by the checkout id: "take 1 if available ≥ 1; record hold k1, expires 12:10:01". A retry with the same key finds the same hold.
- Holds expire, unless money is in flight. If payment hasn't started, the hold lapses at 10 minutes and the unit goes back. If a payment is pending or its outcome is unknown, the hold is extended up to a cap (say 30 minutes) while payments resolves it; then it's either sold or released, never both.
- Stock is per warehouse. The hold picks a warehouse that has the unit and can meet the delivery promise; three warehouses mean three counters, naturally sharded.
What is published: the 2014 email explained cancellations as many people buying the same product at once, so orders were accepted for items that had sold out seconds earlier, and the founders promised to "minimise any cancellations". Flipkart's engineers list inventory and pricing among the use cases on its Aerospike platform (Aerospike's write-up of their talk, 2025), and PingCAP describes Flipkart adopting TiDB, a distributed SQL database, after its MySQL setup hit limits on failover, replication and scale (2025). How Flipkart reserves stock, when, and for how long is not published; the rest is our design.
%% title: One unit of deal stock, from available to sold or back stateDiagram-v2 [*] --> AVAILABLE: received at a fulfilment centre AVAILABLE --> HELD: checkout starts, key k1 HELD --> AVAILABLE: hold expired, no payment HELD --> PAYING: payment started PAYING --> SOLD: payment succeeded PAYING --> AVAILABLE: payment failed PAYING --> PAYING: outcome unknown, hold extended SOLD --> [*] class SOLD ok class PAYING queue
- Three numbers, one invariant. Per SKU per warehouse: available + held + sold = on hand. Holds, sales and expiries move units between them; a reconciler checks the sum every few minutes.
- Deal stock is its own pool, carved out before the sale under the deal's own counters, so a deal can't drain the regular listing.
- The hot key is bounded by the line. With admission in front, a deal sees ~6,500 holds in total, not 30,000 writes a second. If a non-deal product is still hot, split its stock into shards (per warehouse, then sub-shards), each decremented only while positive, so the shards can't add up to more than exist. Dream11 (Bonus Problem 25) does the same with mega-contest spots.
- Strong where it counts. Counter and hold record are one atomic write on a strongly consistent store; pages read replicas and caches. A page that says "In stock" a second too long costs a "sorry, just sold out" at checkout, not a cancelled order.
- Expiry is a sweep, not a timer per hold. Holds are indexed by expiry; a sweeper releases expired ones every second. A payment that succeeds after its hold was released takes another unit if one is free, or is refunded.
# inventory/hold.py
"""
Holding one unit at checkout (illustrative). The decrement and the hold record
are one atomic step on the stock store, keyed by the checkout id so a retry
finds the same hold. Holds expire unless a payment is in flight; a sweeper
returns expired units. available + held + sold always equals on hand.
"""
HOLD_S = 600 # 10 minutes to start paying
MAX_HOLD_S = 1800 # cap while a payment's outcome is unknown
class OutOfStock(Exception):
pass
def hold(store, sku: str, fc: str, qty: int, key: str, now: float) -> dict:
existing = store.get(f"hold:{key}")
if existing:
return existing # a retry: same hold
ok = store.transact([
store.decrement_if_at_least(f"stock:{sku}:{fc}:available", qty),
store.increment(f"stock:{sku}:{fc}:held", qty),
store.put_if_absent(f"hold:{key}", {"sku": sku, "fc": fc, "qty": qty,
"state": "HELD", "expires": now + HOLD_S}),
store.index_add("holds_by_expiry", key, score=now + HOLD_S),
])
if not ok:
raise OutOfStock(sku)
return store.get(f"hold:{key}")
def extend_for_payment(store, key: str, now: float, started: float) -> None:
h = store.get(f"hold:{key}")
new_exp = min(now + 300, started + MAX_HOLD_S) # money in flight: wait, within a cap
store.compare_and_set(f"hold:{key}", expect_state=h["state"], state="PAYING", expires=new_exp)
store.index_add("holds_by_expiry", key, score=new_exp)
def sweep(store, now: float) -> None:
for key in store.index_range("holds_by_expiry", max_score=now):
h = store.get(f"hold:{key}")
if h and h["state"] in ("HELD", "PAYING"):
store.transact([
store.compare_and_set(f"hold:{key}", expect_state=h["state"], state="RELEASED"),
store.increment(f"stock:{h['sku']}:{h['fc']}:available", h["qty"]),
store.increment(f"stock:{h['sku']}:{h['fc']}:held", -h["qty"]),
])
store.index_remove("holds_by_expiry", key)Deep Dive 4: Checkout and the Payment Surge (Week 2 Concepts: Idempotency, Circuit Breakers; Week 3: Transactional Outbox)#
"Checkout is where our system meets systems we don't run: gateways, UPI, banks' authentication pages, OTP over SMS. At a thousand times their normal load, some will be slow and some answers won't come at all. The order must stay correct when a partner is slow or silent."
This builds on Week 2, Day 2 · Idempotency in Practice, Week 2, Day 3 · Circuit Breakers, Week 2, Day 1 · Timeout Hell and Week 3, Day 2 · Transactional Outbox.
12:00:20, one bank slows down
Rohan pays by UPI. At the same moment, one large bank's card authentication slows from 2 seconds to 20. Rohan's UPI app shows "pending" and he taps "Pay" again. Ninety seconds later the gateway still hasn't said whether the first payment succeeded. How do you keep the order from being charged twice, cancelled while money is in flight, or lost between "paid" and the warehouse? And what do you do about the slow bank?
One strong answer
- One key for one attempt. Checkout creates the order in
PENDING_PAYMENTwith an idempotency key; every call to payments carries it. The second tap finds the first attempt in flight and shows its status instead of starting another charge. - Unknown is a state, not a failure. The order goes to
PAYMENT_UNKNOWN, the stock hold is extended, and a reconciler polls the gateway's status API until it gets a definite answer; only then is the order confirmed or failed. - Paid and "tell the warehouse" commit together. The order's state change and an outbox row are written in one transaction; a relay publishes the outbox to the message bus. A crash between them can't lose an order.
- Route around the slow bank. Per-bank success rate and latency drive a circuit breaker: checkout stops offering that bank's cards first, suggests UPI or another method, and sheds new attempts instead of queuing them behind 20-second timeouts.
What is published: Gaurav Arora, Flipkart's vice-president of payments and SuperCoins, told Business Standard in September 2026 that Flipkart handles nearly 1,000 times more payment requests during peak festive periods than under normal business conditions, and that biometric-authenticated transactions recorded a 12–15% higher success rate than OTP-based ones in the payment flows discussed (as reported by DQIndia, which adds that a surge of that size can't be met by expanding Flipkart's own databases alone, since payments also run through banks, gateways, authentication services and telecom networks). Flipkart has open-sourced Phantom, a proxy for calling distributed services "inspired by Twitter Finagle clients" and built on Netflix Hystrix, used for "several hundred million API calls"; Varadhi, its REST message bus, in production for about ten years "sending billions of messages daily"; and a Kafka client with priority levels, where each level gets reserved consuming capacity (2018).
%% title: A double tap and an unknown outcome, resolved without a double charge sequenceDiagram autonumber participant A as Rohan's app participant C as Checkout participant P as Payments participant G as Gateway participant R as Reconciler participant I as Inventory A->>C: Pay, key k1 C->>P: Charge order o77, key k1 P->>G: UPI collect for o77 G-->>P: No answer in 30 s P->>C: o77 PAYMENT_UNKNOWN C->>I: Extend hold k1 while money is in flight A->>C: Pay again, key k1 C-->>A: Payment in progress, no new charge R->>G: Status of o77? G-->>R: Success R->>C: o77 paid C->>C: CONFIRMED and outbox row, one transaction C->>I: Hold k1 becomes a sale
- The order is a state machine.
PENDING_PAYMENT → PAYMENT_UNKNOWN or PAYMENT_FAILED or CONFIRMED → PACKED → SHIPPED → DELIVERED; each transition is a compare-and-set on the order's version, so two workers can't both confirm it. - A timeout is unknown, not failed. It's never retried as a new charge; it's resolved by status polling, then by the gateway's settlement file the next day.
- Steer to the method that works. Order payment options by live success rate. A method with a 12–15% higher success rate, as Flipkart's Gaurav Arora reported for biometric authentication, is capacity: every failure is a retry that comes back.
- Bulkheads per partner. Each bank and gateway gets its own pool and concurrency limit, the Hystrix pattern Phantom was built on; a slow bank fails fast in its own pool and can't starve the others.
- Priority on the bus. Order confirmations, payment results and stock releases ride a high-priority lane; analytics and emails a low one, which waits when consumers fall behind.
# checkout/pay.py
"""
Paying for an order (illustrative). A second tap is a lookup by key; an
unanswered call is UNKNOWN, never retried as a new charge; a confirmed order
and its outbox event commit in one transaction.
"""
GATEWAY_TIMEOUT_S = 30
def pay(db, payments, inventory, order_id: str, key: str, method: str) -> str:
attempt = db.get_payment_attempt(key)
if attempt:
return attempt.state # second tap: show the first attempt
db.insert_payment_attempt(key, order_id, method, state="STARTED")
inventory.extend_for_payment(key)
try:
result = payments.charge(order_id, key=key, method=method, timeout_s=GATEWAY_TIMEOUT_S)
except Timeout:
db.set_attempt_state(key, "UNKNOWN")
db.transition(order_id, "PENDING_PAYMENT", "PAYMENT_UNKNOWN")
return "UNKNOWN" # the reconciler takes it from here
return settle(db, inventory, order_id, key, result.status)
def settle(db, inventory, order_id: str, key: str, status: str) -> str:
with db.tx():
if status == "SUCCESS":
db.transition_any(order_id, ["PENDING_PAYMENT", "PAYMENT_UNKNOWN"], "CONFIRMED")
db.outbox_add("orders.confirmed", {"order": order_id}, priority=0) # same transaction
else:
db.transition_any(order_id, ["PENDING_PAYMENT", "PAYMENT_UNKNOWN"], "PAYMENT_FAILED")
db.set_attempt_state(key, status)
if status == "SUCCESS":
inventory.mark_sold(key)
else:
inventory.release(key)
return status
class Timeout(Exception):
passStock holds and payments
Q1/2 A UPI payment is pending when the 10-minute hold is about to expire. What happens?
Answer & explanation
Answer: B
Money in flight extends the hold. Unknown outcomes get resolved, not guessed.
Q2/2 The gateway doesn't answer for 30 seconds. Should checkout retry the charge?
Answer & explanation
Answer: C
A timed-out charge may have succeeded. Retrying as new risks a double charge; failing it risks taking money for nothing.
Deep Dive 5: Prices, Catalog and Search Under Load (Week 4 Concepts: Cache Invalidation, Multi-Tier Caching; Week 7: Search)#
"In 2014 some prices reverted to full rates for a few hours. Flipkart didn't say why, and I won't guess. But the class of bug is familiar: a price assembled from several sources and cached in several places, one of which falls back to a default. So two rules: one version of the truth at a time, and when in doubt, don't sell."
This builds on Week 4, Day 2 · Cache Invalidation, Week 4, Day 5 · Multi-Tier Caching and Week 7, Day 1 · Search Fundamentals.
The price that went back up
At midnight, 5 million products change price and thousands of bank offers start. Search, the product page, the cart and checkout each show a price. One cache node restarts at 12:03 empty; another loses the offer service. What should each page show, and what must checkout charge? Should the search index be rebuilt with the new prices? And what if a sale price was entered as ₹999 instead of ₹9,999?
One strong answer
- One versioned price book. Prices and offers for the phase are a staged version (Deep Dive 1). Every page reads the active version's entries; an empty cache node refills from that version, never from "the latest row" or a default.
- Fail closed. If a price or offer can't be read, show "price unavailable, try again" and no Buy button, never the list price or a stale offer. Hiding a button for 30 seconds costs little; a wrong price costs trust.
- Checkout re-prices from the source with the active version, and charges the price shown in the cart or lower; if the price went up since the cart (an offer ended), the customer sees the new total before paying.
- Don't rebuild the index for prices. The index holds what changes slowly (titles, attributes, ratings); price, offer and stock badges are joined at serve time from the price cache.
- Guardrails before the flip. A staged price more than, say, 70% below the product's 90-day low, or below the seller's cost, needs a second approval; the build fails otherwise. And every deal has a kill switch that pulls it from sale in seconds.
What is published: the 2014 email: some prices changed to non-discounted rates for a few hours, and "we realise that this breaks the trust our customers have put in us". Flipkart's engineers list search, recommendations, ads, pricing and inventory among the use cases on its Aerospike platform, which Aerospike's write-up of their talk says serves "consistent sub-millisecond reads, even during Big Billion Days peaks" (2025). Its catalogue is 150 million products across 80+ categories (Flipkart, 2024; Google Cloud, 2023). How Flipkart versions prices or builds its search index for the sale isn't published.
%% title: Where a price comes from, on every page
flowchart TD
PTR["Active version pointer<br/>v42 since 12:00:00"]:::hot
BOOK@{ icon: "sd:cache", label: "Price book v42<br/>price, offers, deal state" }
IDX@{ icon: "sd:index", label: "Search index<br/>titles, attributes, ratings" }
SERP["Search results<br/>index hits plus prices joined"]:::service
PDP["Product page<br/>price, offers, stock badge"]:::service
CHK["Checkout<br/>re-price from v42, charge shown or lower"]:::core
MISS["Price unknown<br/>no Buy button, retry"]:::bad
PTR ==> BOOK
IDX ==> SERP
BOOK ==> SERP
BOOK ==> PDP
BOOK ==> CHK
BOOK -.->|"miss or offers down"| MISS
class BOOK cache
class IDX store- Search splits slow and fast fields. Matching and ranking use the index, refreshed in minutes; price, offer, stock and delivery date come from caches for the top results only.
- Cache the head. A few thousand queries ("smart tv", "washing machine") carry much of the sale's search traffic; their first page of result ids is cached for a minute.
- Degrade relevance, not results. Under load, drop personalised re-ranking first and cap result depth.
- Three tiers of cache. App, edge and in-memory stores in each data centre, all keyed by version: a version change makes every key new at every tier, without a purge.
- Offers are part of the version, so the discount on the page and at payment come from the same book and can't disagree.
# pricing/quote.py
"""
Quoting a price for a page or for checkout (illustrative). Every read goes
through the active version; a missing entry or an unreachable offer service
fails closed (no Buy button), never falls back to list price. Checkout
re-prices and charges the cart's price or lower.
"""
from decimal import Decimal
class PriceUnavailable(Exception):
pass
def quote(cache, sku: str, card_bin: str | None = None) -> dict:
v = cache.active_version()
entry = cache.get(f"price:{v}:{sku}")
if entry is None:
raise PriceUnavailable(sku) # fail closed
price = Decimal(entry["price"])
offer = None
if card_bin:
offers = cache.get(f"offer:{v}:{sku}")
if offers is None:
raise PriceUnavailable(sku) # don't guess the offer either
offer = best_offer(offers, card_bin, price) # eligibility by card range, capped
final = price - (offer["discount"] if offer else 0)
return {"version": v, "price": price, "offer": offer, "final": final}
def checkout_price(cache, cart_line: dict, card_bin: str | None) -> Decimal:
now = quote(cache, cart_line["sku"], card_bin)
return min(now["final"], Decimal(cart_line["shown_final"])) # never more than shownDeep Dive 6: Delivery Promises That Survive the Surge (Week 10 Concepts: Capacity Planning; Week 3: Backpressure)#
"A delivery date is a promise about capacity: someone has to pick, pack, sort and drive this parcel by then. If the sale fills tonight's warehouse waves, the date on the page has to move before the customer orders, not after."
This builds on Week 10, Day 4 · Capacity Planning, Week 3, Day 3 · Backpressure and Flow Control and Week 10, Day 1 · SLIs, SLOs and SLAs.
Delivery by Tue, 13 Oct
Rohan's washing machine sits in a fulfilment centre near Kolkata that works in waves: the 12:30 wave can process 20,000 units, picking and packing take 60 minutes, and the truck towards Bhagalpur leaves at 13:30. At midnight three times the usual orders arrive. What happens to orders that don't fit in the 12:30 wave? Should the page's date change, and when? And if Bhagalpur orders and orders for a 20:30 truck compete for the 12:30 wave, who should get it?
One strong answer
- The wave is a stock of capacity. Each order reserves a unit of capacity in a wave at checkout, the same way it holds a unit of product. When the 12:30 wave is full, new orders "spill" to the next wave, miss the 13:30 truck and lose a day.
- The page's date follows the capacity. The promise table for each (fulfilment centre, pincode) is recomputed as waves fill: once the 12:30 wave is nearly full, the page starts showing 14 October to new viewers. Orders already placed keep 13 October, because their capacity is reserved.
- Reserve for the urgent truck. Don't fill the wave first-in-first-out. Keep part of the 12:30 wave for orders whose truck leaves at 13:30; orders for the 20:30 truck can wait for a later wave without losing anything.
- Plan capacity before the sale. Demand is forecast per centre and pincode weeks ahead and people, shifts and trucks planned from it; during the sale the engine allocates what was planned.
What is published: Flipkart's engineers wrote in 2026 that a Promise Engine "calculates the precise delivery date shown to a customer during checkout" from "available processing capacity across network and truck connection schedules". Fulfilment centres work in waves, each with a cut-off and a capacity (their example: a 12:30 cut-off with 20,000 units, and pick-to-dispatch times of 30 to 180 minutes), with operational constraints limiting a centre to 6–8 cut-offs a day. When a wave is full, orders "spill" to the next, and spilled orders "miss the truck and likely wait 24 hours for the next one". The engine typically allocates capacity first-in-first-out; Flipkart built a way to reserve capacity for orders with impending truck connections and, during sales such as BBD, when "demand massively exceeds capacity", for high-priority cohorts. Its Central Planning Platform forecasts demand down to pincode level (Flipkart Tech, 2026). Ahead of the 2026 sale, Flipkart said automation in Ekart's warehouses raised peak processing capacity by over 30% and that it added over 900 new delivery hubs (Flipkart, October 2026).
%% title: An order takes a slot in a wave, or the date moves
flowchart TD
ORD["Order line<br/>washing machine to 812001"]:::service
FCS@{ icon: "sd:warehouse", label: "Fulfilment centre<br/>holds the unit" }
WAVE{"12:30 wave has capacity<br/>for this truck?"}
RES["Reserve 1 unit of capacity<br/>urgent-truck share first"]:::hot
TRUCK@{ icon: "sd:truck", label: "13:30 truck<br/>towards Bhagalpur" }
SPILL["Next wave<br/>misses the truck, plus 1 day"]:::bad
TABLE@{ icon: "sd:cache", label: "Promise table<br/>FC × pincode, recomputed" }
ORD ==> FCS ==> WAVE
WAVE ==>|"yes"| RES ==> TRUCK
WAVE -->|"no"| SPILL
RES -.->|"capacity left"| TABLE
SPILL -.->|"new date for new viewers"| TABLE
class FCS store
class TRUCK external
class TABLE cache- Two promises, like two stocks. The page shows a cached date per (fulfilment centre, pincode), recomputed as capacity burns down; checkout reserves the slot. The order's date is the slot's date, not the page's.
- Promise = processing + transit. Transit is mostly fixed by truck schedules; processing is the variable, at each touch point (fulfilment centre, mother hub, delivery hub).
- Hold the slot with the stock. Both are made at checkout and released together if payment fails.
- Choose the warehouse by promise: the centre whose next feasible slot gives the earliest date, not the nearest one with a full wave.
- Under a surge, protect the cohort, show the truth to the rest. Reserve capacity for chosen cohorts and let other dates move out honestly: a later date before the order beats a broken one after it.
# promise/allocate.py
"""
Reserving capacity for an order (illustrative). Each fulfilment-centre wave has
a capacity and a cut-off; part of it is reserved for orders whose truck leaves
soon after the cut-off. If no feasible wave catches the truck, the promise
moves a day. The order keeps the date of the slot it reserved.
"""
from dataclasses import dataclass
@dataclass
class Wave:
cutoff: int # minutes after midnight
capacity: int
urgent_share: float # part kept for orders whose truck leaves soon after the cut-off
used_urgent: int = 0
used_general: int = 0
def take(w: Wave, urgent: bool) -> bool:
urgent_cap = int(w.capacity * w.urgent_share)
if urgent and w.used_urgent < urgent_cap:
w.used_urgent += 1
return True
if w.used_general < w.capacity - urgent_cap: # urgent orders may also use the general share
w.used_general += 1
return True
return False
def reserve(waves: list, now: int, p2d: int, truck_departs: int, transit_days: int, today: int) -> dict:
for w in sorted(waves, key=lambda w: w.cutoff):
if now + p2d > w.cutoff:
continue # can't be picked and packed by this cut-off
catches_truck = w.cutoff <= truck_departs
urgent = catches_truck and truck_departs - w.cutoff <= 120
if not take(w, urgent):
continue # full: spill to the next wave
ship_day = today if catches_truck else today + 1
return {"wave": w.cutoff, "deliver_by_day": ship_day + transit_days}
return {"wave": "tomorrow", "deliver_by_day": today + 1 + transit_days}Prices, search and promises
Q1/3 A cache node restarts empty and can't reach the offer service. What should the product page show?
Answer & explanation
Answer: C
Fail closed. A missing button for seconds is cheaper than a wrong price for hours.
Q2/3 The 12:30 wave is full. What happens to the date shown to a new visitor from Bhagalpur?
Answer & explanation
Answer: B
The page's date follows remaining capacity; orders already placed keep the slots they reserved.
Q3/3 Flipkart's engine typically allocates wave capacity first-in-first-out. Why reserve part of a wave for orders whose truck leaves soon?
Answer & explanation
Answer: B
Give the scarce early slot to the orders it actually speeds up.
Phase 5: Scaling and Edge Cases (5 minutes)#
"Walk me through the opening night from the platform's side, then the cases that bite."
Rohan's Night, From the Platform's Side#
- Weeks before · Forecast and rehearse
- Demand forecast per surface, warehouse and pincode; stateful clusters grown; load and chaos tests at the forecast
- Days before · Stage the sale
- Price book v42 built, checked and loaded everywhere; deal stock carved out; code freeze
- 11:00 pm · Warm up
- Sale pages on the CDN; search caches filled; apps download the sale's assets
- 12:00:00 am · Flip
- The active version moves to v42; apps refresh with jitter; Early Access opens to Plus and Black members
- 12:00:02 · Deals open
- The lottery window closes; tokens are issued
- 12:00 to 12:10 · Peak
- Orders reach ~1,200 a second; tier-3 widgets are shed; one slow bank's card flow is demoted behind UPI
- 12:10 · Holds expire
- Unpaid holds return; returned tokens admit the next people in line; deals flip to sold out on the CDN
- Morning · Settle and replan
- Unknown payments resolved from gateway files; the night's orders feed the next day's warehouse plan
Edge Cases#
Edge case 1: A deal priced ₹999 instead of ₹9,999
Problem: A typo in the staged price book makes a phone 90% cheaper than intended; at midnight it sells in seconds.
Solution: Build-time guardrails (Deep Dive 5), a per-deal kill switch that pauses it on the CDN in seconds, and a policy for orders already placed, decided before the sale, not at 12:05.
Edge case 2: A deal's stock count is wrong
Problem: A warehouse count finds 300 of a deal's 5,000 units missing after the deal opened.
Solution: Lower the deal's available count at once (never below what's held or sold); if holds exceed what's really there, the latest holds are released before payment with an apology, which is better than cancelling paid orders later.
Edge case 3: A fulfilment centre goes dark
Problem: A power failure stops one centre for six hours during the sale.
Solution: Set its wave capacity to zero, so new orders go to other centres or see a later date; re-plan its promised orders and tell customers whose date moves.
Scaling and edge cases
Q1/2 A staged deal price is 90% below the product's 90-day low. What should happen?
Answer & explanation
Answer: B
Guardrails before the flip, a kill switch after it, and a policy decided in advance.
Q2/2 A fulfilment centre loses power for six hours mid-sale. What does the promise engine do?
Answer & explanation
Answer: B
Capacity is data. Set it to what's real and the dates follow.
Phase 6: Monitoring and Operations#
"The 2014 email is effectively the list of alerts: errors on the site, stock-outs, cancellations, wrong prices. Add the partners and the promises."
Monitoring Dashboard#
Read plane
| Metric | Now |
|---|---|
| Concurrent users (forecast) | 6.1M (6.5M) |
| Edge requests / at services | 3.0M/s / 610k/s |
| Product page p99 at the edge | 210 ms |
| Price lookups failing closed | 0.002% |
| Tier-3 widgets shed | 35% |
Write plane
- Orders: 1,140/s; checkout p99: 420 ms
- Deal 4471: tokens 6,500 / 6,500, held 1,210, sold 3,640, in line 184,000
- Sold beyond stock: 0; stock invariant mismatches: 0
Partners and promises
- Payment success: UPI 96.1%, cards 91.8%, one issuer 62% (breaker open, option demoted)
- Payments UNKNOWN: 3,900 open, oldest 4 min
- Kolkata centre 12:30 wave: 88% full; page date for 812xxx moved to 14 Oct
SLOs for the Sale#
SLO 1: The store stays up
- Target: product and search pages p99 < 300 ms and success > 99.9% from 11:55 pm to 1:00 am
SLO 2: Stock is exact
- Target: zero units sold beyond stock; zero stock-invariant mismatches; zero orders cancelled for lack of stock
SLO 3: Money is exact
- Target: zero duplicate charges; every UNKNOWN payment resolved within 24 hours; price charged never above price shown
SLO 4: Promises hold
- Target: ≥ 95% of sale-week orders delivered by the promised date; dates shown on pages within one wave of real capacity
Reading the dashboard
Q1/3 "Sold beyond stock" is 0, but "orders cancelled for lack of stock" shows 40. What does that mean?
Answer & explanation
Answer: B
If every order has a hold, there's nothing to cancel for lack of stock.
Q2/3 UNKNOWN payments climb from 3,900 to 40,000 while UPI success holds steady. Where do you look?
Answer & explanation
Answer: B
Unknowns grow when status polling falls behind or a gateway stops answering; holds are being extended meanwhile.
Q3/3 A deal shows tokens 6,500 of 6,500 issued, sold 3,640, held 0, and 180,000 people in line. What's wrong?
Answer & explanation
Answer: B
Units are left with people waiting: returned tokens should admit the next in line.
Final Challenge: Day 1, a Month Before Diwali#
The interviewer leans back. "One more."
"Suppose Day 1 opens to everyone at midnight, and a lot goes wrong at once. Diwali is a month away, so every promised date matters. Tell me what you do."
- Forecast 10 million concurrent users (assumption), about 1.6× the 6.3 million reported for the 2026 opening hour
- Hourly deals; the first: 10,000 units at 12:00
- 12:03: one large issuer's card authentication slows to ~20 s
- 12:07: 1,200 products live with a 25% bank offer instead of 10%
- 1:00 am: a northern fulfilment centre down for six hours
What do you protect, and what do you degrade?
Decide the capacity and caching plan for 10 million, how the hourly deals are admitted, what checkout does about the slow issuer, how you correct the offer without breaking the prices people already saw, and what happens to orders and dates tied to the dark centre.
One strong answer
- Capacity: scale to the 10 million forecast with margin, stateful tiers first, days ahead; stage Day 1's price book as its own version; warm caches; shed tier 3 from the start of the peak.
- Deals: each hourly deal gets its own line, 13,000 tokens for 10,000 units, carved-out stock and a lottery window.
- The slow issuer: its breaker opens; checkout demotes its cards behind UPI; in-flight charges become UNKNOWN and are reconciled, with holds extended.
- The wrong offer: build v43 with the corrected offer and flip to it; orders already placed keep the price they were shown; carts priced under v42 are re-quoted, and the customer sees the new total before paying.
- The dark centre: set its wave capacity to zero; new orders route elsewhere or see later dates; customers whose date moves are told now, not on the old date.
Final challenge · Quest check
Q1/2 1,200 products went live with the wrong bank offer. How do you fix it?
Answer & explanation
Answer: B
Versions make corrections as clean as launches; "never charge more than shown" protects customers mid-checkout.
Q2/2 A 10,000-unit deal opens on Day 1. How many tokens, and why more than the units?
Answer & explanation
Answer: B
A little overbooking keeps units from going unsold; the stock hold still prevents overselling.
Interview Conclusion#
"Why not just lock the product row and let the database decide who gets the deal?"
"Because 30,000 people a second would queue on one row to be told no 98% of the time, and the fastest networks would win. The database should decide only among people who can actually succeed. The deal line answers the rest at the edge, fairly, in under two seconds."
"And why not put the whole site behind a waiting room for the first ten minutes?"
"Because the scarce thing is a few deals, not the store. Six million people are browsing 150 million products; queuing all of them to protect a few product pages turns a sale into an outage. Queue the deal, cache the store."
"The single most important decision?"
"Splitting every promise into a cached hint and a reserved commitment. The page can show 'In stock', ₹9,999 and 'Delivery by 13 Oct' from caches a few seconds old, because checkout holds the unit, re-prices and reserves the slot before taking money. Never accept an order against a number that was only true a moment ago."
Summary: Concepts Applied from 10-Week Course#
- Week 1: Data at scale
- Hot keys and skew — deal stock bounded by admission; sharded stock counters for hot non-deal products
- Rate limiting — tokens per deal, limits per customer, device and address
- Week 2: Failure-first design
- Idempotency — one key from checkout through stock hold and payment
- Timeouts and circuit breakers — UNKNOWN payments, per-bank breakers and bulkheads
- Week 3: Messaging and async processing
- Backpressure — a line per deal; priority lanes on the message bus
- Transactional outbox — confirmed orders reach the warehouse exactly when they commit
- Week 4: Caching
- Thundering herd — versioned sale switch instead of synchronised expiry; jittered refreshes
- Cache invalidation and multi-tier caching — version-keyed prices at app, edge and data-centre tiers
- Week 5: Consistency and coordination
- Strong consistency for stock holds and order transitions; hints everywhere else
- Week 7: Search systems
- Slow fields in the index, fast fields joined at serve time; head-query caching
- Week 10: Production readiness
- Capacity planning from forecasts, rehearsal and chaos testing; promises as capacity; sale-night SLOs
Why Flipkart Matters#
The peak is a date, and it's public
- The worst minute of the year is announced weeks in advance. The challenge is preparing exactly: forecasting, rehearsing, staging and switching, not reacting
Scarcity is the product
- A deal is worth something because there isn't enough. The system's job is to say "no" to almost everyone, quickly and fairly, and "yes" exactly as many times as there are units
The promise reaches into the physical world
- Stock sits on shelves, payments run through banks, and parcels ride trucks with fixed departures. Every number on the screen is backed by something outside the software that can run out
The failure is on record
- Flipkart's founders wrote down, the morning after, what went wrong in 2014: capacity, stock-outs, cancellations and prices
"Queue the deal, cache the store, and never accept an order against a number that was only true a moment ago."
Self-Assessment Checklist#
After studying this case study, you should be able to:
The opening and the deals:
- Plan capacity for a scheduled spike from forecasts, and rehearse it with load and chaos tests
- Switch a sale on by version instead of by expiry, and explain why that avoids a thundering herd
- Design a per-deal line with tokens, a lottery window, limits and an honest answer for everyone else
Stock, checkout and payments:
- Hold stock at checkout with expiring, idempotent holds, and keep available + held + sold = on hand
- Handle a double tap, a timed-out payment and a slow bank without a double charge or a lost order
- Use an outbox and priority lanes so confirmed orders reach the warehouse first
Prices, search and promises:
- Serve prices from versioned price books that fail closed, and charge no more than shown
- Keep prices out of the search index and join them at serve time
- Compute delivery dates from wave capacity and cut-offs, reserve slots at checkout, and protect urgent trucks
Sources#
Flipkart:
- Read
Flipkart — India Logs on for The Big Billion Days: Flipkart Records 6.3 Million Concurrent Users as the Festive Season Opens (6.3 million concurrent users in a minute; Early Access at midnight on 8 October; 287 million+ sessions; 6× a regular hour; Tier 3 and non-metro shares), 9 October 2026: https://stories.flipkart.com/announcement/india-logs-on-for-the-big-billion-days-flipkart-records-63-million-concurrent-users-as-the-festive-season-opens
- Book
Flipkart — Flipkart's Big Billion Days Returns for Its 13th Edition on October 9 (500 million registered customers; 150 million products; warehousing, processing capacity and delivery hubs added), 6 October 2026: https://stories.flipkart.com/announcement/flipkarts-big-billion-days-returns-for-its-13th-edition
- Read
Flipkart — With 33+ Crore User Visits, Flipkart's Big Billion Days 2024 Kicks Off India's Festive Season (150 million products across 80+ categories; 19,000+ pincodes), 28 September 2024: https://stories.flipkart.com/announcement/with-33-crore-user-visits-flipkart-s-big-billion-days-2024-kicks-off-india-s-festive-season
- Read
Flipkart — Millions served, lakhs empowered: Flipkart's festive supply chain at the heart of Bharat (BBD 2025: 7.3 million+ shipments in a day; 5,000+ a minute on peak days), December 2025: https://stories.flipkart.com/announcement/millions-served-lakhs-empowered-flipkart-s-festive-supply-chain-at-the-heart-of-bharat
- Blog
Anjani Kumar — The Race Against the Clock: Optimizing Supply Chain Capacity for Faster Deliveries, Part 1 (the Promise Engine; waves, cut-offs and spillage; 6–8 cut-offs a day; capacity reserved for urgent trucks and for cohorts during BBD; 4 million+ daily shipments), Flipkart Tech Blog, 15 April 2026: https://blog.flipkart.tech/the-race-against-the-clock-optimizing-supply-chain-capacity-for-faster-deliveries-part-1-01b867c400de
- Blog
Piyush Vyas — The Pulse of India's Supply Chain: How Flipkart Plans for 500 Million Customers, Part 1 (fulfilment centres, mother hubs, delivery hubs; the Central Planning Platform; pincode-level forecasts; 50% more 1-day deliveries), Flipkart Tech Blog, 5 May 2026: https://blog.flipkart.tech/the-pulse-of-indias-supply-chain-how-flipkart-plans-for-500-million-customers-part-1-50d241ca494f
- Read
Flipkart — Varadhi, a message bus with a REST interface (about ten years in production, billions of messages daily), GitHub: https://github.com/flipkart-incubator/varadhi
- Read
Flipkart — Priority Kafka Client (priority levels with reserved consuming capacity; first release October 2018), GitHub: https://github.com/flipkart-incubator/priority-kafka-client
- Book
Flipkart — Phantom (service proxies inspired by Finagle, built on Netflix Hystrix; several hundred million API calls), GitHub: https://github.com/flipkart-incubator/phantom
Partners and case studies:
- Blog
Sudhir Reddy (Flipkart) and Savitha Sethuram (Google) — Reshaping Flipkart's technological landscape with a mammoth cloud migration (6–8× BAU transaction volume; 7× in 2023; 60 TB vs 500 TB of messages a day; 1.25 million messages a second; scale tests to 5× BAU), Google Cloud Blog, 31 October 2023: https://cloud.google.com/blog/products/data-analytics/flipkarts-dx-journey-to-futureproof-its-platform
- Blog
Aditya Tiwari (Flipkart) and Savitha Sethuram (Google) — The Bigtable Advantage: How Flipkart's fStream platform gets the most out of every byte (6–8× BAU transaction volume; the platform scaled up 4× during the sale), Google Cloud Blog, 9 August 2024: https://cloud.google.com/blog/products/databases/how-flipkart-gets-the-most-out-of-every-byte-with-bigtable
- Read
Aerospike — Aerospike Database on Kubernetes Enabled 95 Million Transactions Per Second on E-commerce Platform for Festive Sale (Flipkart's in-house Aerospike platform, sustained during The Big Billion Days; the release does not name the year), 4 November 2024: https://aerospike.com/press-release/aerospike-database-on-kubernetes-enabled-95-million-transactions-per-second/
- Book
Aerospike — Inside Flipkart's journey to 90 million QPS: Scaling with Aerospike and Kubernetes (Aerospike's write-up of a talk by Aditya Goyal and Sahil Jain of Flipkart at the Aerospike Bangalore Summit: 50+ use cases; 200+ clusters; 90 million QPS in aggregate across three data centres; sub-millisecond reads), 4 November 2025: https://aerospike.com/blog/flipkart-journey-90-million-qps
- Talk
PingCAP — How Flipkart Scales Over 1M QPS with Zero Downtime Maintenance (a write-up of Flipkart's TiDB User Day India 2025 talk: 900 standalone MySQL clusters and their limits; adopting TiDB), 29 May 2025: https://www.pingcap.com/?p=27687
- Read
CNCF — Flipkart case study (chaos platform; 2,000+ experiments; 6+ misconfigurations found ahead of TBBD 2024; ~20% faster detection, ~25% faster resolution), June 2026: https://www.cncf.io/case-studies/flipkart/
Reporting:
- Read
Preeti Anand — How Flipkart handles Big Billion Days traffic (Gaurav Arora to Business Standard, September 2026: nearly 1,000× more payment requests at the festive peak; biometric-authenticated transactions with a 12–15% higher success rate than OTP-based ones), DQIndia, 8 October 2026: https://www.dqindia.com/data-and-ai/flipkart-big-billion-days-aerospike-real-time-infrastructure-12636211
- Read
Business Today (PTI) — Flipkart founders apologise for Big Billion Day glitches (the founders' email; 1.5 million shoppers; ₹600 crore in 10 hours), 7 October 2014: https://www.businesstoday.in/story/flipkart-mega-sale-sachin-bansal-binny-bansal/1/211159.html
- Read
International Business Times India — Flipkart apologizes to beleaguered patrons over Big Billion Day sale fiasco (the founders' email), October 2014: https://www.ibtimes.co.in/flipkart-apologizes-beleaguered-patrons-over-big-billion-day-sale-fiasco-promises-better-service-610761
- Read
GSMArena — Xiaomi sells 40,000 Redmi 1S units in India in 4.2 seconds (on Flipkart; more than 250,000 registered), 2 September 2014: https://www.gsmarena.com/xiaomi_sells_40000_redmi_1s_units_in_india_in_42_seconds-news-9528.php
Design note: the deal line, stock holds, checkout, payment handling, price books, search split, promise allocation code, thresholds, dashboards and every number labelled as an assumption or estimate are an illustrative design for the problem, not a description of Flipkart's internal systems beyond what Flipkart and its partners have published. Request rates, orders a second, taps on a deal, conversion and checkout ratios, cache hit rates, hold durations, token counts and the Day 1 brief are assumptions. Rohan Mandal is a fictional customer.
Further Reading#
Related systems to study:
- Read
IRCTC Tatkal (Bonus Problem 11 in this series): a 10:00 spike on one train's seats, with admission control
- Read
BookMyShow (Bonus Problem 16): a site-wide waiting room when every seat is the scarce thing
- Read
Razorpay (Bonus Problem 18): payments, unknown outcomes and reconciliation from the gateway's side
- Read
Dream11 (Bonus Problem 25): a scheduled spike, forecast-driven capacity, and sharded counters
Course material:
- Read
The Week 2 capstone, a flash sale for MegaMart, is a smaller version of Deep Dives 2 and 3
Foundational concepts (this course):
- Read
Week 1, Day 3 — Rate Limiting at Scale
- Read
Week 1, Day 4 — Hot Keys and Skew
- Read
Week 2, Day 1 — Timeout Hell
- Read
Week 2, Day 2 — Idempotency in Practice
- Read
Week 2, Day 3 — Circuit Breakers
- Read
Week 3, Day 2 — Transactional Outbox
- Read
Week 3, Day 3 — Backpressure and Flow Control
- Read
Week 4, Day 2 — Cache Invalidation
- Read
Week 4, Day 3 — Thundering Herd
- Read
Week 4, Day 5 — Multi-Tier Caching
- Read
Week 5, Day 1 — Consistency Models
- Read
Week 7, Day 1 — Search Fundamentals
- Read
Week 10, Day 1 — SLIs, SLOs and SLAs
- Read
Week 10, Day 4 — Capacity Planning
"Queue the deal, cache the store, and never accept an order against a number that was only true a moment ago."
this lesson in shorts · 4
play them all →One idea from this lesson in about a minute each, made for your phone.
- 0/7stages cleared
New lessons by email
New lessons, side quests and case files as they ship, at most once a week. No spam, unsubscribe anytime.
Discussion · …
Public: everyone reading this lesson sees comments. Be kind and keep it on the material.



