Moving inventory reservations from Redis back to the database

An engineering account circulating online describes shifting inventory reservations out of a separate in-memory store and into the main relational.

An engineering account circulating online describes shifting inventory reservations out of a separate in-memory store and into the main relational database, and reports that the simpler design held up under production load.

Key takeaways

  • Inventory reservation is the mechanism that stops two shoppers from buying the last unit of the same item at the same time.
  • Splitting that state between a cache such as Redis and a database such as MySQL creates two sources of truth that can drift apart.
  • Consolidating reservations into the relational database trades raw per-operation speed for transactional guarantees that the application no longer has to reconstruct.
  • Claims that a relational database cannot handle high-contention counters are widely repeated but depend heavily on schema design, index layout and access patterns.
  • The specific figures, hardware and workload behind any individual migration report are rarely published in full, so the result should be read as one team’s outcome rather than a general benchmark.

What is actually being described

The pattern under discussion is narrow but consequential. When a shopper adds an item to a basket or begins checkout, an online store often places a temporary hold on stock so the unit cannot be sold twice while payment is processed. That hold is a reservation. It has to be created quickly, released automatically if checkout is abandoned, and counted accurately against the underlying stock level.

For years the reflexive answer has been to keep reservations in an in-memory key-value store, most commonly Redis, because reservation traffic is high-frequency, short-lived and conceptually simple. The database of record holds the durable stock figure; the cache holds the volatile hold count.

The change being described reverses that split. Reservations move into the same relational database that already stores the product and order data, expressed as rows and enforced with ordinary transactions. The in-memory layer is removed from that specific path rather than from the system as a whole.

Why this is being discussed now

Engineering write-ups of this kind surface periodically and attract attention because they run against a widely held default. A large ecommerce operator describing a move away from a specialised store, rather than towards one, is the kind of result that invites scrutiny.

The discussion is also part of a broader re-examination of infrastructure sprawl. Over the past decade many teams accumulated a cache, a queue, a search index and a primary database, each with its own failure modes, upgrade path and on-call burden. As relational databases have improved in throughput and as hardware has become substantially faster, some teams have begun asking which of those components were solving a real constraint and which were adopted by convention.

It is worth stating plainly what is not verifiable from a headline or a discussion thread: the exact traffic volumes, latency distributions, database configuration and rollout method behind any particular migration. Those details determine whether a result generalises.

The background a newcomer needs

Redis stores data in memory and serves simple operations extremely quickly. It offers atomic operations on individual keys, so incrementing a counter is safe. What it does not natively provide is a general transaction spanning many keys with the same guarantees a relational database gives across many rows, nor durability characteristics identical to a database designed for durability first.

MySQL, by contrast, is built around transactions. A transaction lets an application read a stock level, subtract a reservation and commit both changes as a single unit that either fully happens or does not happen at all. Concurrency is managed by locking, and locking is where the performance objections concentrate: if thousands of requests contend for the same row, they queue.

The trade-off, therefore, is not simply fast versus slow. It is where correctness is enforced. With a cache, the application code must reconcile two systems, handle the case where one write succeeds and the other fails, and decide what happens after a restart or an eviction. With a single database, that reconciliation logic largely disappears, at the cost of putting more pressure on one component.

Who is affected and how

The most directly affected group is engineers building commerce, ticketing, booking or any system where a finite quantity is claimed concurrently. For them, the question is architectural: whether to maintain a second store for hot counters.

Operations and platform teams are affected differently. Removing a component removes a class of incidents — cache failover, memory exhaustion, split-brain between stores — and replaces it with concentrated load on the database. That may be a favourable exchange or an unfavourable one depending on how close the database already runs to its limits.

Smaller teams arguably have the most to gain from the simpler shape, because the cost of operating additional infrastructure falls disproportionately on them. Larger organisations may find the calculus reversed, since they can staff specialised systems and their contention hotspots are more extreme.

Merchants and shoppers do not see the architecture, but they experience its failures: overselling, phantom out-of-stock messages, or baskets that lose items during a high-demand sale.

Where informed people disagree

The first disagreement is empirical. Some engineers hold that a well-indexed relational database handles reservation workloads comfortably on modern hardware, and that cache layers were introduced to solve problems that no longer bind. Others contend that this holds only until a single product becomes intensely contended, at which point row-level locking on one row becomes the bottleneck no amount of hardware resolves cleanly.

