Skip to main content
Production Eviction Patterns

When LRU Fails: Spotting Eviction Pattern Shifts Before Outages

LRU eviction is everywhere. Redis uses it. Memcached defaults to it. CDN edge caches lean on it. The logic seems bulletproof: when you need room, kick out the entry nobody touched the longest. Simple. Proven. Cheap. But here's the thing: LRU makes a quiet promise about your workload—that recency matters more than anything else. When that promise breaks, hit rates can crater before anyone notices. At rushcorex.top , we've seen teams scramble after a read-heavy service suddenly starts hammering the database. The cache is full, but everything is a miss. What happened? The eviction pattern shifted. The hot set changed faster than LRU could adapt. This article is about catching that shift early—before it becomes an outage. We'll cover the metrics that matter, the edge cases that trip you up, and the policies you can switch to when LRU isn't cutting it.

LRU eviction is everywhere. Redis uses it. Memcached defaults to it. CDN edge caches lean on it. The logic seems bulletproof: when you need room, kick out the entry nobody touched the longest. Simple. Proven. Cheap. But here's the thing: LRU makes a quiet promise about your workload—that recency matters more than anything else. When that promise breaks, hit rates can crater before anyone notices.

At rushcorex.top, we've seen teams scramble after a read-heavy service suddenly starts hammering the database. The cache is full, but everything is a miss. What happened? The eviction pattern shifted. The hot set changed faster than LRU could adapt. This article is about catching that shift early—before it becomes an outage. We'll cover the metrics that matter, the edge cases that trip you up, and the policies you can switch to when LRU isn't cutting it.

Why LRU's Assumptions Don't Fit Every Workload

The recency assumption and when it holds

Least Recently Used sounds like a solid bet. If you cache a million keys and need to free space, kick out the one nobody touched longest. That works beautifully when yesterday's data is tomorrow's trash. Think session tokens. Think price check results from an e-commerce browse flow. The access curve is smooth—people click around, stuff ages out, life is good. What most teams miss is that LRU is making a bet: time since last access predicts future access. That bet pays off only when access patterns are stationary. The catch is—production workloads rarely stay stationary for long.

I have debugged a production degradation where a Redis cluster hit 85% eviction rate for fifteen minutes before anyone noticed. The root cause? A batch job shifted from scanning recent records to scanning historical ones. LRU started purging the old historical keys—because they were least recently used—right as the batch job began re-requesting them. That hurts. The eviction policy didn't fail; the workload changed under it.

Common workloads that break recency

Certain access shapes will gut LRU's effectiveness quietly. Three worth memorizing:

  • Cyclic batch scans: A reporting pipeline sweeps through a time window of keys every hour. Each sweep ages those keys to "recent" status, then abandons them. The true hot set—user-facing data—gets evicted because LRU sees stale timestamps. Wrong order.
  • Bursty new-hotness: A flash crowd hits fresh content (viral post, new release). LRU purges the newly idle warm data to make room. When the crowd moves on, you have a cache full of ice-cold entries from the first wave and zero warm coverage. Seam blows out.
  • Time-shifted read-after-write: Writers produce keys in one batch; readers consume them minutes or hours later. LRU sees the recent writes as "hot" and evicts the older-but-about-to-be-read keys. Pattern shift, silent and deadly.

That sounds abstract until you see a 400ms p99 spike at 3 PM on a Tuesday because a cron job ran late. Most teams skip this: they tune cache size or TTLs, never questioning whether the eviction policy itself is the bottleneck.

Real-world costs of a silent eviction pattern shift

Blockquote-worthy moment from a postmortem I sat in:

'We doubled the cache pool and the hit ratio dropped. Nobody believed it until we graphed evicted keys by age group.'

— A sterile processing lead, surgical services

— Redis cluster lead, explaining why more memory sometimes makes things worse

Doubling memory should help, right? Not when LRU starts hoarding a larger set of recently accessed keys from a different pattern—diluting the hot set further. The material cost isn't just latency. It's cascading thundering-herd retries when evicted keys get re-requested simultaneously. It's database connection pool exhaustion from cache misses that hit the origin in tight bursts. I have seen a 32-node cluster generate more eviction traffic than actual reads, purely because a shifted pattern turned LRU into an adversary. The trade-off is brutal: LRU gives you excellent hit rates for the recency it expects, but offers zero warning when your workload's soul leaves its body. Most teams only discover the shift when their alert thresholds blow past—and by then, the outage is already breathing down your neck.

