Caching Strategies

Cache patterns, eviction policies, and CDNs

Caching Strategies

Caching stores copies of frequently accessed data in a faster storage layer to reduce latency and database load. Proper caching can improve performance by 10-100x.

Cache Architecture

Caching Patterns

typescript
// Cache-Aside (Lazy Loading)
// Most common pattern — app manages cache
async function getUser(userId: string): Promise<User> {
  // 1. Check cache first
  const cached = await redis.get(`user:${userId}`);
  if (cached) return JSON.parse(cached); // Cache HIT

  // 2. Cache miss — read from database
  const user = await db.users.findById(userId);

  // 3. Populate cache for next time
  await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));

  return user;
}

// Write-through: Write to cache AND database together
async function updateUser(userId: string, data: Partial<User>) {
  const user = await db.users.update(userId, data);
  await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
  return user;
}

// Write-behind: Write to cache, async write to database
async function updateUserAsync(userId: string, data: Partial<User>) {
  await redis.setex(`user:${userId}`, 3600, JSON.stringify(data));
  queue.publish('user-update', { userId, data }); // async DB update
}

Cache Eviction Policies

  • LRU (Least Recently Used): Evicts the item that hasn't been accessed for the longest time. Most commonly used.
  • LFU (Least Frequently Used): Evicts the item accessed the fewest times. Good for skewed access patterns.
  • TTL (Time To Live): Items expire after a fixed duration. Simple and predictable.
  • FIFO (First In First Out): Evicts oldest items first. Simplest but often least effective.
  • Random Replacement: Randomly selects item to evict. Surprisingly effective in some cases.

💬 What is cache stampede and how to prevent it?

Cache stampede occurs when many requests simultaneously find a cache miss (e.g., after TTL expiry) and all hit the database at once. Prevention: 1) Lock/mutex — only one request refreshes cache, others wait. 2) Jittered TTL — add random offset to expiry times. 3) Early refresh — repopulate before expiry. 4) Stale-while-revalidate — serve stale data while refreshing.