An online shop end to end: which parts may be stale and which must be exact, how not to sell the last unit twice, what a cart owns, how a checkout across four services fails safely, how stale a price may be, and what a million simultaneous buyers do to one row. Tap any for a plain-language card.
A hundred million people look at products. A few percent buy. These two groups need completely different guarantees.
The numbers show why. Page views per second are in the hundreds of thousands, orders per second in the tens; but a promotion multiplies both, and the buy path must hold under the peak. Size the browse path for reads and the buy path for correctness and for the spike.
100 million daily visitors viewing 15 pages each is 17,000 page views a second; at 3% conversion that is about 35 orders a second. A 10× sale peak means 174,000 views and 350 orders a second.
Page views per second (average / peak)17.4K / 174K
Orders per second (average / peak)35 / 347
Page views per order500
100 million visitors a day make 17.4K page views and 35 orders a second (174K and 347 at a 10× peak). That is 500 reads for every order: serve reads from caches, and keep the few writes strongly consistent.
Arithmetic with illustrative inputs. Orders are low volume but each touches inventory, payment and fulfilment, so cost per order is high.
Ten left. Two hundred people clicking.
The page says “10 in stock”. Two hundred people click Buy together. How many orders do you accept?
For very hot items the single row becomes the bottleneck (see the flash-sale scene), and some systems pre-split the stock into several counters or hold it in Redis with an atomic DECR, reconciling with the database afterwards. For everything else, let the database enforce the invariant (qty >= 0 as a constraint) so no code path can break it.
10 units, 200 buyers inside one second and 20 ms between read and write: the naive code accepts 45 orders (35 too many). The atomic decrement accepts exactly 10.
Orders accepted
45 (stock 10)
Orders accepted45
Units you promised but do not have35
200 buyers against 10 units: the read-check-write code accepted 45 orders, 35 more than exist, because many buyers read the same stock value before any write landed.
Seeded simulation: buyers arrive uniformly within one second; the naive path reads the current stock and writes stock − 1 after the chosen delay.
Who owns it while it is in a cart?
Items in a cart are not bought yet. If they are reserved, abandoned carts hide stock from real buyers. If they are not, checkout can fail.
A reservation must expire automatically (a ), or abandoned carts lock stock forever. How much stock is tied up follows : holds created per minute × the average time a hold lasts. The longer the hold and the lower the conversion, the more stock is sitting idle in carts that will never buy.
500 holds a minute, 20% of which buy after 5 minutes and the rest expire after 15: on average 6,500 units are tied up and 92% of them will never be bought. A 5,000-unit item looks sold out while most of it is idle.
Average hold lasts13.0 min
Units tied up at any moment6.5K
Of which will never be bought92% (6K)
Share of the stock idle in expiring holds100%
500 holds a minute lasting 13 minutes on average tie up 6.5K units at any moment, 6K of them in carts that will expire unsold. Against 5,000 in stock that is 100%: the item shows as sold out while it sits idle in carts. Shorter holds release stock faster.
Little's law: average units on hold = hold arrival rate × average hold time, where purchasers release at their buying time and the rest at expiry.
Four systems, no shared transaction
Checkout reserves stock, charges the card, creates the order and sends a confirmation. Each is a different service. The third one fails.
Without compensation, every failure leaves debris: stock reserved forever for an order that does not exist, a customer charged with no order. Sagas can be orchestrated (one coordinator drives the steps and compensations and records progress) or choreographed by events. Either way each step and each compensation must be , because they will be retried. Run 10,000 checkouts.
With failure rates of 2%, 3%, 1% and 2% across the four steps, about 7.6% of checkouts fail. Without compensation that leaves about 570 orders with stock locked and 105 customers charged with no order; with compensation, none.
Completed orders
9,240
Stock reserved for no order
571
Charged but no order
105
Failures cleanly rolled back
0
Checkouts that failed7.6%
Corrupt states left behind676
Of 10,000 checkouts, 760 failed. With no compensation 571 left stock reserved for nothing and 105 customers were charged without an order.
Seeded simulation. A step-3 failure leaves stock reserved and the card charged; a step-2 failure leaves stock reserved. A failure of the confirmation step is not compensated here since it does not affect the order.
The price you saw and the price you pay
The product page, served from a cache, says 499. At checkout it says 549. Whose fault is that?
How stale? If a price changes at random (several times a day for a dynamically priced item) and copies live for a TTL, the share of views that show an old price grows with the TTL times the change rate. Cut staleness by shortening the TTL, by invalidating the cache when a price changes (the price service publishes an event), or by fetching the price separately from the cached page. Try your own rates.
An item whose price changes 24 times a day, cached for 10 minutes, shows an old price on 7.9% of views. With a 1-minute TTL it is 0.8%.
Views showing an out-of-date price7.89%
Out-of-date views per hour1,578
With 24 price changes a day and a 10-minute cache, 7.9% of views show a stale price: about 1,578 wrong prices an hour for 20,000 views. Checkout must still re-read the real price.
Exact for Poisson price changes: stale share = 1 − (1 − e^(−λT)) ÷ (λT), with λ the change rate per minute and T the TTL.
Pick the mechanism
Six problems, four mechanisms.
Each problem has one mechanism that fits best. Each answer is worth 20 XP.
Match the symptom: double-sold stock, idle cart stock, half-finished checkouts, or a crowd on one row?
Question 1 / 6
Two customers both receive the last unit of an item.
Tip: press 1 to 4 to answer, Enter for the next one.
Who lives by this
Shops at scale.
Published ideas from large commerce systems.
The always-writable cartAmazon Dynamo (2007)
a highly available key-value store
The Dynamo paper described a store built so that adding to the shopping cart would always succeed, even during failures, accepting eventual consistency and merging conflicting versions later, because a rejected add-to-cart is lost revenue.
Isolating tenantsShopify pods
sharded, isolated stacks
Shopify has described splitting its platform into isolated 'pods', each serving a set of shops with its own database and services, so that a flash sale or a failure at one merchant cannot take down the others.
Waiting roomsVirtual queues
Queue-it and similar services
Ticketing and retail sites use virtual waiting-room services that put visitors in a fair queue and admit them at a rate the checkout can sustain when demand spikes far above capacity.
Safe retries for paymentsStripe idempotency keys
idempotent requests
Stripe's API lets clients send an idempotency key with a charge so that a retried request after a timeout returns the original result instead of charging twice, the same idea checkout sagas need at every step.
Interview angle
What interviewers actually ask.
Six questions, each worth 20 XP. Strong answers separate the browse path from the buy path.
Say what may be stale, what must be exact and how failure is undone.
Question 1 / 6
How do the browse and buy paths differ?
Tip: press 1 to 3 to answer, Enter for the next one.
Break it
The new console goes on sale at noon. 10,000 units. A million people click Buy in the same second. All of them update the same stock row.
The cure is to control admission before the hot row. A waiting room (a virtual queue) lets in only as many shoppers as the checkout can serve, assigns places fairly (often randomly among those who arrived in the first minute), and shows a position and an honest estimate. Combine with the techniques already seen: stock held in a fast counter, pre-split across several rows, and of everything not essential. The sale runs at the speed the system can sustain, not the speed of the crowd.
1,000,000 buyers, 10,000 units, a hot row that handles 2,000 updates a second and a 10-second timeout: without admission control 980,000 requests time out (98%). With a waiting room, nobody times out, all the units sell and the line drains in 5 seconds of row work.
Orders that go through10K
Requests that time out980K
Share of buyers who see an error98%
Waiting calmly in the queue0
1M simultaneous clicks on a row that handles 2,000 a second with a 10 s timeout: only 20K requests are served in time, 980K (98%) time out and will retry, making it worse.
Fluid arithmetic: requests handled within the timeout = update rate × timeout. Real retries amplify the overload; the waiting room keeps the load inside capacity.