Skip to content
Reliable Data Engineering
Practice problem hard cdcstreamingconsistencyservingreconciliation
Practise with timer, notes and rubric

Design Real-Time Inventory Availability for Omnichannel Retail

Problem

A retailer sells through 2,000 stores, 15 warehouses, a website and apps. Customers see “in stock at your store” and “delivery by tomorrow”. Inventory changes come from POS sales, returns, receiving, transfers, online orders and periodic stock counts. Design the data system that provides accurate, low-latency availability for the website and apps, prevents overselling, and feeds analytics (replenishment, shrink).


Clarifying questions

QuestionAssumed answer
Scale?200k SKUs × 2,015 locations ≈ 400M SKU-locations (≈ 50M with non-zero stock or activity)
Change rate?~30M inventory events/day, peak 5k events/s (Black Friday 25k/s)
Read rate?Availability lookups: 50k QPS peak from web/apps
Freshness?Availability within ~5 seconds of a sale or reservation
Correctness?Never promise stock that doesn’t exist for online orders (oversell is costly); slight under-promising is acceptable
Systems of record?Store systems (POS, inventory), warehouse management system (WMS), order management system (OMS)

1. Requirements

Functional: current on-hand per SKU-location; available-to-promise (ATP) = on-hand − reserved − safety stock; reservations for online orders; the event history for analytics; reconciliation with periodic physical counts.

Non-functional: p99 read latency < 50 ms; ATP freshness < 5 s; no lost events; correct under retries and reordering; graceful degradation when a store system is offline.

2. Estimates

3. Architecture

flowchart LR
    subgraph SOR[Systems of record]
        POS[Store POS / inventory DBs]
        WMS[(Warehouse WMS)]
        OMS[(Order management)]
    end
    POS -- events / CDC --> K
    WMS -- CDC --> K
    OMS -- reservation events --> K
    K[(Kafka: inventory-events<br/>key = sku_id:location_id)]
    K --> SP["Stream processor (Flink)<br/>keyed state per SKU-location:<br/>on_hand, reserved, last_seq"]
    SP --> KV[(Availability store<br/>DynamoDB / Redis / Cassandra)]
    SP --> CH[(Changelog topic: availability-updates)]
    KV --> API[Availability API<br/>+ CDN/edge cache for PLPs]
    API --> WEB[Web / apps]
    K --> BR[(Bronze: all inventory events)]
    BR --> SV[(Silver: inventory ledger, daily snapshots)]
    SV --> AN[Replenishment, shrink analytics, ML]
    COUNT[Cycle counts / physical inventory] --> K
    SV --> REC[Reconciliation vs SOR snapshots]

4. Data model

Event (inventory ledger entry): event_id, sku_id, location_id, type (SALE, RETURN, RECEIPT, TRANSFER_OUT/IN, ADJUSTMENT, COUNT, RESERVE, RELEASE, FULFIL), quantity_delta, source_system, source_seq (per location monotonic sequence), event_ts.

State per SKU-location: on_hand, reserved, safety_stock, atp = max(0, on_hand - reserved - safety_stock), last_source_seq per source, updated_at.

The inventory is modelled as a ledger (append-only deltas) plus derived state, exactly like an accounting system. Counts set absolute values; everything else is a delta.

5. Deep dives

5.1 Ordering, duplicates and exactly-once state

5.2 Reservations and oversell prevention

5.3 Absolute counts vs deltas

5.4 Hot keys and peak events

5.5 Store systems offline

5.6 Reconciliation and analytics

6. Trade-offs

DecisionChoiceAlternative
State computationStream processor with keyed statePolling DBs (slow) or computing in the serving DB with triggers (couples systems)
Oversell preventionConditional writes at reservation timePurely eventual ATP (oversells at low stock)
ServingKV store + edge cacheLakehouse/warehouse (latency, concurrency)
Store accuracySafety stock buffersPromise raw on-hand (more cancellations)
Hot SKUsSub-bucket allocationSingle counter (contention)

7. Failure modes

8. What separates a senior answer

9. Follow-up questions

How do you show availability on a product listing page with 50 items × 30 nearby stores?

Batch the lookup (a multi-get of 1,500 keys) against the availability store or a precomputed per-region availability summary; cache at the edge for a few seconds; return coarse states (in stock / low / out) rather than exact counts, which also tolerates small staleness.

Analysts want hourly inventory positions for 2 years. How do you store it efficiently?

Don’t store hourly snapshots of 400M rows. Store the ledger (deltas) plus daily snapshots, and compute positions at any time as snapshot + Σ deltas since snapshot. Materialise only the aggregates analysts actually query (e.g. by category × region × day).


Self-assessment rubric