Cache and Redis reasoning
Status: Complete. Last reviewed 2026-08-28.
A cache trades freshness, memory, and failure complexity for reduced work or latency. Redis is an in-memory data platform with specific persistence, replication, clustering, and command semantics; it is not synonymous with “cache” and does not inherit database guarantees merely because operations are fast.
Define the cache contract
Section titled “Define the cache contract”For every cached value, name the source of truth, key dimensions, representation/schema version, freshness tolerance, TTL, invalidation owner, miss behavior, and acceptable behavior when the cache is unavailable. Without these, a cache is an unowned copy of data.
Cache-aside reads the cache, loads from the source on a miss, then writes the cache. It is simple but permits stale values and a miss race. Read-through delegates loading to a cache layer. Write-through updates cache as part of the write path, while write-behind defers source persistence and can lose or reorder writes unless designed as durable data processing. These names describe flows, not consistency guarantees.
TTL bounds how long an untouched entry remains eligible, not how stale a value can ever be. Repopulating stale data resets its TTL; invalidation can be lost; clocks and failover matter. Event-driven invalidation reduces expected staleness but adds a delivery dependency. Versioned keys make deployment/schema invalidation cheap but leave old keys to expire and do not solve source-data changes.
Keys must include every dimension that selects a value: tenant, authorization scope where relevant, locale, currency, filters, schema version, and feature/experiment. Prefer not to cache per-user authorization decisions broadly; if you do, invalidation on role/policy changes is a security requirement.
Misses, negative caching, and eviction
Section titled “Misses, negative caching, and eviction”A miss can mean absent, expired, evicted, never cached, invalidated, or unreachable cache. Code should not infer source absence from a miss. Negative caching stores “not found” briefly to protect expensive lookups, but it can hide newly created data and amplify enumeration or authorization leaks if keys/scopes are wrong.
Redis expiry removes keys according to its expiration mechanisms, while eviction happens under configured memory pressure. Policies decide which keys are candidates. A key can disappear before its TTL; therefore cache consumers must tolerate misses. If the application cannot operate without a key, that key is operational state and needs an availability/durability design beyond casual cache assumptions.
Size values as well as key counts. One hot large value can saturate network and serialization; many small keys add metadata. Compression saves bytes at CPU cost. Observe hit ratio by workload, miss latency, evictions, expirations, memory fragmentation, command latency, hot keys, bandwidth, and source load. A high global hit ratio can conceal a critical endpoint stampede.
Stampedes and hot keys
Section titled “Stampedes and hot keys”When a popular entry expires, many callers can regenerate it simultaneously, overwhelming the database or provider. Defenses include:
- request coalescing/single-flight within a process;
- a short distributed regeneration lease with bounded wait and fallback;
- stale-while-revalidate, serving an acceptable old value while one owner refreshes;
- probabilistic or jittered early refresh to avoid synchronized expiry;
- warming known keys before traffic or deployment;
- rate/admission limits on the source path.
Every defense needs failure behavior. If the refresher dies, the lease expires and another caller tries. If refresh is slower than the lease, multiple refreshers may overlap. If stale data is unsafe, callers may need to fail closed or use the source with bounded concurrency. Jittering TTL spreads expiries but does not protect a single extremely hot key.
A hot Redis key can saturate one shard/core or network path even with a good overall cluster hit rate. Replication, local caching, key partitioning, precomputation, or changing the read model may help, but partitioning counters or ordered state changes semantics and merge cost.
Redis command and transaction semantics
Section titled “Redis command and transaction semantics”Redis executes commands serially within an event-loop execution path, so an individual command such as SET NX or a Lua/function execution is atomic relative to other commands on that server. A multi-command application sequence is not atomic unless expressed with a suitable command, transaction, script/function, or optimistic WATCH flow. Long scripts/commands block other work and should be bounded.
Redis transactions queue commands and execute them without interleaving after EXEC; they do not provide relational rollback. Runtime command errors can coexist with other successful commands. WATCH provides optimistic conflict detection, requiring the client to retry the whole logical operation.
Choose data structures by their actual semantics: strings for values/counters, hashes for fields, sets for membership, sorted sets for ranked/range access, streams for append/consumer-group workflows. Avoid packing an unbounded aggregate into one serialized blob when field-level access, contention, or update bandwidth matters.
Persistence, replication, and failover
Section titled “Persistence, replication, and failover”Redis can persist snapshots (RDB), an append-only file (AOF), or both, with durability/performance trade-offs and configuration-specific loss windows. Persistence does not make asynchronous replication synchronous. A primary can acknowledge a write that has not reached a replica; failover can promote a replica missing it. The WAIT command can improve the probability that replicas received a write but does not turn Redis Cluster/Sentinel into consensus or guarantee persistence on those replicas.
Replication normally serves scaling and failover with eventual convergence. Replica reads can be stale. During network partitions, failover and split-brain protections/configuration determine availability and potential lost writes. Treat Redis as authoritative only after choosing a topology, persistence policy, backup/restore test, recovery objective, and application conflict model suitable for that data.
Redis Cluster partitions keys by hash slots. Multi-key operations require compatible slot placement; hash tags can co-locate related keys but may create hotspots. Resharding, failover, client redirection support, and topology discovery are operational requirements. A single-node development setup does not test them.
Leases, locks, and fencing
Section titled “Leases, locks, and fencing”A lock with an expiry is a lease. A client can acquire it, pause longer than the lease through GC, scheduling, network, or dependency latency, and continue acting after another client acquires a new lease. Safe release must delete only when the stored ownership token matches; an unconditional delete can remove a successor’s lease. Extending a lease has the same ownership and timing hazards.
For a downstream resource that can reject stale actors, issue monotonically increasing fencing tokens. Each successful lease acquisition gets a higher token; the protected storage accepts an operation only if its token is newer than any seen. This converts overlapping holders into a rejectable stale write. Redis alone cannot fence a system that ignores tokens.
Distributed lock algorithms make timing and failure assumptions. Evaluate the required safety under pauses, partitions, clock behavior, failover, and client uncertainty. For a database invariant, prefer a unique constraint, conditional update, or row/advisory lock in that database. Use a cache lease for duplicate-work reduction only when overlap is tolerable, or combine it with a durable correctness mechanism.
Failure-mode design
Section titled “Failure-mode design”Cache failure should degrade according to a budget, not trigger an uncontrolled source avalanche. Options include fail open to the source with strict concurrency/rate limits, serve bounded stale data, use a local fallback, reject optional traffic, or fail closed for security-sensitive state. Time out cache calls quickly enough to preserve the request deadline.
Avoid synchronized reconnects and retries after Redis recovery. Connection pools, client-side queues, and retry policies must be bounded. Never retry a non-idempotent multi-command sequence after an ambiguous timeout without resolving whether it executed.
Test cold cache, mass expiry, eviction, high latency, unavailable primary, failover, stale replica, oversized values, and malformed old schema. Reconciliation matters when cached materializations are fed by events: compare with the source and rebuild safely.
Current and legacy context
Section titled “Current and legacy context”- Current: Redis 8 documentation is the review baseline; verify command, persistence, cluster, and client behavior against the deployed version/edition.
- Common: Redis serves mixed roles—cache, rate limiter, sessions, queues, streams, coordination. Each role needs different loss and availability decisions.
- Legacy: “Redis is single-threaded,” “SETNX is a distributed lock,” and “TTL guarantees freshness” are incomplete mental models that hide I/O threads, multi-step races, leases, eviction, and source ownership.
Interview practice
Section titled “Interview practice”- DATA-CACHE-01 — Define a cache contract
- DATA-CACHE-02 — Prevent a cache stampede
- DATA-CACHE-03 — Explain Redis durability and failover
- DATA-CACHE-04 — Design a safe lease
- DATA-CACHE-05 — Diagnose a hot key and eviction
- DATA-CACHE-06 — Degrade during cache failure