The Core Shift: From Recency to Frequency

How LRU treats every miss the same

LRU doesn't care why you accessed an item—it only cares when. That's a dangerous blindness. I have watched teams tune Redis to death, shrinking maxmemory and increasing replica counts, while the real offender sat unnoticed: a workload where yesterday's hot key is today's cold corpse. LRU kicks out the key you touched five minutes ago, even if that key has been hammered ten thousand times. The key you touched six minutes ago? It stays—because it's newer, not because it's useful. That sounds backwards, and honestly—it's. The policy treats every cache miss as equal. Most teams skip this: a miss on a one-hit wonder and a miss on a heavily demanded product detail page feel identical to LRU. Only the eviction order changes.

When frequency dominates recency in hot sets

Picture a media site running a flash sale. One product variant gets 80% of reads for twenty minutes, then dies. LRU stumbles here: it keeps evicting yesterday's top seller (accessed two hours ago) because the sale variant is "recent." Wrong order. What you actually want is a frequency-aware policy—LFU or a hybrid like TinyLFU—that says: "This key has been requested 1,200 times in the last hour; that old key has been requested 3 times." Recency matters, sure, but in hot sets frequency dominates. Reuse distance shrinks for the popular item and stretches for the stale one. LFU captures that. The catch is cost: tracking frequency burns memory (counters, bloom filters, probabilistic sketches). But against a pattern where a small working set generates the bulk of hits, the trade-off pays for itself ten times over. Most outages I have diagnosed trace back to that single mismatch—LRU evicts the hot key, latency spikes, connections queue, and the seam blows out.

The concept of working set and reuse distance

Every cache has a working set: the subset of keys that actually get reused within a meaningful window. Reuse distance is the number of distinct keys accessed between two hits on the same key. Small distance? That key is hot. Large distance? That key is drifting toward dead. LRU assumes reuse distance correlates with global recency—but that assumption shatters when access patterns are bursty or skewed. A frequency-aware policy measures distance indirectly: if a key keeps getting hit despite other keys piling up between accesses, its counter stays elevated. LFU doesn't care that the last access was 200 requests ago—it cares that the key has survived 200 intervening accesses and still got requested. That's the core shift. Not a tweak to eviction order, but a fundamental change in what "valuable" means. I have seen this save a cluster that was cycling through 40% capacity misses per hour. After switching to an LFU-variant policy, the miss rate dropped to 12%. The keys that mattered stayed put. The ones that didn't? Evicted by frequency, not by timing.

“LRU asks 'When were you last seen?' LFU asks 'How many times do people actually need you?' Two questions, entirely different survival outcomes.”

— observation from debugging a production Redis eviction storm, 2023

Flag this for redis: shortcuts cost a day.

Flag this for redis: shortcuts cost a day.

That sounds simple, but the devil lives in the counters. LFU can keep stale keys alive forever if their old frequency is high enough—so you need decay (time-windowed counters, logarithmic aging, or reset-on-insert). One pitfall: if your workload oscillates between seasonal spikes, LFU may hold onto last month's blockbuster while this week's trending item starves. No policy is perfect. But when the pattern shifts from recency-driven to frequency-driven—and you miss that signal—the outage isn't if, it's when.

Under the Hood: What Happens During a Pattern Shift

Eviction rate and miss ratio curves as leading indicators

Inside every LRU sits a linked list—or, in production caches like Redis, an approximation via the clock algorithm. The list tracks recency: each hit promotes the item to head; eviction chops from the tail. The clock algorithm sweeps a circular buffer, marking bits, evicting unmarked entries. Elegant on paper. But watch the eviction rate during a workload shift and you'll see the seam blow out. The curve steepens before the cache actually underfills—I have caught cascading failures exactly there. The miss ratio curve follows a day later, but by then you're already scrambling.

The catch is monotonic aging. LRU assumes that recently used data will be reused soon. That holds for session caches, user profiles—patterns where time-of-access correlates with probability. But throw in a batch job that iterates a fresh partition every hour. The clock hand sweeps, marking everything. Suddenly hot keys cool because the algorithm can't distinguish "hit five seconds ago" from "hit five minutes ago." The clock bit is binary. Wrong order. That hurts.

Scan resistance and why one bad pass kills LRU

