Database & Cloud

Architecting Multi-Layer Cache Invalidation: Redis Pub/Sub, Edge CDNs, and Stale-While-Revalidate Patterns

Laurince Quijano
Laurince Quijano
September 8, 20267 min read
Architecting Multi-Layer Cache Invalidation: Redis Pub/Sub, Edge CDNs, and Stale-While-Revalidate Patterns

There are only two hard things in Computer Science: cache invalidation and naming things. As high-traffic web applications scale to handle millions of daily active users, relying on a single database or serverless instance creates severe latency bottlenecks.

To maintain sub-50ms global page paints, modern enterprise architectures implement a Multi-Layer Cache Pipeline: 1. L1 Edge CDN Cache: Serves static HTML assets directly from edge PoPs worldwide. 2. L2 Application In-Memory / Redis Cache: Caches expensive database queries, GraphQL responses, and session tokens. 3. L3 Database Engine: The underlying persistent source of truth (PostgreSQL / MySQL / Magento EAV).

However, introducing multiple cache layers creates data consistency challenges. When an administrator updates a product price or stock count, stale data can linger across CDN edge nodes and Redis instances.

Here is a technical blueprint for engineering an automated, multi-tier cache invalidation pipeline using Redis Pub/Sub, Edge CDNs, and Stale-While-Revalidate (SWR) headers.


The Multi-Layer Caching Topology

code
[ Client Browser ]
        │ (1. HTTP Request)
        ▼
[ L1: Edge CDN (Vercel / Cloudflare) ] ── (Hit: < 50ms) ──► Return Cached HTML/JSON
        │ (Miss / Stale)
        ▼
[ L2: Distributed Redis Cluster ]    ── (Hit: < 5ms)  ──► Return Cached Data
        │ (Miss)
        ▼
[ L3: Primary SQL Database ]         ── (Query: 100ms) ──► Write Back to Redis & CDN

Step-by-Step Implementation

1. Configuring Stale-While-Revalidate (SWR) HTTP Headers SWR allows the browser or CDN edge to instantly return a cached payload while asynchronously triggering a non-blocking background fetch to refresh stale data:

typescript
// app/api/catalog/products/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  const products = await fetchProductsFromDatabase();

  return NextResponse.json(products, {
    headers: {
      // s-maxage=60: Fresh on CDN edge for 60 seconds
      // stale-while-revalidate=86400: Serve stale payload up to 24 hours while revalidating in background
      "Cache-Control": "public, s-maxage=60, stale-while-revalidate=86400",
      "CDN-Cache-Control": "public, s-maxage=300",
    },
  });
}

2. Broad-Scale Cache Invalidation via Redis Pub/Sub When a database update occurs, microservices or database triggers publish an invalidation event to a Redis Pub/Sub channel. Subscribed application workers immediately purge regional Redis keys and trigger CDN API purges:

typescript
// lib/cache/invalidation.ts
import Redis from "ioredis";

const publisher = new Redis(process.env.REDIS_URL!);
const subscriber = new Redis(process.env.REDIS_URL!);

const INVALIDATION_CHANNEL = "cache:invalidate:events";

export interface InvalidationEvent {
  entity: "product" | "category" | "user";
  id: string;
  tags: string[];
}

// 1. Publisher: Triggered on database write / CMS webhook
export async function publishCacheInvalidation(event: InvalidationEvent) {
  await publisher.publish(INVALIDATION_CHANNEL, JSON.stringify(event));
}

// 2. Subscriber: Listens across distributed app server nodes
subscriber.subscribe(INVALIDATION_CHANNEL, (err) => {
  if (err) console.error("Failed to subscribe to invalidation channel:", err);
});

subscriber.on("message", async (channel, message) => {
  if (channel === INVALIDATION_CHANNEL) {
    const event: InvalidationEvent = JSON.parse(message);
    console.log(`Received cache purge trigger for ${event.entity}: ${event.id}`);

    // Purge regional Redis keys
    const redisKey = `cache:${event.entity}:${event.id}`;
    await publisher.del(redisKey);

    // Call CDN Purge API (e.g. Cloudflare / Vercel Edge Cache)
    await purgeCdnCache(event.tags);
  }
});

async function purgeCdnCache(tags: string[]) {
  if (process.env.CLOUDFLARE_ZONE_ID && process.env.CLOUDFLARE_API_TOKEN) {
    await fetch(
      `https://api.cloudflare.com/client/v4/zones/${process.env.CLOUDFLARE_ZONE_ID}/purge_cache`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ tags }),
      }
    );
  }
}

Cache Invalidation Patterns Comparison

PatternInvalidation SpeedDatabase LoadData Consistency
Time-To-Live (TTL) ExpirationPassive (Waits for timer expiration)ModerateEventual Consistency (Delayed updates)
Stale-While-Revalidate (SWR)Instant (Serves stale; revalidates async)LowHigh (Updated on next request)
Event-Driven Redis Pub/SubImmediate (< 10ms purge across nodes)MinimalStrict Consistency

Best Practices for Enterprise Multi-Layer Caching

  1. Prevent Cache Stampedes (Thundering Herd): When a popular cache key expires under high traffic, thousands of concurrent requests hit the database simultaneously. Implement Mutex Locks or Singleflight pattern so only 1 request queries the database while others wait for the refreshed key.
  2. Tag-Based Group Invalidation: Assign hierarchical tags (e.g. product-123, category-apparel) to cache items so invalidating a parent category automatically purges all child product pages.
  3. Monitor Cache Hit Ratios: Aim for a > 95% L1 CDN hit ratio and a > 99% L2 Redis hit ratio to ensure database CPU usage remains low during traffic surges.

By combining SWR edge headers with event-driven Redis Pub/Sub invalidation, enterprise web applications achieve sub-50ms global latency while guaranteeing strict content consistency across all devices!

Laurince Quijano
Written By

Laurince Quijano

Full-Stack Architect

Award-winning Web Developer with 10+ years of experience. Specializing in enterprise Next.js performance, custom Magento plugins, API integrations, and Technical SEO infrastructure.

Need premium developer consulting?

Let's discuss how we can build API split checkouts, dynamic Next.js interfaces, or speed audits for your business.

Get in Touch