Caching Strategies and Distributed Caching Questions
Using caches to reduce latency and load: cache-aside, read-through, write-through, and write-behind patterns, TTLs, eviction policies, and distributed caches such as Redis or Memcached. Covers cache invalidation, stampede and thundering-herd protection, and the consistency tradeoffs of caching. Focuses on where and how to cache across tiers.
Explain the purpose of caching in distributed systems. Define cache hit, miss, and hit ratio, and describe the typical benefits and tradeoffs of adding a cache to a service. Give concrete examples of workloads that benefit from caching (and why) and workloads where caching could be harmful.
Sample Answer
Direct answer
A cache is a fast, smaller copy of data kept close to where it is used, so repeated reads can be served without redoing expensive work (a database query, a network call, a heavy computation) every time. The core trade-off is speed and reduced load in exchange for the risk that the cached copy becomes stale relative to the real source of truth.
Structured elaboration
- What problems caching solves: latency (serving from memory or a nearby node is far faster than recomputing or fetching from a distant source), throughput/load (fewer requests reach the expensive backend), and cost (fewer database queries or external application programming interface (API) calls, which often cost money directly).
- Where caches typically live: client/browser (closest to the user, zero network cost on a hit), content delivery network (CDN) / edge (near the user but shared across many users), application in-memory (fast, but local to one process/instance), and a shared distributed cache like Redis or Memcached (shared across all instances in a region, one network hop away).
- Primary trade-offs: staleness (the cached copy may not reflect the latest write), added complexity (invalidation logic, cache-miss handling, monitoring another moving part), and memory cost (caches are not free storage).
- When caching helps: read-heavy workloads where the same data is requested repeatedly and can tolerate at least a little staleness, or where the underlying computation/fetch is expensive relative to a cache read.
- When caching is harmful: data that changes on every read (no repeated value to cache), workloads that are already write-heavy with low read repetition (cache churn without benefit), or correctness-critical data where any staleness is unacceptable and the added complexity of cache invalidation introduces more risk than the latency win is worth.
Worked example
An API endpoint that computes a dashboard aggregate over a large dataset in 800ms, requested 200 times per minute by the same handful of users, is a strong caching candidate: a 30-second time-to-live (TTL) cache serves nearly every request from cache after the first, cutting both latency (single-digit milliseconds instead of 800ms) and backend load by over 95 percent, for a staleness window most dashboard users will not notice. The same 30-second TTL applied to an account balance shown right after a deposit would be actively harmful, showing the user a stale, "wrong" number at the exact moment they are checking it.
Trade-offs and pitfalls
The most common mistake is caching by default rather than by evaluating whether the read pattern and staleness tolerance actually justify it; caching adds a second place data can be wrong (the cache disagreeing with the source of truth), and that failure mode does not exist at all if you never cache the data in the first place.
Compare TTL (time-to-live) based expiration with explicit eviction/purge. Provide examples of use-cases where TTL is sufficient and where explicit invalidation is necessary. Also list drawbacks of using long TTL values.
Sample Answer
Direct answer
Time-to-live (TTL) expiration is a passive, self-healing bound on staleness that requires no extra machinery; explicit invalidation (purge, versioned keys, or event-driven invalidation) actively corrects the cache the moment the source changes, at the cost of needing a reliable signal that a change happened.
Structured elaboration
- When TTL is sufficient: content where a bounded staleness window is genuinely acceptable (a homepage banner, a "trending now" list, most analytics dashboards); TTL requires no coordination and self-heals even if a write is missed entirely, because the entry expires on schedule regardless.
- When explicit invalidation is necessary: content where staleness has real consequences (permission revocation, inventory during checkout, a price correction that must take effect immediately); waiting out even a short TTL is unacceptable when correctness, not just freshness, is on the line.
- Versioned keys as a form of explicit invalidation: rather than deleting the old entry, bump a version token that is part of the key; the old version simply becomes unreachable and ages out naturally, avoiding races where a delete arrives before or after the write it corresponds to.
- Event-driven invalidation: a write publishes an event (directly or via change data capture, CDC) that consumers use to purge or update affected cache entries; this gets close to real-time correction but adds a dependency on the message delivery being reliable.
- A hybrid approach: use a moderate TTL as a safety net (so a lost invalidation event self-heals within a bounded window) combined with explicit invalidation for the fast path; this is the most common production pattern because it gets the responsiveness of explicit invalidation with the resilience of TTL.
Worked example
A user's permission level changes from "member" to "banned." Relying purely on a 5-minute TTL means the banned user could still access restricted content for up to 5 minutes; that is not acceptable, so this write triggers explicit invalidation of the user's cached permission entry, with a short TTL (e.g., 60 seconds) still applied as a safety net in case the invalidation event is somehow lost.
Trade-offs and pitfalls
Long TTLs used purely for "simplicity" on data that actually needs freshness is one of the most common production incidents in caching (a stale price, a stale permission, a stale feature flag reaching users for far longer than intended); size the TTL to the actual staleness tolerance of the data, not to whatever is convenient to implement. Relying on explicit invalidation alone, with no TTL backstop, means any lost or delayed invalidation event becomes unbounded staleness with no self-correction.
Compare cache placement options: client-side, CDN/edge, reverse-proxy (e.g., Varnish), application-level in-memory (e.g., Redis/Memcached), and database-side (materialized views or DB-level caching). For each option describe pros, cons, typical use cases, security/privacy considerations, and how TTLs and invalidation differ by placement.
Sample Answer
Direct answer
Cache placement is a ladder from "closest to the user, cheapest, hardest to invalidate precisely" to "closest to the source of truth, most expensive per request, easiest to keep correct": client-side, content delivery network (CDN)/edge, reverse proxy, application in-memory, and database-side.
Structured elaboration
- Client-side: browser cache, local storage, or a mobile app's local store. Zero network cost on a hit, but you have essentially no control once data leaves your servers; invalidation means waiting out a time-to-live (TTL) or changing a versioned URL.
- CDN/edge: shared across all users near a given geography, ideal for static or long-TTL, non-personalized content. Purge/invalidation is slower (can take seconds to propagate globally) and typically coarser-grained than a server-side cache.
- Reverse proxy (e.g., Varnish): sits in front of your application servers, caching full HTTP responses; good for reducing application-server load for cacheable pages without needing application code changes, but still shared and public unless carefully scoped per-user.
- Application-level in-memory (Redis/Memcached, or in-process): shared across your own service's instances (Redis/Memcached) or private to one instance (in-process); this is where most business-logic caching happens, because you have full control over invalidation and can cache personalized data safely.
- Database-side (materialized views, query result caching): closest to the source of truth, so almost always the most consistent option, at the cost of doing the least to reduce load on the database itself.
- Redis vs. CDN by use case: for static assets, a CDN wins outright (no reason to burn application-tier memory on data that never changes per-request). For personalized HTML fragments, a CDN only works with edge compute; otherwise application-tier Redis is the safe default. For frequently-read configuration flags, an in-process or small Redis cache with a short TTL beats a CDN, since the data is tiny and needs low latency, not global edge distribution. For large objects with varying TTLs (e.g., images), a CDN with per-object cache-control headers is the natural fit.
Worked example
A product page has a static hero image (CDN, long TTL, content-hashed URL so invalidation is just a new URL), a shared "similar products" block computed the same for all users (reverse proxy or application cache, medium TTL), and a personalized "recently viewed" section (application-level cache keyed per user, short TTL, never placed at a shared CDN/proxy layer).
Trade-offs and pitfalls
Placing personalized content at a shared caching layer (CDN or reverse proxy) without per-user cache keys is a serious privacy bug, not just a staleness inconvenience; one user's private data can be served to another. The further a cache sits from the source of truth, the cheaper it is per request and the harder it is to invalidate precisely; choose the placement based on how quickly and precisely that specific data needs to be corrected on write.
Explain cache hit, miss, and eviction. Describe the metrics you would monitor to understand cache health in a production system and how you would measure them. Given a cache that receives 100k requests/min and returns 80k hits, calculate the hit rate and discuss implications for capacity sizing and SLOs.
Sample Answer
Direct answer
A cache hit is a read served from the cache; a miss is a read that had to go to the origin; the hit ratio is hits divided by total requests, and it is the single most important summary number for whether a cache is doing its job.
Structured elaboration
- Hit ratio formula: hit ratio=hits+misseshits. It should be tracked as a rolling metric (e.g., per minute) rather than a single lifetime number, since it reacts to traffic pattern changes, deploys, and cache flushes.
- Eviction: when the cache is full and a new item needs room, an eviction policy (least-recently-used, LRU; least-frequently-used, LFU; and others) picks an existing item to remove; eviction rate is a separate signal from hit ratio and matters because a high eviction rate on an undersized cache is a common root cause of a lower-than-expected hit ratio.
- Measuring it in production: most cache clients and cache servers (e.g., Redis's
INFOcommand) expose hit/miss counters directly; export them to your metrics system and alert on a sustained drop, not a single noisy data point. - What the number implies for capacity sizing: a hit ratio well below what the workload's theoretical repeat-read rate suggests usually means the cache is too small (working set does not fit, so useful entries get evicted before they are reused again) rather than a fundamental mismatch between the workload and caching.
- What it implies for service-level objectives (SLOs): because a miss is typically an order of magnitude or more slower than a hit, the hit ratio directly predicts your p95/p99 latency distribution; a drop in hit ratio should be treated as a leading indicator of a latency SLO breach, not just a cache-health curiosity.
Worked example
A cache receiving 100,000 requests per minute returns 80,000 hits. Hit ratio is 80,000/100,000=0.80, or 80 percent. If a hit takes 2ms and a miss takes 150ms, the blended average latency is 0.80×2ms+0.20×150ms=1.6ms+30ms=31.6ms. If the hit ratio drops to 60 percent with the same per-hit and per-miss costs, blended latency rises to 0.60×2+0.40×150=1.2+60=61.2ms, roughly doubling despite "only" a 20-point hit-ratio drop, which is why hit ratio is such a sensitive early-warning signal.
Trade-offs and pitfalls
A hit ratio number without context is misleading: 80 percent is excellent for a highly diverse, long-tail workload and mediocre for a workload with a small, stable, highly-repeated key set that should be closer to 99 percent. Always compare against the workload's own historical baseline and theoretical ceiling, not a generic industry number.
How do you decide whether to introduce a cache for a given service endpoint? Describe the signals and measurements you would collect, the tests you would run (load, latency, profiling), and the criteria that justify adding an in-process cache, a shared cache (Redis), or a CDN. Include considerations for cost, operational complexity, and correctness.
Sample Answer
Direct answer
Decide whether to add a cache by measuring the actual read pattern (how often the same value is requested, how expensive it is to produce, and how much staleness is tolerable), not by defaulting to caching every endpoint; a cache with a low repeat-read rate or zero staleness tolerance is a cost with no real benefit.
Structured elaboration
- Signals to collect: request rate for the same key/query (does the same data actually get read repeatedly, or is nearly every read unique), the cost of producing the value (a fast, cheap lookup gains little from caching even if repeated), and the data's staleness tolerance (how quickly must a change be visible).
- Tests to run: a load test comparing latency and backend load with and without a proposed cache, and a profile of the actual query/computation to confirm it is genuinely a meaningful cost worth caching against.
- Criteria for choosing a cache tier: an in-process cache fits data that is cheap to duplicate per instance and does not need cross-instance consistency; a shared cache (Redis) fits data that benefits from being consistent across instances or too large to duplicate per instance; a content delivery network (CDN) fits public, non-personalized content that benefits from being close to users geographically.
- Cost: weigh the infrastructure and operational cost of adding a caching layer (a new dependency to monitor, secure, and keep available) against the actual load/latency benefit measured above; a marginal benefit may not justify the added complexity.
- Operational complexity: caching adds invalidation logic, a new failure mode (cache unavailable), and another thing to monitor; these costs are real even when the caching decision is otherwise sound, and should be weighed explicitly.
- Correctness: if the data's staleness tolerance is effectively zero (a value that must always reflect the absolute latest state, with no acceptable delay), caching adds risk without benefit, since any caching mechanism introduces at least a small window of potential staleness.
Worked example
An endpoint returning a real-time stock quote, requested uniquely per symbol per user with essentially no repeat reads within any meaningful window, and requiring zero staleness tolerance: this fails on both the "does the same value get read repeatedly" test and the "can staleness be tolerated" test, making it a poor caching candidate regardless of how expensive the underlying computation is. Contrast with a product description, read thousands of times per hour by different users for the same handful of popular items, changing rarely: this passes both tests clearly.
Trade-offs and pitfalls
Caching by default, without measuring the actual read-repetition rate, either wastes cache capacity on data that gets no benefit or, worse, introduces a staleness risk on data that could not tolerate it; always start from measurement, not habit. The decision is not binary per endpoint; the same service can have some data that benefits enormously from caching and other data (even on the same page) that should never be cached, and treating the whole endpoint uniformly misses that nuance.
Unlock Full Question Bank
Get access to all 7 Caching Strategies and Distributed Caching interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.