Caching Strategies: CDN, Edge, and App Layer

seo engineering 01

Caching hierarchy: browser to database and everything between

Caching exists at multiple levels, each shaving latency and reducing backend load:

  • Browser caching → Assets stored locally on user devices.
  • CDN caching → Content cached geographically close to users.
  • Edge caching → Computation and dynamic responses cached at the network edge.
  • Application-level caching → In-memory caches like Redis or Memcached for API responses or session data.
  • Database-level caching → Query results or connection pooling for hot paths.

The key to performance is understanding the hierarchy and using each layer appropriately.


CDN caching: static assets and edge optimization

Content Delivery Networks (Cloudflare, Akamai, Fastly, BunnyCDN) cache static assets—images, CSS, JS—reducing latency and bandwidth usage.

Cache-Control headers and TTL strategies

  • Cache-Control: public, max-age=31536000 → Long-lived static assets (hashed filenames).
  • Cache-Control: no-store → Sensitive content.
  • Stale-while-revalidate → Serve old cache while refreshing in background.

Longer TTLs (time-to-live) = fewer origin hits but higher risk of stale data.

Purging strategies and cache invalidation patterns

  • Manual purge → Flush specific assets on deploy.
  • Cache-busting → Use hashed filenames (app.abc123.js).
  • Soft purge → Mark stale, serve until refreshed.
  • Event-driven invalidation → APIs trigger cache purges after updates.

Edge caching: Cloudflare, Fastly, and edge functions

Modern CDNs enable edge caching of dynamic content:

  • Cloudflare Workers / Fastly Compute@Edge → run logic near the user.
  • Cache API responses or HTML fragments (SSR apps).
  • Example: Personalized page with cached shell + live API calls.

Benefits: <50ms latency, reduced origin load.
Challenges: cache invalidation logic must account for user-specific data.


Application-level caching: Redis, Memcached, in-memory

For backend APIs:

  • Redis → Advanced (persistent, supports data structures, pub/sub).
  • Memcached → Lightweight, ephemeral, key-value only.
  • In-memory (Node.js LRU cache, Java Guava) → Fastest, but limited by instance memory.

Use cases:

  • Session storage.
  • Rate limiting.
  • Expensive API call caching.

Database query caching and connection pooling

Databases often become bottlenecks—caching can help:

  • Query caching → Store results of frequent reads.
  • Materialized views → Precomputed results for complex joins.
  • Connection pooling (PgBouncer, ProxySQL) → Reuse DB connections, reducing handshake overhead.

Caution: query caches can stale quickly on write-heavy workloads.


Cache coherence and invalidation strategies

The hardest problem in caching = invalidation. Common approaches:

  • Write-through → Cache updated on every write.
  • Write-behind → Write to cache, sync to DB later.
  • Read-through → Cache checked first, DB only if miss.
  • Event-driven invalidation → Pub/sub signals (Redis Streams, Kafka) to invalidate caches across services.

Consistency trade-off: strong vs eventual.


Performance measurement: cache hit rates and latency impact

Metrics to track:

  • Cache hit ratio (CHR) → % of requests served from cache. Aim for 80%+.
  • Latency reduction → Compare cached vs uncached request times.
  • Origin offload → % of requests bypassing the backend.

Tools: Grafana + Prometheus, CDN dashboards, APM (New Relic, Datadog).


Common anti-patterns and cache stampede prevention

  • Anti-patterns:
    • Over-caching → Serving stale/incorrect data.
    • Under-caching → Missing opportunities to reduce load.
    • One big cache bucket → Lack of segmentation increases invalidation risk.
  • Cache stampede prevention:
    • Locking → One request repopulates cache, others wait.
    • Randomized TTLs → Prevent mass expiry at same time.
    • Background refresh → Pre-warm caches.

Monitoring and debugging cache performance

  • Track cache layer metrics (Redis memory usage, CDN hit rates).
  • Log cache misses with reason (expired, evicted, bypassed).
  • Debug headers in CDNs (e.g., cf-cache-status: HIT/MISS).
  • Use synthetic tests to simulate cache purge/reload scenarios.

The strategy

Caching is not one-size-fits-all—it’s a layered strategy:

  • Browser + CDN for static assets.
  • Edge caching for low-latency APIs.
  • App-level caches for session data and heavy computations.
  • DB caching for query hot spots.

The art is in choosing the right layer for the right data, balancing performance with consistency.


FAQs

What’s the difference between CDN caching and edge caching?
CDN = static asset delivery; edge = dynamic API/content logic near users.

Is Redis always better than Memcached?
Redis is more powerful, but Memcached is lighter for simple key-value caching.

How do I know if caching works?
Check cache hit ratio—if <50%, you may be misconfigured.