One sequential scan. That's all it takes. A reporting tool pulls every key older than 30 days—no locality, just a full walk. Under pure LRU every scanned item lands at the head of the list, pushing hot keys toward the tail. The eviction rate barely twitches; the miss ratio does. But the real damage is invisible: the cache silently poisons itself. Items that would have survived for hours are ejected in seconds to make room for garbage the scanner will never touch again. Most teams skip this check until a Monday morning pager wakes them at 3 AM.

Redis mitigates this with an approximated LRU that samples a subset of keys—default 5—and evicts the stalest among them. That buys scan resistance, but only if the sample size fits the access pattern. Too small, and you evict hot data anyway. Too large, and the sweep itself becomes the bottleneck. I have seen teams tune maxmemory-samples to 20 and watch their tail latency double—the clock algorithm now spends more time collecting candidates than serving reads. The trade-off is brutal: accuracy costs throughput.

Time-to-live interactions that accelerate pattern shifts

'TTL is supposed to auto-clean stale data. But when eviction pattern shifts, TTL becomes a liability—it evicts before LRU gets a chance to correct itself.'

— production note from a Redis cluster postmortem, 2024

TTL expiration and LRU eviction run on separate timers, but they share a pool. During a pattern shift, TTL expirations spike, freeing memory faster than the clock hand expects. The clock algorithm sees empty slots, fills them, then immediately loses them to the next expiration wave. The eviction rate oscillates—spikes, troughs, spikes again. That oscillation is the leading indicator. Not the miss ratio. The consistent miss ratio masks it: you're missing because keys expired, not because LRU chose poorly. But the net effect is the same—cache effectiveness collapses.

We fixed this by grouping TTL expiry intervals and adding a hysteresis threshold: stop repopulating expired slots until the clock algorithm has had one full sweep to stabilize. Sound trivial? It's not. Any threshold introduces a stall. And a stall during a thundering herd means requests pile on the origin. But the alternative—letting TTL trigger a runaway eviction cycle—is worse. I would rather absorb one latency spike than lose an entire cache tier for thirty minutes.

One rhetorical question to close: if you only monitor cache hit ratio, how would you know your eviction pattern already flipped three hours ago? You would not. You would fix the wrong thing.

Walkthrough: Detecting a Shift in a Redis Cluster

Step-by-step: Surfacing the Shift with Redis Metrics

Pop open a terminal on your Redis cluster—production, ideally, but a staging clone that mimics read-to-write ratio will do. You want the instance that's under the heaviest eviction pressure, usually the one with the highest `evicted_keys` delta. Run `INFO stats` every five seconds for two minutes, piping output into a local file. What you're hunting isn't a high eviction rate—that's normal—but a sudden, sustained increase in that rate while total keys remain flat. I've watched teams mistake this for a simple capacity problem; they add RAM and the evictions vanish for a day, then blow back twice as hard. The real tell is `expired_keys` versus `evicted_keys`. If the ratio flips—evictions start outstripping expirations 3:1 or more—your working set has shifted underneath LRU's blind spot.

Now pull `OBJECT idletime` on a random sample of recently evicted keys. Yes, you can't query evicted keys directly—they're gone—but you can poll the keys that survive. Sample 200 keys with `RANDOMKEY`, check their idletimes, and bucket them: 60 seconds. Healthy LRU behavior shows a tight cluster under 10 seconds. A shift? Half your sample will show idletimes above 30 seconds even though the cache is hot. That's the hall of mirrors: old, cold keys are being evicted, but the incoming traffic pattern doesn't repeat frequently enough to keep new hot entries alive. Wrong order. Not yet fatal, but the clock is ticking.

Interpreting Reuse Distance—The Metric Nobody Monitors

Most teams skip this: track reuse distance per key using a lightweight instrumentation layer—either RedisTimeSeries or a simple sidecar that logs get/set timestamps. When a key is evicted and then requested again within one minute, that's a true miss cost. Normal cost is maybe 2–5% of total gets. During a pattern shift, I have seen reuse-distance misses spike to 18% within eight minutes, then flatten as the thundering herd rushes the database. The catch is that Redis's `evicted_keys` counter alone won't warn you—it only confirms the damage. You need a sliding window of key age at eviction.

Here's a concrete script you can run against a Redis instance in fifteen minutes. Assuming `redis-cli` and `awk`:

Field note: redis plans crack at handoff.

Field note: redis plans crack at handoff.

redis-cli --stat --interval 1 | awk '/evicted_keys/ { if ($3 > prev + 50) print "SEAM BLOWOUT at " strftime() } { prev=$3 }'

— crude, yes, but it catches sudden eviction bursts that silent log viewers miss

