Warm Critical Cache Keys Before Traffic
Preload high-value keys after a deploy or cache flush, coalesce the warm-up, and verify hit ratio without turning startup into a stampede.
Task
Avoid a cold-cache latency spike and origin overload when a deploy, restart, or cache flush removes hot data.
When to use this
A cache flush during peak traffic sends every request for the homepage and top products to the database, causing a latency spike and timeouts.
Prerequisites
- A cache with a way to set keys and TTLs
- A list or query for the highest-value keys
- A way to run the warm-up outside the request path
Steps
1. Select a bounded set of keys by traffic or business value, not the entire dataset.
const hotKeys = await db.query(
`SELECT id FROM products ORDER BY views_last_7d DESC LIMIT 500`
);
Expected output: The warm-up targets a bounded, high-value set instead of the full database.
2. Warm keys concurrently with a small concurrency limit and reuse the single-flight loader to avoid duplicate origin calls.
await mapLimit(hotKeys.rows, 8, async ({ id }) => {
const key = `product:${id}`;
if (await cache.exists(key)) return;
const value = await singleFlight(key, () => db.loadProduct(id));
await cache.set(key, JSON.stringify(value), { EX: 300 });
});
Expected output: The cache contains the hot keys before traffic arrives, with bounded database concurrency.
3. Verify hit ratio and origin QPS after the warm-up and compare with a cold start.
curl -s http://localhost:3000/metrics | grep cache_hit_ratioExpected output: The hit ratio rises before the traffic peak and origin QPS stays within capacity.
Troubleshooting
Problem
Warm-up slows deployment
Solution
Run it after the instance is healthy but before it receives traffic, with a time limit; do not block the entire rollout on a huge list.
Problem
Warm-up causes a stampede
Solution
Use a small concurrency limit and single-flight; multiple instances should share a distributed lock or stagger their warm-up.
Limitations
- Warmed keys can still be evicted or expire; the TTL and eviction policy remain the real protection.
- A warm-up list based on historical traffic can miss a new hotspot.
Cold caches hurt most at peak
A restart, deploy, or manual flush empties the cache. The next requests all miss and hit the database at once. If the hot set is known, warm it before the instance receives traffic. Warm a bounded set, not the entire dataset; a warm-up that loads everything is a self-inflicted stampede.
await mapLimit(hotKeys, 8, async (key) => {
if (await cache.exists(key)) return;
const value = await singleFlight(key, () => load(key));
await cache.set(key, JSON.stringify(value), { EX: 300 });
});
Bound concurrency and deduplicate
A warm-up should not open hundreds of database connections or duplicate work across instances. Limit concurrency, use single-flight for duplicate keys, and consider a distributed lock so only one instance warms a given key. Stagger the warm-up across instances if the origin cannot handle the combined load.
Measure the effect
Compare cache hit ratio and origin QPS after a cold start with and without warming. A warm-up that runs after traffic arrives does not prevent the spike. Add it to the deploy sequence: start the process, warm within a time limit, then mark ready. If the warm-up fails, decide whether to serve with a cold cache or wait; do not block readiness indefinitely.
Verify the warm-up
Expected outcome: after the warm-up completes, the target keys exist in the cache, the hit ratio is above the pre-warm baseline before traffic arrives, and origin QPS stays within capacity. Confirm by sampling the hot keys with the cache client, comparing hit-ratio metrics with a cold-start run, and checking that the warm-up duration did not exceed the deploy budget. A key that is still missing points to a TTL, serialization, or key-prefix mismatch.