Command Palette

Search for a command to run...

Hectal
PHASE 5Intermediate ~10 min· topic 5 of 5

Topic 5.5

Keyspace Notifications and Client-Side Caching

In one line

Keyspace notifications publish events when keys change or expire, useful for reacting to changes but fire-and-forget. Client-side caching (CLIENT TRACKING) lets apps keep hot keys in local memory while Redis sends invalidation messages when those keys change: a near-cache with server-assisted consistency.

0/5 · 0%

Think of it like this

A newspaper subscription where the publisher calls you if an article you clipped gets corrected. You keep your clipping (local cache) and read it instantly, and you only throw it out when you get the call (invalidation).

Key ideas

  1. 01

    Keyspace notifications: enable with notify-keyspace-events (for example Ex for expired events, KEA for everything). Redis publishes on __keyspace@0__:<key> (event name as message) and __keyevent@0__:<event> (key name as message). They use Pub/Sub, so they're lost if nobody's listening, and in Cluster each node only publishes events for its own keys.

  2. 02

    Expired events fire when Redis actually deletes the key (lazily or in the active cycle), which can be later than the TTL moment. Don't use them as precise timers or as the only trigger for important work.

  3. 03

    Client-side caching: CLIENT TRACKING ON makes Redis remember which keys this connection read; when any of them changes, Redis sends an invalidation (a RESP3 push, or on RESP2 via a redirected Pub/Sub connection on __redis__:invalidate). The client drops the local copy and fetches fresh data next time.

  4. 04

    Modes: default mode tracks exact keys per client (uses server memory, bounded by tracking-table-max-keys); BCAST PREFIX user: PREFIX product: sends invalidations for any change under the prefixes without per-key memory on the server; OPTIN/OPTOUT let the client choose which reads to track. NOLOOP skips invalidations for the client's own writes.

  5. 05

    What you gain: local reads in nanoseconds for hot keys, far fewer network round trips, and relief for hot-key shards. What you give up: memory in every app instance, a short window where a local copy may be stale (between the write and the invalidation arriving), and complexity when connections drop (flush the local cache on reconnect).

  6. 06

    Client support has grown (for example Lettuce, Jedis, redis-py and node-redis have client-side caching support in recent versions, mostly requiring RESP3). Check your client version before relying on it.

Code & diagrams

keyspace-events.redisredis
127.0.0.1:6379> CONFIG SET notify-keyspace-events Ex
OK
127.0.0.1:6379> PSUBSCRIBE __keyevent@0__:expired
# In another terminal:
127.0.0.1:6379> SET temp:1 x PX 100
# The subscriber receives, when the key is actually removed:
1) "pmessage"
2) "__keyevent@0__:expired"
3) "__keyevent@0__:expired"
4) "temp:1"
tracking.redisredis

RESP3 connection: invalidations arrive as push messages on the same connection.

127.0.0.1:6379> HELLO 3
...
127.0.0.1:6379> CLIENT TRACKING ON
OK
127.0.0.1:6379> GET product:9
"{...}"                         # client caches this locally
# Another client runs: SET product:9 "{new}"
-> invalidate: ["product:9"]    # push message; client drops its local copy

# Broadcast mode: no per-key memory on the server
127.0.0.1:6379> CLIENT TRACKING ON BCAST PREFIX product: PREFIX flags:
OK
near-cache.mermaiddiagram
Rendering diagram…

Interview problem

The problem

Hot feature flags read 200K times per second

Every request reads ~20 feature flags from a Redis hash. At 10K requests/sec per service × 20 services, the flags shard is overloaded. Flags change a few times a day and changes should apply within about a second. Design a solution.

You're given

  • 200K+ flag reads/sec
  • Changes rare
  • Propagation within ~1 s
  • Many app instances

The interviewer follows up

01

Why not use keyspace notifications to trigger the refresh?

When it breaks

Business logic that depends on expired-key notifications

What you see

Actions happen late (keys expire lazily) or not at all (the listener was disconnected, or events went to another cluster node).

Fix & prevent

Use a sorted-set scheduler polled by workers for timed actions; treat notifications as optional hints.

Client-side cache not flushed after a reconnect

What you see

Invalidations sent while disconnected are lost; the instance keeps serving stale values indefinitely.

Fix & prevent

Flush the local cache on every reconnect and put a TTL on local entries as a backstop.

Explain it without notes

01

Explain how CLIENT TRACKING keeps a local cache consistent.

Practice

01

Enable expired notifications in the lab and log every expired key for a minute. Then set 1,000 keys with 1 s TTL and compare the log timing with the TTLs.

Trade-offs

  • ↔

    Client-side caching massively reduces load and latency but spends app memory and accepts a tiny staleness window.

  • ↔

    Broadcast tracking saves server memory but sends more invalidations than necessary.

Done when you can

  • I can enable and consume keyspace notifications and know their limits.

  • I can design a near-cache with CLIENT TRACKING or TTL plus Pub/Sub.