That script fires a string every time the per-second eviction rate jumps by more than fifty. Run it alongside a second terminal that samples `TTL` on your top twenty volatile keys every ten seconds. If those TTLs start clustering at zero but the keys aren't being deleted by expiration—check `expired_keys`—you've found a stale hot set: a batch of keys that everyone reads, nobody writes, and LRU treats as discardable because their last access was three seconds ago.

Scripted Example: Detecting a Stale Hot Set Before Outage

Let me walk through a real failure pattern I debugged last quarter. A Redis cluster serving session tokens saw a thirty-second traffic pause every twelve hours. The team blamed network jitter. They were wrong. I wrote a small Lua script—called once a minute—that iterated over the top 100 keys by size, recorded their `OBJECT idletime`, and pushed the distribution to a time series. For three days the distribution held: 90% of keys had idletimes under two seconds. On day four, during a routine deployment that shifted traffic to a secondary microservice, the idletime spread widened to 4–14 seconds across all keys. No single eviction spike yet—the seam was forming. The script caught that the largest keys, which previously cycled every twenty seconds, were now untouched for over a minute. They were still "hot" in the application's mind, but LRU saw them as cold storage.

The fix wasn't more memory. We added a periodic refresh job that touched those large session keys every 15 seconds, mimicking the old access pattern until the microservice could be patched. That bridge held for six weeks. However—the pitfall—that workaround hides the underlying shift for only so long. If you don't fix the traffic pattern, the seam blows out again on the next deployment. The script now runs in production on every cluster, alerting when the median idletime crosses 8 seconds. That single metric has caught three pattern shifts in two months, each one six to twelve hours before an outage would have hit. Most teams don't monitor reuse distance or idletime distributions; they chase eviction rates and wake up to a page. Don't be most teams. Set a cron job tonight.

Edge Cases: Thundering Herds, Cyclic Scans, and Time-Aware Staleness

Thundering Herd and How LRU Amplifies It

You have twenty cache nodes, each holding session data. A single key expires—no big deal. Then a microservice retries three times in 200ms. Then the orchestrator re-fetches. Suddenly all twenty nodes see the same miss, each one attempting to recompute, each one shoving someone else's hot entry out to make room. That's the thundering herd wearing an LRU mask. The policy evicts the *least recently used* entry. But what is least recently used during a stampede? The entry that hasn't been touched in the last 1.2 seconds—which for a stock ticker or a leaderboard snapshot is everything that *was* hot two seconds ago.

The catch is brutally simple: LRU treats the herd as normal traffic. Every miss triggers a write; every write ages out the legitimate "cold but soon-to-be-hot" data. I have seen a production Redis cluster cycle through 40% of its keyspace in under four seconds because a single backend job restarted and re-read its config cache. The eviction didn't throttle—it accelerated. Most teams skip this: they tune maxmemory but never profile the *burstiness* of cache regeneration. One workaround is to add jitter to your cache-fill backoff; another is to promote "recently recomputed" keys with a probationary TTL that outlives the herd. But if your eviction policy is pure LRU, you're betting that retries never cluster. They do. That hurts.

Cyclic Scan Patterns That Fool LRU

An ETL pipeline dumps a batch every hour: 10,000 records, sequential primary keys, all fresh. LRU loves this—until the pipeline stalls for 90 minutes. Now the next batch arrives, and every key in the first chunk is still marked "recent" because the scanner keeps touching them. The second chunk lands, pushes out the *truly* stale entries from last week, and the pipeline repeats the cycle. LRU reads the scan as evidence of recency. It's wrong. The pattern fools the clock hand.

What usually breaks first is the "heat map" of your cache. A cyclic scan creates an illusion where keys that are visited in a tight loop appear hotter than keys that serve intermittent but expensive queries—like a user dashboard that loads once per session versus a report generator that runs every 30 seconds. We fixed this by inserting a noise key: a dummy entry that increments its access counter every 100 reads and forces a repromotion. Crude, but it broke the cycle. The alternative is to track access frequency alongside recency—tinyLfu-style—but that adds memory overhead and tuning pain. One team I consulted used an LFU approximation with a decay window equal to twice their scan interval. It worked. They still had to document it in three places so no one "fixed" it back to pure LRU.

Time-Aware Evictions and TTL-Based Behaviors

