System Design Fundamentals

Lesson 4 of 12 · 22 min

x
33%

Caching at Every Layer

Caching stores the result of an expensive operation so future requests return instantly without repeating the work. It exists at every layer: the browser caches responses via Cache-Control headers, a CDN caches content at edge locations, the application caches database results in Redis, and the database itself caches hot pages in its buffer pool. The hardest caching problem is invalidation — knowing when to evict stale data. Write-through caches update the cache on every write, staying consistent at the cost of slower writes. Cache-aside is the most common: read from cache, fall through to the database on miss, then populate the cache for next time. Use TTLs as a safety net so stale data cannot live forever even if invalidation logic misses.

Before
No cache — every read hits the database
// 580 feed reads/sec all hit Postgres directly
async function getUserFeed(userId: string) {
  return db.query(`
    SELECT p.* FROM posts p
    JOIN follows f ON f.followee_id = p.author_id
    WHERE f.follower_id = $1
    ORDER BY p.created_at DESC LIMIT 20
  `, [userId]);
}
After
Cache-aside — hot feeds served from Redis
async function getUserFeed(userId: string) {
  const key = `feed:${userId}`;
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  const feed = await db.query(/* expensive join */);
  // Cache for 60s — feed can be slightly stale
  await redis.setex(key, 60, JSON.stringify(feed));
  return feed;
}

Key Takeaway

Cache at the layer closest to the user — CDN for assets, Redis for computed results, buffer pool for hot database pages.

PreviousNext Lesson