Redis Caching Patterns in Node.js (2026): Cache-Aside, Write-Through & Cache Stampede Solutions

Database read operations are frequently the primary bottleneck in web applications. Integrating Redis as an in-memory caching layer dramatically reduces database load and lowers API latency from hundreds of milliseconds to sub-milliseconds.

However, improper caching implementations lead to stale data, memory exhaustion, and system outages during traffic spikes caused by Cache Stampedes (Thundering Herd Problem). This guide covers production-grade caching patterns and mitigation strategies in Node.js.

Core Caching Architecture Patterns

Pattern Mechanism Pros Cons
Cache-Aside (Lazy Loading) Application reads from cache. On miss, reads from DB, updates cache, and returns result. Resilient to cache failure; only requested data is cached. Cache miss penalty on initial request; potential temporary stale data.
Write-Through Application writes to cache first, and the cache synchronously writes to the DB. Cache is always consistent with the database. Higher write latency; unused data may pollute memory.
Write-Behind (Write-Back) Application writes to cache; cache asynchronously persists to DB in background batches. Ultra-fast write response times. Risk of data loss if Redis crashes before async DB sync finishes.

1. Implementing the Cache-Aside Pattern in Node.js

npm install ioredis
// services/userService.js
import Redis from 'ioredis';
import User from '../models/User.js';

const redis = new Redis(process.env.REDIS_URL);
const CACHE_TTL = 3600; // Time To Live: 1 Hour in seconds

export const getUserById = async (userId) => {
  const cacheKey = `user:${userId}`;

  // 1. Try reading from Redis Cache
  const cachedUser = await redis.get(cacheKey);
  if (cachedUser) {
    return JSON.parse(cachedUser); // Cache Hit
  }

  // 2. Cache Miss: Fetch from Primary Database
  const user = await User.findById(userId).lean();
  if (!user) return null;

  // 3. Populate Redis Cache with expiration time (TTL)
  await redis.set(cacheKey, JSON.stringify(user), 'EX', CACHE_TTL);

  return user;
};

2. Preventing Cache Stampede (Thundering Herd Problem)

A Cache Stampede occurs when a heavily requested cached key expires. Hundreds or thousands of concurrent incoming requests experience a cache miss simultaneously, all attempting to query the database and re-populate the cache at the same time, crashing the database.

Solution: Distributed Locking (Redlock Strategy)

Ensure that only one request queries the database on a cache miss while other concurrent requests wait or read the updated cache value.

// utils/fetchWithLock.js
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL);

export const fetchWithLock = async (cacheKey, fetchDbFn, ttl = 3600) => {
  // 1. Check cache
  const cachedData = await redis.get(cacheKey);
  if (cachedData) return JSON.parse(cachedData);

  const lockKey = `lock:${cacheKey}`;
  // Acquire an atomic lock with 5-second auto-release timeout (NX = Only if Not Exists)
  const acquiredLock = await redis.set(lockKey, 'LOCKED', 'EX', 5, 'NX');

  if (acquiredLock) {
    try {
      // Current thread won the lock -> Fetch from Database
      const freshData = await fetchDbFn();
      if (freshData) {
        await redis.set(cacheKey, JSON.stringify(freshData), 'EX', ttl);
      }
      return freshData;
    } finally {
      // Release lock
      await redis.del(lockKey);
    }
  } else {
    // Other threads wait 100ms and retry reading from cache
    await new Promise((resolve) => setTimeout(resolve, 100));
    return fetchWithLock(cacheKey, fetchDbFn, ttl);
  }
};

Redis Caching Best Practices

  • Always Set Time-To-Live (TTL): Every cached key should have an explicit TTL set to prevent orphan keys from filling up memory.
  • Set Eviction Policy: Configure Redis with maxmemory-policy volatile-lru or allkeys-lru so old/unused keys are automatically evicted when memory capacity is reached.
  • Use Key Namespaces: Structure cache keys systematically using colon separators (e.g., tenant_id:entity:id) for clarity and easy key pattern management.
Advertisement
Advertisement