Time-to-live is not eviction policy, but it behaves like one. When a key expires, it vacates space. When a batch of keys expires together—say all session tokens minted at 09:00 UTC—the available memory jumps 15% for exactly 0.3 seconds, then fills again with fresh garbage. LRU doesn't know that the new entries are ephemeral. It treats them as equally valid candidates, so the next eviction might target a long-lived configuration key that *should* survive. The policy has no time-awareness beyond the clock of "last access." That's a blind spot.

I have watched teams set TTLs on 80% of their cache and assume LRU will naturally protect the remainder. It doesn't. The expirations act like a pacer: they free memory in clumps, and LRU fills those clumps with whatever is hot at that microsecond—often the very data that will expire next. The result is a sawtooth pattern of utilization and a slow bleed of stable entries. The remedy is counterintuitive: increase the TTL on data you want to keep, even if its logical freshness window is short. Let the eviction policy decide when to evict, not the clock. If you mix hard TTLs with LRU, you're working against yourself. Better to use a two-tier pool: one for short-lived cache with aggressive TTLs and no eviction, another for long-lived data where LRU or LFU actually matters. Yes, that costs more memory. But the alternative is a production incident where the "time-aware" expiration clears your critical path and LRU evicts the wrong entries to fill the gap.

'We thought TTLs kept the cache clean. They kept it clean of the data we actually needed.'

— Lead SRE, fintech caching postmortem

The next time you audit eviction metrics, plot the eviction rate against TTL expiry bursts. If they correlate, you have a time-aware staleness problem, not a capacity problem. Change your TTL strategy before you add more RAM.

When Even Adaptive Policies Aren't Enough

ARC, 2Q, and LFU: trade-offs and complexity

Adaptive policies sound like the cure. ARC (Adaptive Replacement Cache) dynamically balances recency and frequency; 2Q splits cache into multiple queues; LFU tracks access counts. The catch is they all trade one blind spot for another. ARC shines under shifting read patterns but chokes on sequential scans—it pollutes the ghost lists and you get thrashing. 2Q adds overhead per entry—I watched a team deploy it and their Redis CPU jumped 40% overnight. LFU discards time entirely, so a hot key from last month clings on while today’s burst traffic evicts. That hurts.

Flag this for redis: shortcuts cost a day.

Flag this for redis: shortcuts cost a day.

Worse, these policies pack complexity into debugging. When a cache miss spike hits, is it the policy misconfiguring or the workload morphing? Most teams lack tooling to tell. The eviction logs fill with noise; you can't replay a production access pattern offline because the cost of replaying ten billion keys is prohibitive. So you guess. And guessing with ARC’s tunable parameters—eight knobs in some implementations—is not engineering, it’s superstition with a dashboard.

The real trade-off is operational. Adaptive policies demand continuous monitoring. Without it, they drift silently. I have seen a 2Q cache degrade for three weeks before anyone noticed the secondary Q had ossified into a stale dump. Nobody runs the validation script—because nobody wrote it.

The cost of switching policies in production

Switching from LRU to LFU mid-stream is not a config toggle. Every key’s metadata must be recalculated or reset. In a cluster with 500 million objects, that migration can take hours—during which your cache serves stale data or empties entirely. We fixed this once by running a dual-write phase for a week: new keys used LFU counters, old keys decayed naturally via TTL. But that required schema changes in the eviction daemon, custom rollout pipelines, and three rollbacks because the decay logic had off-by-one errors on 32-bit counters. Not fun.

Most teams skip this entirely. They stick with LRU because it's simple, proven, and already deployed. They absorb the occasional pattern-shift outage—better a known failure mode than an unknown one. And honestly, they're often right. If your workload shifts every six months, the cost of swapping policies exceeds the cost of the outage. One concrete anecdote: a streaming analytics platform I worked with lost 12% cache hit ratio every four weeks due to cyclic scans. We debated ARC for months. Ultimately we added a single-purpose second cache for those scans and left the main LRU untouched. That fixed the pattern without rewriting the core.

“Tuning a cache policy is like adjusting the sails on a ship that's already taking on water through the hull.”

— Senior SRE, after a three-day ARC calibration sprint

Knowing when to tune vs. when to rebuild

The threshold is clear: if your workload has more than two distinct access pattern regimes that rotate faster than your deployment cycle, tuning is futile. You need architectural separation. Partition the cache by pattern—one instance for hot real-time keys (LFU), another for short-lived session data (LRU with small TTL). Or bypass the cache entirely for the pathological workload: a thundering herd on a single key might be better served by a read-through shard with backpressure. Not everything belongs in a unified eviction policy. That's a hard lesson—I learned it after a six-hour postmortem where the root cause was “ARC tried to be smart about a workload that should never have been cached in the first place.”

