Symptoms
- Logs full of
RedisTimeoutExceptionorRedisConnectionException; response times climb and requests fail. - Cache-aside fallback floods the database, which then also slows down.
1. Read the timeout message
StackExchange.Redis timeouts include useful counters:
Timeout performing GET (5000ms), inst: 0, qu: 0, qs: 120, aw: False, rs: ReadAsync,
ws: Idle, in: 65536, serverEndpoint: ..., mc: 1/1/0, mgr: 10 of 10 available,
IOCP: (Busy=0,Free=1000,Min=8,Max=1000), WORKER: (Busy=180,Free=32587,Min=8,Max=32767)
| Counter | What it tells you |
|---|---|
qs high | Requests sent and waiting for the server — server slow or network |
in high | Data sitting in the socket not read — client too busy |
WORKER Busy > Min | .NET thread pool starvation on the client |
| Large values in the operation | Big keys hogging the connection |
2. Check the server side
az monitor metrics list --resource <redis-resource-id> \
--metric "serverLoad" "usedmemorypercentage" "connectedclients" "evictedkeys" "cacheRead" \
--interval PT1M --offset 1h -o table
- Server load above ~80%: Redis is single-threaded per shard for commands; expensive commands (
KEYS *, bigSMEMBERS, Lua scripts) block everything. - Used memory near 100% with evictions: fragmentation and eviction work slow the server.
- Cache read bandwidth at the tier limit: large values saturate the network.
Find slow commands and big keys:
redis-cli -h <name>.redis.cache.windows.net -p 6380 --tls -a '<key>' SLOWLOG GET 20
redis-cli -h <name>.redis.cache.windows.net -p 6380 --tls -a '<key>' --bigkeys
redis-cli -h <name>.redis.cache.windows.net -p 6380 --tls -a '<key>' INFO memory
Warning
--bigkeysscans the keyspace. It uses SCAN and is generally safe, but on an already overloaded cache run it from a replica-aware window or after the immediate fire is out.
3. Immediate relief
- Kill the bad caller: if
SLOWLOGshowsKEYS *or a huge range query, disable the feature or job that issues it. - Free memory: set TTLs on keys without one, delete obvious large junk keys (
UNLINKis non-blocking). - Scale: move to a larger size or add shards (Premium clustering). Scaling is online but adds load briefly.
az redis update -g rg-cache -n redis-prod --sku Premium --vm-size P2
- Reboot only if the cache is wedged; you lose data on Standard unless persistence is on, and every client reconnects at once.
4. Client-side fixes
- Raise the thread pool minimum on .NET to absorb bursts:
ThreadPool.SetMinThreads(workerThreads: 200, completionPortThreads: 200);
- Reuse one
ConnectionMultiplexerper process. Creating one per request exhausts connections. - Keep values small (under ~100 KB). Split or compress large payloads.
- Use async APIs and avoid sync-over-async (
.Result) in request paths. - Set
connectTimeoutandsyncTimeoutexplicitly and add retry with jitter.
5. Protect the database during recovery
When the cache is unhealthy, every miss hits the database. Add a short circuit breaker or serve stale data where acceptable, and rate-limit cache rebuilds so a cold cache does not start a stampede.
Prevent it next time
- Alert on server load > 70%, used memory > 80% and evicted keys > 0.
- Set
maxmemory-reservedandmaxfragmentationmemory-reservedso Redis has headroom. - Block dangerous commands in code reviews; never call
KEYSin production. - Load test with production-like value sizes before releases.