~learn.

Flipkart Big Billion Days, the whole design.

A national sale that opens to millions in the same minute, by switching prices and deals on as a pre-staged version, queuing only each deal's button with tokens at the edge, holding stock and delivery slots at checkout before taking money, and treating every unanswered payment as unknown until it is resolved.

7 diagrams · 8 sections · revise in ~10 min · Download PDF prints it in the light theme, diagrams included

SQ-27 · Flipkart Big Billion Days · design sheethimanshukukreja.in/learn/sheets/bonus-27-flipkart-big-billion-days

At a glance#

Number Value What it drives
Opening concurrency 6.3M users in a minute (2026) forecast and pre-scale; warm every cache before midnight
Transactions 6–8× business-as-usual size each tier by its own multiplier
Payment requests ~1,000× normal at the peak per-bank breakers; unknown outcomes reconciled
Requests ~3.2M / s at the edge · ~630k / s at services (est.) the read plane is served from the CDN and memory
Orders ~1,200 / s at the opening · design 5,000 (est.) ~3,600 stock holds a second
One hot deal 5,000 units · ~30,000 taps / s (est.) a line per deal; ~6,500 tokens
Shipments ~4M a day · 7.3M on the BBD 2025 peak day delivery dates from wave capacity
Database load 95M transactions / s during TBBD (Aerospike, Nov 2024) page lookups fan out; keep them in memory

Architecture#

DiagramA cached read plane, a small exact write plane
%% title: A cached read plane, a small exact write plane
flowchart TD
  APP@{ icon: "sd:phone", label: "Flipkart app and site<br/>browse · deals · checkout" } ==> CDN@{ icon: "sd:cdn", label: "CDN<br/>pages · deal states" }
  APP ==> GW@{ icon: "sd:gateway", label: "Gateway<br/>tiers · shedding · bot checks" }
  GW --> SRCH@{ icon: "sd:search", label: "Search<br/>index without prices" }
  GW --> PRICE@{ icon: "sd:cache", label: "Price book cache<br/>versioned · fail closed" }
  GW ==>|"Buy now"| ADM["Deal line<br/>tokens · lottery window"]:::hot
  ADM ==> INV["Stock holds<br/>expiring · keyed by checkout"]:::hot
  INV --> KV@{ icon: "sd:database", label: "Stock and order stores<br/>strongly consistent" }
  INV ==> CHK["Checkout and orders<br/>idempotent · outbox"]:::core
  CHK ==> PAY["Payments<br/>breakers · unknown outcomes"]:::core
  PAY ==> BANK@{ icon: "sd:bank", label: "Gateways and banks<br/>UPI · cards · EMI" }
  CHK -->|"reserve a slot"| PROM["Promise engine<br/>wave capacity · cut-offs"]:::core
  CHK ==> BUS@{ icon: "sd:queue", label: "Message bus<br/>priority lanes" }
  BUS ==> FC@{ icon: "sd:warehouse", label: "Fulfilment centres<br/>waves · trucks" }
  class APP client
  class CDN,GW edge
  class SRCH service
  class PRICE cache
  class KV store
  class BANK external
  class BUS queue
  class FC store
DiagramOne tap on a deal: answered at the edge unless it can succeed
%% title: One tap on a deal: answered at the edge unless it can succeed
flowchart TD
  TAP["Buy now on a deal<br/>customer · device · address"]:::service
  BOT{"Limits and bot checks pass?"}
  WIN{"Inside the 2 s lottery window?"}
  LOT["Random order among<br/>the first taps"]:::service
  TOK{"Tokens issued below<br/>units × 1.3?"}
  GIVE@{ icon: "sd:ticket", label: "Signed token<br/>3 minutes · one per customer" }
  WAIT["In line<br/>position shown"]:::service
  SOLD@{ icon: "sd:cdn", label: "Sold out<br/>published once" }
  TAP ==> BOT
  BOT -->|"no"| WAIT
  BOT ==>|"yes"| WIN
  WIN -->|"yes"| LOT ==> TOK
  WIN -->|"no"| TOK
  TOK ==>|"yes"| GIVE
  TOK -->|"no"| WAIT
  WAIT -.->|"tokens all used"| SOLD
  class GIVE ok
  class SOLD edge

Key flows#

DiagramRohan buys the deal phone at 12:00:01
%% title: Rohan buys the deal phone at 12:00:01
sequenceDiagram
  autonumber
  participant A as Rohan's app
  participant E as Edge and deal line
  participant C as Checkout
  participant I as Inventory
  participant P as Payments
  participant B as Bank
  A->>E: Buy now, deal 4471
  E-->>A: Token, valid 3 minutes
  A->>C: Checkout with token, 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
  C-->>A: Order confirmed
  Note over C: confirmed state and outbox row commit together · bus to the warehouse