The second concerns generalisation. A migration that works for one company reflects that company’s schema, sharding strategy, query patterns and traffic shape. Critics of the “just use the database” position argue such reports are read too readily as universal advice.

The third is about where complexity should live. One camp treats fewer moving parts as intrinsically valuable, because operational simplicity compounds. Another argues that separating volatile state from durable state is good design regardless of whether the database could cope, since it isolates load and keeps the system of record calm.

There is also disagreement about whether the comparison is fair, since a cache-based design that was poorly implemented will lose to a well-implemented database design without saying much about the underlying technologies.

The practical implications

For a team considering the same change, the useful questions are specific rather than ideological. How concentrated is contention — is load spread across many items, or does it collapse onto a handful during promotions? What is the acceptable latency at the tail, not the median? How much application code currently exists purely to keep two stores consistent, and how many past incidents originated there?

Design details matter more than the choice of technology. Whether reservations are stored as individual rows or as an aggregated counter, how expiry is handled, whether transactions are kept short, and how indexes are laid out will typically dominate the outcome.

A migration of this kind is rarely a single switch. The common approach is to write to both systems, compare results, then shift reads before removing the old path. That allows a rollback and produces evidence from real traffic rather than synthetic tests.

What to watch next

Watch for detailed technical follow-ups rather than summaries. The valuable material in these accounts is the schema, the locking strategy and the failure cases encountered during rollout — the parts that let another team judge relevance to their own system.

Watch, too, for the broader pattern of consolidation: teams folding queues, caches and derived stores back into a primary database as that database gains features and capacity. Whether this becomes a durable shift or a temporary correction is not yet clear.

Finally, watch for counter-reports. Architectural claims are best assessed when teams publish the cases where the simpler design did not hold, and those accounts are published less often than the successes.

Frequently asked questions

Why would anyone replace Redis with MySQL?

The usual motivation is correctness and simplicity rather than speed. Keeping reservations in a cache while stock lives in a database means two systems can disagree, and the application must contain logic to detect and repair that drift. Putting both in one transactional database removes the reconciliation problem. Teams also cite lower operational burden, since one fewer system needs monitoring, patching and failover planning.

Is Redis unreliable for inventory counts?

Not inherently. Redis provides atomic operations and is widely used for exactly this purpose. The difficulty is not reliability of the tool but the architecture around it: when the durable record sits elsewhere, some coordination scheme must guarantee the two stay aligned across restarts, evictions and partial failures. That coordination is where defects tend to appear, particularly under unusual conditions such as flash sales.

What is an inventory reservation?

It is a temporary hold placed on stock while a purchase is in progress. When a shopper begins checkout, the system marks a unit as claimed so a second shopper cannot buy it simultaneously. If payment completes, the reservation becomes a sale; if the shopper abandons the process, it expires and the unit returns to available stock. The mechanism prevents overselling of limited items.

Does putting reservations in MySQL cause lock contention?

It can, and this is the main technical objection. If many concurrent requests update the same row, they serialise behind a lock. Whether that matters depends on how concentrated demand is and how the data is modelled. Techniques such as splitting a counter across multiple rows, keeping transactions short and avoiding unnecessary work inside them are commonly used to reduce the effect.

Is this a sign that caching layers are unnecessary?

No. The pattern being discussed concerns one specific workload with strong consistency requirements. Caches remain effective for read-heavy paths such as product pages, search results and session data, where stale data is tolerable and volume is high. The narrower argument is that state requiring transactional guarantees may belong with the data it must stay consistent with, rather than in a separate store.

How can a team test this change safely?

The standard approach is incremental. Write reservations to both the existing store and the database in parallel, compare the two continuously to quantify divergence, and only then move reads across, ideally for a subset of traffic first. Load-test with realistic contention patterns, including the case where demand concentrates on a single item, since averages conceal exactly the behaviour that causes failures.

Sources and further reading

  • Public engineering blogs published by large ecommerce platforms, which periodically document infrastructure changes and their rationale.
  • Official MySQL documentation, particularly the sections covering InnoDB transactions, isolation levels and row-level locking behaviour.
  • Official Redis documentation on atomic operations, persistence options and expiry semantics.
  • Hacker News discussion threads, useful for the range of practitioner objections and counter-examples rather than as verified fact.

Surfaced from the hackernews signal “a database architecture migration”. AI-assisted draft, editorially reviewed.

Visited 1 times, 1 visit(s) today
share this recipe:
Facebook
X
WhatsApp
Telegram
Email
Reddit