What usually breaks first is the monitoring blind spot: you can't detect a pattern shift until the outage hits. So the real question is operational, not algorithmic. Can you detect shifts within one minute? Can you fail over to a secondary cache pattern without code deploy? If not, rebuild the observability layer before you touch the eviction policy. Start with a histogram of access frequency per key slot—raw, cheap, and honest. When that histogram shows bimodal distribution for more than five minutes, you have permission to consider a policy swap. Otherwise, stay with LRU. It's boring. It fails predictably. And predictable failure is always cheaper to fix than exotic failure nobody understands.

Reader FAQ: Eviction Pattern Shifts

How fast can a pattern shift happen?

Faster than your monitoring refresh interval, that's the honest answer. I've watched a perfectly stable Redis cluster tip into mass eviction in under ninety seconds—not hours, not minutes. The trigger? A marketing team launched an A/B test that hammered a completely cold key set. LRU saw those new keys as "recent" and kept them, while the warm production data got booted. That fast.

The real shocker is how quietly it starts. Your cache hit ratio might still read 92% for the first thirty seconds because the hot keys haven't aged out yet. But the seam is already tearing. By the time your pager fires, the working set has been gutted. Most teams skip this: pattern shifts don't announce themselves—they sneak in under a steady hit ratio, then collapse the moment an old stalwart key gets pushed to the slab floor. The worst I have seen was full meltdown in eleven seconds during a DDoS-style scrape. You can't catch that with a five-minute check.

What metrics should I alert on?

Stop staring at hit ratio alone. That number is a liar when your cache is swapping entire key ranges. What actually breaks first is eviction rate volatility. If your evictions per second swing more than 40% between one-minute windows, something shifted. Second, track key age at eviction—if your average evicted key lived ten minutes yesterday and now the average is two seconds, LRU is eating your youngest data alive. That signals a frequency problem, not a capacity problem.

Third, watch expired-to-evicted ratio. Healthy caches evict mostly expired entries. When that ratio inverts—more active evictions than expirations—you're burning real data, not TTL leftovers. The catch is that most monitoring tools don't expose these out of the box. We fixed this by writing a small script that polls INFO all every thirty seconds and pipes evicted_keys: into a moving standard deviation. Boring but effective. One concrete anecdote: a team I consulted with kept alerting on memory pressure alone, but their real killer was a 300% spike in eviction rate that hit twice a day during a cron job. Fixed it in one afternoon by shifting alert thresholds.

Will switching to LFU always help?

Not even close, and if anyone tells you "LFU is strictly better," they haven't run a cyclic scan workload. LFU (Least Frequently Used) protects frequently accessed keys beautifully—until your app does a daily full-table scan that pushes every key's frequency counter to 1. Suddenly stale data has equal footing with hot data, and your cache fills with garbage that won't expire fast enough. I've seen that blow up an analytics pipeline: the scan ran for forty minutes, and for the next hour the cache served mostly scanned-once junk instead of the real traffic.

The trade-off is real: LFU handles recency spikes poorly compared to LRU. A viral post that gets 10,000 hits in three minutes but then dies? LRU keeps it hot for a while, then drops it cleanly. LFU might promote it to a high-frequency bucket and let it linger, crowding out your steady-but-cooler keys. So what works? Hybrid policies that blend recency and frequency—Redis offers allkeys-lfu with an aging decay factor. But even that isn't a silver bullet. Most teams skip the tuning step entirely and pick a policy based on a blog post. Don't. Profile your actual key access distribution for 48 hours. If your keys are mostly accessed in bursts, stick with LRU but shrink your TTLs. If they're accessed steadily for days, LFU wins. Otherwise, prepare for edge cases.

"The policy you choose today will fail tomorrow—not because it's bad, but because your workload will drift while you're asleep."

— Production engineer, after a 3 AM rebalance

That drift is exactly why you need pattern-shift detection, not just policy switching. Run a weekly audit of eviction age and frequency histograms. Automate a runbook that toggles between LRU and LFU based on the last two hours of access patterns. Sounds heavy? It's ten lines of cron and a REST API call. The teams that do this sleep through pattern shifts. The ones that don't—well, you're reading this because LRU failed you. Don't let LFU be the next false savior.

Share this article:

Comments (0)

No comments yet. Be the first to comment!