Ever been browsing Amazon and seen "Only 2 left in stock — order soon"? Or Flipkart pushing "Hurry, only 3 left!" in bright red?
Feels urgent. Feels real-time. But the number is often not exact, and that is usually intentional.
This article breaks down the system design tradeoff behind that experience: how large e-commerce platforms show stock counts to huge numbers of browsers while still guaranteeing that the last unit is not accidentally oversold.
The Core Problem
An e-commerce platform has two very different workloads hitting the same product:
• Millions of users browsing the product page • A much smaller number of users actually trying to buy it
Those two actions need different guarantees.
Browsing wants speed. Product pages should load in tens of milliseconds and scale under huge read traffic.
Checkout wants correctness. Once money is involved, the system must not oversell inventory even under concurrency and failures.
The Central Tradeoff
If every product page read went to the primary inventory database, the system would collapse under read load.
If every buyer trusted a stale cache, the platform would oversell.
So the architecture splits the problem in two:
• Discovery path: fast and approximate • Checkout path: slower and authoritative
That asymmetry is the whole design.
Layer 1: Product Discovery
When a user opens a product page, the request usually does not hit the source-of-truth inventory database directly.
Instead, the response is assembled from fast read infrastructure:
text1User Browser -> CDN Edge -> Product Page Service -> Redis / Memcache
The banner shown on the page is typically backed by an eventually consistent cache.
That means the number can be slightly stale. The product page may say "Only 3 left" even if the real stock is now 2, or 4, or already 0 for a brief window.
That is acceptable because browsing can tolerate approximation. The goal of this path is low latency and scale, not transactional precision.
Layer 2: Checkout
The moment the user clicks Buy Now, the system switches to a different path:
text1Checkout Service -> Lock / Conditional Write -> Primary Inventory Store
This path reads the authoritative stock, tries to reserve or decrement it atomically, and either confirms the order or rejects it.
This is why users sometimes see a product page claiming low stock but still get "Sorry, this item just sold out" during checkout.
The page trusted the cache. Checkout did not.
Why Eventual Consistency Is Fine Here
Strong consistency for every read is too expensive at e-commerce browse scale.
Eventual consistency works because the business risk is asymmetric:
• Stale read on browse: acceptable • Stale read on payment: unacceptable
The platform is designed so that the stale number affects urgency and browsing experience, but not the final correctness of stock allocation.
The Role of Distributed Locking
When only one unit is left, multiple buyers may click at nearly the same time.
The checkout service must serialize or conditionally guard those attempts. Common implementations include:
• Redis locks with TTL • DynamoDB conditional writes • PostgreSQL advisory locks • ZooKeeper style coordination
The lock or conditional guard is usually scoped per SKU, not globally, so unrelated products do not block each other.
Atomic Decrement Is Non-Negotiable
Even with a lock, the inventory mutation itself must be atomic.
In SQL, a typical pattern is:
sql1UPDATE products 2SET stock = stock - 1 3WHERE id = ? AND stock > 0;
Only one transaction should successfully claim the final unit.
Without that atomic guard, two concurrent requests can both read stock = 1 and both think they succeeded.
A Practical Checkout Flow
A simplified purchase flow looks like this:
- Acquire a per-SKU lock or issue a conditional write
- Read authoritative stock
- Reject if stock is already 0
- Create a reservation or idempotency record
- Atomically decrement stock
- Confirm the order
- Mark cache entries as stale
- Release the lock
The important point is that the checkout path never uses the banner count as truth.
Cache Invalidation Strategy
After a successful purchase, the browse cache is stale.
Most platforms do not immediately rebuild every cache on every decrement because that would create too much invalidation traffic during flash sales.
Instead, they often use a delayed or batched refresh:
• normal stock updates: lazy refresh after a short delay • zero-stock transition: aggressive fast-path invalidation
The second case matters more because once stock hits zero, continuing to show urgency creates a poor experience and floods checkout with doomed requests.
Why Zero Stock Gets Special Treatment
When stock goes from 1 to 0, the system wants that information to propagate fast:
• remove the urgency banner • update CDN and cache layers • stop sending buyers into checkout for an impossible purchase
This is both a UX optimization and an infrastructure protection mechanism. Fast zero-stock invalidation reduces wasted checkout load during traffic spikes.
Common Edge Cases
Real systems also handle:
• cart holds that expire after a short time • damaged or unsellable warehouse inventory • request coalescing to avoid cache stampedes • batch invalidation during flash sales • idempotent retries so payment or network glitches do not double-reserve stock
These are the details that make the system survivable in production.
What the User Sees vs What the System Knows
A useful mental model is this:
• the product page shows a projection • checkout enforces reality
The projection is optimized for read scale and persuasive UX.
Reality is optimized for correctness, concurrency control, and inventory safety.
Key Takeaways
- The stock number on the product page is often approximate and eventually consistent.
- Browsing and checkout are intentionally routed through different system paths.
- Distributed locking or conditional writes protect the last unit from concurrent buyers.
- Atomic decrement at the source of truth is what actually prevents overselling.
- Zero stock is treated as a special high-priority cache invalidation event.
- The urgency banner threshold is usually a business decision shaped by A/B testing.
Conclusion
Scarcity banners look simple, but the architecture behind them is one of the cleanest examples of distributed systems tradeoffs in consumer software.
The system accepts stale reads where the business can tolerate them, and pays the cost of strict correctness only where it matters: the moment a user tries to buy.
That is why "Only 3 left in stock" can be a little bit wrong, while the order confirmation still stays correct.
