Design Backend for an App to Distribute 6 Million Free Burgers in One Hour — System Design Interview Practice
Design a system to handle flash distribution of limited items with high concurrency and fairness. Work through the requirements, architecture trade-offs, and an interactive design review.
Concepts and architecture decisions to consider
- flash saleConcept to explore
- concurrencyConcept to explore
- high trafficConcept to explore
- inventoryConcept to explore
- scalabilityConcept to explore
Interview prompt
Design a fair, abuse-resistant flash-distribution system that gives away six million vouchers or burgers in one hour while keeping inventory, eligibility, and redemption correct under extreme concurrency.
- Define eligibility, fairness, admission, reservation, redemption, expiry, and inventory ledgers with an idempotency key per user/campaign.
- Absorb millions of arrivals with waiting-room admission, bot defenses, quotas, randomized or timestamped ordering, and bounded queues.
- Keep the hot path small and asynchronous; make inventory allocation atomic and reconcile abandoned reservations and store redemptions durably.
- Explain regional capacity, retries, fraud, queue failure, campaign pause/resume, observability, and a transparent sold-out mode.
Requirements and scale assumptions
- Admit eligible users, issue a fair position/token, reserve an item, confirm redemption, and expose queue and reservation status.
- Enforce one-per-user/device limits, campaign windows, inventory variants, expiration, cancellation, and store/fulfillment handoff.
- Make retries safe, prevent oversell and double redemption, audit decisions, and recover from queue, payment-free fulfillment, or region failures.
- Absorb at least 10 million arrival attempts in an hour while keeping allocation correctness and a bounded admission response.
- Distribute six million successful allocations without a single hot key or unbounded synchronous work.
- Do not lose committed state; make retries and duplicate events safe.
- Degrade safely when downstream workers, caches, or external dependencies fail.
- 10M arrivals/hour and 6M available items across thousands of stores
- Partition by the primary tenant, user, item, or geographic key and isolate hot partitions.
- Keep serving state bounded; retain raw events or durable records for replay and auditing.
- Peak scale: 10M arrivals/hour; 6M inventory — Capacity assumption that drives partitioning and backpressure.
- Latency target: admission absorbs 10M/hour; no oversell — User-facing budget for the primary request or read path.
- Durable boundary: Committed before async — The campaign inventory and redemption ledger are authoritative; queue positions and counters are derived state.
- Async boundary: At-least-once workers — Keep Queue-based request handling, Redis for atomic inventory decrements, Rate limiting per user off the synchronous path.
Key entities
- RedemptionIntentintentId, customerId, offerId, status, version, idempotencyKey
Idempotent backend reservation intent with an explicit lifecycle.
- InventoryClaimclaimId, itemId, quantity, expiresAt, status, version
Short-lived conditional claim that protects scarce availability.
- PaymentAttemptattemptId, intentId, provider, requestHash, status, providerRef
Retry-safe payment attempt with unknown-outcome reconciliation.
- FulfillmentStateintentId, stage, owner, lastEventId, status, updatedAt
Durable downstream reservation fulfillment progress.
Data flow
- 1. Search and price current candidatesThe backend query path combines catalog, eligibility, price, and current availability without treating a stale index as a final claim.
- 2. Claim scarce inventoryThe backend service creates a short-lived conditional claim keyed by item and request idempotency before charging or confirming.
- 3. Confirm the redemption safelyPayment, policy, and inventory transitions are versioned; unknown provider outcomes are reconciled before retry.
- 4. Publish fulfillment workThe committed backend intent emits an event for partner, packing, delivery, or campaign dispatch workers.
- 5. Expire, cancel, and reconcileExpiry and cancellation release claims idempotently while reconciliation compares internal state with external providers or partners.
Deep dives and trade-offs
- Inventory claims and oversell controlUse conditional writes or a serialized inventory partition for backend scarce capacity. Give holds an expiry and reaper, but never release a confirmed claim from a stale worker. Separate searchable availability from the authoritative claim path.
- Payment and unknown outcomesBind backend payment attempts to the intent and request hash, not just the customer session. Treat timeout as unknown, query or reconcile provider state, and avoid a blind second charge. Keep sensitive payment tokens outside business records and logs.
- Fulfillment and partner recoveryPublish backend events after commit, consume at least once, and track per-stage progress. Use reconciliation against partner feeds or delivery evidence instead of assuming a callback arrives. Expose pending and expired status so the client can explain what happened.
- Reservation versus oversell tolerancePrefer a short-lived backend claim for scarce inventory and state the consistency scope explicitly. A cache or search index cannot safely decrement the final room count.
- Synchronous checkout versus asynchronous fulfillmentCommit the reservation intent synchronously and move partner or delivery work behind events. Waiting for downstream fulfillment makes retries ambiguous and increases checkout tail latency.
- Precompute availability versus compute at read timePrecompute common search dimensions but validate the final claim against authoritative state. Serving only a precomputed backend availability view creates oversell or stale-price failures.