~learn.

Amazon Prime Day Checkout, the whole design.

A cart, checkout and order pipeline for ~5,000 orders and ~100,000 cart operations a second at the Prime Day peak (assumed), where the cart never refuses an add, a purchase id makes every order happen once, and customer cells keep any one failure to a small share of shoppers.

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

SQ-34 · Amazon Prime Day Checkout · design sheethimanshukukreja.in/learn/sheets/bonus-34-amazon-prime-day-checkout

At a glance#

Number Value What it drives
DynamoDB peak, all of Amazon 192 million req/s (Prime Day 2026) platform context, not our sizing
Items purchased 100,000+ a minute (Prime Day 2022) the anchor for order rates
Orders at peak (assumed) ~5,000 / s · design 20,000 placement and pipeline capacity
Cart operations at peak (assumed) ~100,000 / s · design 400,000 cart tables, cell count
Cart requests per checkout ≥10 (from the Dynamo paper's figures, 2007) cart traffic ≫ order traffic
Services per page 150+ (2007) targets at p99.9; ~14% of pages hit a slow call
Hottest deal counter (assumed) ~5,000 writes / s split into ~20 sub-counters
Cells (assumed) ~40 of up to 8M customers one cell failing ≈ 2.5% of customers

Architecture#

DiagramCart, checkout and orders in customer cells, on shared prices and stock
%% title: Cart, checkout and orders in customer cells, on shared prices and stock
flowchart TD
  APP@{ icon: "sd:phone", label: "App and website<br/>cart · checkout" }
  GW@{ icon: "sd:gateway", label: "Front door<br/>auth · priority · shed bots first" }
  RT@{ icon: "sd:router", label: "Cell router<br/>customer id to cell · map in memory" }
  CART["Cart service<br/>always writable · tagged lines"]:::core
  CHK["Checkout and order<br/>purchase id · one transaction"]:::hot
  CDB@{ icon: "sd:database", label: "Cell tables<br/>carts · orders · tokens · outbox" }
  PRICE@{ icon: "sd:cache", label: "Price and promo<br/>shuffle-sharded groups" }
  INV["Inventory<br/>per product and FC · sub-counters"]:::core
  Q@{ icon: "sd:queue", label: "Order queues per cell<br/>live · sideline" }
  ORCH["Order orchestrator<br/>authorise · release · compensate"]:::core
  PAY@{ icon: "sd:bank", label: "Payment processors<br/>authorise now, capture at ship" }
  FC@{ icon: "sd:warehouse", label: "Fulfilment centres<br/>one release per FC" }
  APP ==> GW ==> RT
  RT ==> CART
  RT ==> CHK
  CART --> CDB
  CHK --> CDB
  CHK --> PRICE
  CHK ==>|"reserve"| INV
  CDB -.->|"outbox"| Q
  Q ==> ORCH
  ORCH ==> PAY
  ORCH ==> FC
  class APP client
  class GW,RT edge
  class CDB store
  class PRICE cache
  class Q queue
  class PAY,FC external
DiagramZoom-in · cells and shuffle shards
%% title: Zoom-in · cells and shuffle shards
flowchart TD
  RT@{ icon: "sd:router", label: "Cell router<br/>static map · works without control plane" }
  C1["Cell 1<br/>full stack · own tables"]:::core
  C17["Cell 17<br/>new build lands here first"]:::hot
  C40["Cell 40<br/>full stack · own tables"]:::core
  SS@{ icon: "sd:hexagons", label: "Price service · 32 groups<br/>each cell gets its own 4" }
  CP@{ icon: "sd:dashboard", label: "Control plane<br/>deploy one cell at a time" }
  RT ==> C1
  RT ==> C17
  RT ==> C40
  C17 ==> SS
  C1 --> SS
  CP -.-> C17
  class RT edge
  class SS service
  class CP service

Key flows#

DiagramPlace your order pressed twice · one order
%% title: Place your order pressed twice · one order
sequenceDiagram
  autonumber
  participant A as App
  participant C as Checkout
  participant I as Inventory
  participant D as Cell tables
  A->>C: place order · purchase id p-7f3 · cart hash h1
  C->>D: seen p-7f3?
  D-->>C: no
  C->>I: reserve lines · key p-7f3
  I-->>C: reserved at two FCs
  C->>D: one transaction · token if absent · order o-88 · outbox row
  Note over A,C: response lost · app retries with jitter
  A->>C: place order · p-7f3 · h1
  C->>D: seen p-7f3?
  D-->>C: yes · o-88 · h1
  C-->>A: order placed · o-88
  alt same id, different cart hash
    C-->>A: parameter mismatch · review your cart
  end
DiagramAdd to cart when the cart key is slow · never refuse
%% title: Add to cart when the cart key is slow · never refuse
sequenceDiagram
  autonumber
  participant P as Phone
  participant S as Cart service
  participant T as Cart table
  participant L as Add-log
  P->>S: add headphones · tag phone 12
  S->>T: conditional update on cart version
  alt within latency budget
    T-->>S: written
  else partition unreachable
    S->>L: append add · same cell, other partition
    L-->>S: durable
  end
  S-->>P: Added to Cart
  Note over S,T: next read merges the add-log · removes name observed tags
DiagramPayment processor times out · unknown, not failed
%% title: Payment processor times out · unknown, not failed
sequenceDiagram
  autonumber
  participant Q as Live queue
  participant O as Orchestrator
  participant P as Processor
  participant F as Fulfilment centre
  Q->>O: OrderPlaced o-88
  O->>P: authorise · key o-88
  P-->>O: no answer before timeout
  Note over O: unknown · retry later, same key, backoff and jitter
  O->>P: authorise · key o-88
  P-->>O: authorised
  O->>F: release · key o-88 and FC
  Note over O,F: a second copy of the release is ignored

Data model#

DiagramCore entities
%% title: Core entities
classDiagram
  class Cart {
    +customerId : string · partition key
    +version : int · conditional writes
    +lines : map of tag to line
    +removed : map of tag to time · tombstones
  }
  class CartLine {
    +tag : device and counter
    +sku : string
    +qty : int · merge takes the larger
  }
  class PurchaseToken {
    +key : customer and purchase id
    +orderId : string
    +hash : lines, total, address
  }
  class Order {
    +orderId : string
    +state : PLACED to RELEASED
    +fcs : list
    +reservationKey : customer and purchase id
  }
  class Reservation {
    +key : customer and purchase id
    +fc : string
    +subCounter : string
    +state : RESERVED or RELEASED
  }
  class StockRow {
    +product : string
    +fc : string
    +onHand : int
    +reserved : int
    +allocated : int
  }
  Cart "1" --> "many" CartLine : holds
  PurchaseToken "1" --> "1" Order : names
  Order "1" --> "1" Reservation : holds
  Reservation "many" --> "1" StockRow : draws from

Lifecycle#

DiagramAn order, from placed to shipped
%% title: An order, from placed to shipped
stateDiagram-v2
  [*] --> PLACED: token, order and outbox committed
  PLACED --> AUTHORISED: card authorised
  PLACED --> PLACED: processor timeout, retry same key
  PLACED --> PAYMENT_PENDING_CUSTOMER: declined for good
  PAYMENT_PENDING_CUSTOMER --> AUTHORISED: new method authorised
  PAYMENT_PENDING_CUSTOMER --> CANCELLED: deadline passed, stock released
  AUTHORISED --> RELEASED: one release per FC
  RELEASED --> SHIPPED: left the FC, charge captured
  SHIPPED --> [*]
  CANCELLED --> [*]
  class SHIPPED ok
  class CANCELLED bad
  class PAYMENT_PENDING_CUSTOMER queue

Decisions#

Decision Chose Over Because
Cart conflicts Tagged lines, observed removes Union or last write wins keeps adds and removes
Cart write path Conditional write + add-log Error when unreachable an add is never refused
Duplicate orders Purchase id from the page Id per click double press = one intent
Token and order One transaction Write, then record crash leaves all or none
When to take stock At placement At add to cart carts don't hold stock
Hot deal Sub-counters One counter row one item can't be split
Blast radius Customer cells, shuffle shards One big fleet bugs reach every replica

When it breaks#

Failure What users see Contain it with
Bad checkout build errors in one cell cell-by-cell deploy · roll back first
Cart partition slow nothing; add accepted add-log fallback · merge on read
Lost response on place order spinner, then same order purchase id · equivalent response
Payment processor down order placed, email later unknown state · retry same key · sideline backlog
Zone lost at peak a few retried requests provisioned for two zones · 2-of-3 write quorum
Bots on one cart or deal throttled bots per-customer limits · shed bots first

Say it in the interview#