DiagramA double tap and an unknown payment, without a double charge
%% title: A double tap and an unknown payment, without a double charge
sequenceDiagram
  autonumber
  participant A as 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 o77, key k1
  P->>G: UPI collect
  G-->>P: No answer in 30 s
  P->>C: o77 PAYMENT_UNKNOWN
  C->>I: Extend hold k1, within a cap
  A->>C: Pay again, key k1
  C-->>A: In progress · no new charge
  R->>G: Status of o77?
  alt success
    G-->>R: Success
    R->>C: CONFIRMED · outbox row
    C->>I: Hold becomes a sale
  else failed
    G-->>R: Failed
    R->>C: PAYMENT_FAILED
    C->>I: Release the unit
  end
DiagramThe midnight switch, built ahead and flipped once
%% title: The midnight switch, built ahead and flipped once
sequenceDiagram
  autonumber
  participant O as Sale ops
  participant B as Version builder
  participant K as Cache nodes
  participant D as CDN
  participant A as Apps
  O->>B: Stage v42 · prices, offers, deal states
  B->>B: Guardrails · second approval
  B->>K: Load v42 under new keys
  B->>D: Push v42 pages, not live
  Note over K,D: v41 still live until 12:00:00
  B->>K: 12:00:00 · active version v41 to v42
  A->>D: Refresh with 0 to 3 s jitter
  D-->>A: Sale pages v42

Data model#

DiagramCore entities
%% title: Core entities
classDiagram
  class PriceBook {
    +version : string
    +activeFrom : time
    +prices : sku to price
    +offers : sku to bank offers
  }
  class Deal {
    +dealId : string
    +units : int
    +tokenCap : units x 1.3
    +startsAt : time
  }
  class Token {
    +customerId : string
    +dealId : string
    +expires : 3 minutes
  }
  class StockCounter {
    +sku : string
    +fc : string
    +available : int
    +held : int
    +sold : int
  }
  class Hold {
    +key : checkout id
    +state : HELD / PAYING / SOLD / RELEASED
    +expires : 10 minutes, capped while paying
  }
  class Order {
    +orderId : string
    +key : idempotency key
    +state : string
    +promisedDate : date
  }
  class WaveSlot {
    +fc : string
    +cutoff : time
    +capacity : int
    +urgentShare : float
  }
  Deal "1" --> "many" Token : admits
  Token "1" --> "0..1" Hold : lets you try
  StockCounter "1" --> "many" Hold : held from
  Order "1" --> "1" Hold : key k1
  Order "many" --> "1" WaveSlot : reserves
  PriceBook "1" --> "many" Order : priced by

Lifecycle#

DiagramAn order, from checkout to delivery
%% title: An order, from checkout to delivery
stateDiagram-v2
  [*] --> PENDING_PAYMENT: checkout · stock and slot held
  PENDING_PAYMENT --> CONFIRMED: paid
  PENDING_PAYMENT --> PAYMENT_UNKNOWN: no answer
  PENDING_PAYMENT --> PAYMENT_FAILED: declined or hold expired
  PAYMENT_UNKNOWN --> CONFIRMED: reconciler · success
  PAYMENT_UNKNOWN --> PAYMENT_FAILED: reconciler · failed
  CONFIRMED --> PACKED: picked in its wave
  PACKED --> SHIPPED: on the truck
  SHIPPED --> DELIVERED: by the promised date
  DELIVERED --> [*]
  PAYMENT_FAILED --> [*]
  class DELIVERED ok
  class PAYMENT_FAILED bad
  class PAYMENT_UNKNOWN queue

Decisions#

Decision Chose Over Because
Midnight switch Stage a new version, flip one pointer Cached prices expiring at midnight synchronised expiry is a thundering herd
Deal access A line per deal, tokens ≈ units × 1.3 First-come at the database 30,000 writes / s on one key, ~98% fail
Queue scope Only the deal's Buy button A site-wide waiting room the scarce thing is one product, not the store
Fairness 2 s lottery window, then first-come Pure arrival order fibre beats 4G by hundreds of ms
When to take stock Expiring hold at checkout start At add-to-cart or at payment carts lock unbought stock; payment-time takes cause cancellations
Payment timeouts UNKNOWN, reconciled by status Retry as a new charge a timed-out charge may have succeeded
Missing price Fail closed, no Buy button Fall back to list price a wrong price costs trust
Delivery date Reserve wave capacity, urgent trucks first FIFO, static dates bumped next-truck orders lose a day

When it breaks#

Failure What users see Contain it with
Caches cold or expiring at midnight slow or failing pages at 12:00 warm ahead; versioned flip; app jitter; shed tier 3
One bank slows at the peak card payments hang per-bank breaker; demote its cards behind UPI; bulkheads
Gateway stops answering "payment pending" UNKNOWN state; extended holds; reconciler; settlement file
Deal priced ₹999, not ₹9,999 a too-good deal build guardrails; per-deal kill switch; policy decided in advance
A price cache node comes back empty "price unavailable" briefly refill from the active version; never a default
A fulfilment centre goes dark later dates; some orders re-planned wave capacity to zero; route to other centres; tell customers early

Say it in the interview#