Urgent.News

What's breaking now, across thousands of outlets.

Tech

Redis Is Not Your Database: Designing Cache Failures, Fallbacks, and Resilient Backends

Redis can make a backend dramatically faster. It can also make a backend dramatically more fragile if the architecture treats the cache as a dependency that must always be available. The distinction is simple: A database stores authoritative data. A cache stores data that can be recreated. Once that distinction is understood, Redis failure becomes an architectural problem rather than simply an…

Redis can significantly boost backend performance by acting as a caching layer between client requests and the primary database. However, treating Redis as an always-available dependency can make the backend fragile. Understanding the key difference between databases, which store authoritative data, and caches, which can recreate data, is crucial for proper architectural design.

Implementing the Cache-Aside Pattern with Redis reduces repeated database reads and improves response latency for frequently accessed data. When Redis becomes unavailable, the application can safely fall back to the primary datastore, avoiding a complete system failure. However, a naive implementation that treats Redis as a non-fallible dependency can create new failure modes.

When Redis goes down, a naive fallback can cause a cascade of failures, overwhelming the database and degrading API performance. To avoid this, the application should implement deliberate timeouts, allowing it to quickly fallback to the database if Redis is slow or unavailable. Circuit breakers can further prevent repeated failures by redirecting traffic to the database after a controlled recovery period.

Retries should be used cautiously, as uncontrolled retries can exacerbate outages by generating additional traffic towards an already unhealthy dependency. Timeouts, exponential backoff, retry limits, jitter, and circuit breakers are essential when dealing with retries.

A cache stampede can occur when many users attempt to fetch the same cached value simultaneously after it expires. Mitigation strategies include TTL randomization, request coalescing, stale-while-revalidate, background refresh, and controlled cache warming. Cache expiration itself can generate traffic, so mitigation strategies are necessary.

In situations where data freshness is not critical, serving stale data can be preferable to failing the request. This approach, known as graceful degradation, preserves useful functionality even when a component is unhealthy.

Redis should have a clear responsibility within the system architecture. Databases should serve as the source of truth, while Redis focuses on performance optimization, and Node.js handles application and business logic. Load balancers manage traffic distribution, and monitoring tools detect and diagnose issues.

Clear separation of responsibilities allows for easier reasoning about failure behavior. If the database fails, the application faces a different problem than if Redis fails. If the primary database fails, the system may lose its source of truth, while a Redis failure primarily impacts performance. Treating both failures equally is an architectural mistake.

Monitoring the cache, rather than just the application, is essential for maintaining system health. Useful cache metrics include cache hit ratio, cache miss ratio, Redis latency, connection errors, timeout rates, memory usage, eviction rate, command latency, and connection count. Detecting falling cache hit rates can indicate issues like an undersized cache, poorly keyed data, aggressive expiration, or insufficient caching benefits.

In summary, Redis can enhance backend performance, but it introduces new failure modes if treated as an always-available dependency. By understanding the distinction between databases and caches, implementing proper fallback mechanisms, using timeouts and circuit breakers, avoiding unnecessary retries, mitigating cache stampedes, serving stale data when appropriate, adhering to clear architectural boundaries, and monitoring the cache, developers can build more resilient and reliable backend systems.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

I labelled a dependency cycle "unknown" and called it rigour

I have spent a while arguing that checks need a third value: not every result is pass or fail, and folding "I could not determine this" into either one is how a suite starts lying.

  • The author proposes labeling dependency cycles as UNKNOWN instead of FAIL.
  • UNKNOWN indicates undetermined outcomes, while FAIL denotes actual failures.
  • Implementing this distinction prevents hiding failures and enables informed decisions.

More from Sunday 13 September →