Module P-8·16 min read

notify-keyspace-events configuration, the event type matrix (key expiry, deletion, Set/List/Hash writes), subscribing to expiry for cache warming, and the critical limitation: keyspace notifications are at-most-once.

P-8 — Keyspace Notifications and Event-Driven Architectures

Who this module is for: You want to trigger application logic when a Redis key expires, is deleted, or is modified — without polling. Keyspace notifications let you subscribe to Redis's internal events via Pub/Sub. This module covers the configuration, the event type matrix, practical patterns, and the critical limitation that most documentation buries at the bottom.


What Keyspace Notifications Are

Keyspace notifications allow Redis to publish messages to Pub/Sub channels when specific events occur — a key expires, a key is written, a key is deleted. Your application subscribes to these channels and reacts to events.

This enables:

  • Cache warming: re-populate a key when it expires
  • Audit logging: record every mutation to sensitive keys
  • Event-driven workflows: trigger downstream logic when a key is set
  • Session expiry hooks: notify your app when a session token expires

Enabling Keyspace Notifications

By default, keyspace notifications are disabled (they add CPU overhead to every write operation).

CONFIG SET notify-keyspace-events "KEA"

Or in redis.conf:

notify-keyspace-events "KEA"

The Event Flag String

The value is a combination of flag characters:

Event type (what happened):

  • g — generic commands (DEL, EXPIRE, RENAME, COPY)
  • $ — String commands (SET, GETSET, SETRANGE, APPEND, INCR, etc.)
  • l — List commands (LPUSH, RPUSH, LPOP, RPOP, LMOVE, etc.)
  • s — Set commands (SADD, SREM, SPOP, etc.)
  • h — Hash commands (HSET, HDEL, etc.)
  • z — Sorted Set commands (ZADD, ZINCRBY, ZREM, etc.)
  • x — Expired events (key expired by TTL)
  • e — Evicted events (key evicted by maxmemory-policy)
  • t — Stream commands
  • d — Module key type events
  • m — Key miss events (when a command targets a non-existent key — generates many events)
  • A — Alias for g$lszxet (all events except key misses)

Channel type (where to publish):

  • K — Keyspace events: channel is __keyspace@{db}__:{key}; message is the event name
  • E — Keyevent events: channel is __keyevent@{db}__:{event}; message is the key name

You must specify at least one channel type (K or E) and at least one event type.

Common configurations:

text

The Two Channel Types

Keyspace Channels (K)

Channel: __keyspace@{db}__:{key}
Message: the event name (e.g., "expired", "set", "del")

Subscribe to a specific key to receive all events affecting it:

text

Keyevent Channels (E)

Channel: __keyevent@{db}__:{event}
Message: the key name

Subscribe to a specific event type to receive it for all keys:

text

For most use cases (cache expiry hooks, session expiry), __keyevent@0__:expired is the right channel.


Practical Pattern: Cache Re-Population on Expiry

When a cached value expires, automatically re-populate it:

typescript

Important limitation: The notification fires after the key is deleted. There is a brief window where the key does not exist in Redis. Re-population via the notification handler closes this window, but it is not instantaneous. If a request arrives during this window, it sees a miss.


Practical Pattern: Session Expiry Hook

Notify your application when a user's session token expires:

typescript

Practical Pattern: Audit Log for Sensitive Keys

Log all modifications to a sensitive key namespace:

typescript

The Critical Limitation: At-Most-Once Delivery

This is the most important thing to understand about keyspace notifications, and it is often buried in documentation:

Keyspace notifications use Pub/Sub internally. Pub/Sub has no delivery guarantees.

If your subscriber is disconnected when an event fires — Redis restart, network blip, subscriber process crash — the notification is permanently lost. There is no replay. The key expired, and your application never found out.

Consequences:

  • Cache re-population handler misses expiry → cache is not re-populated → next request is a miss (relatively benign)
  • Session expiry handler misses expiry → cleanup logic does not run → sessions left in database (moderate)
  • Audit logger misses events → incomplete audit trail (severe for compliance)

Keyspace notifications are not suitable for:

  • Authoritative event processing where every event must be handled
  • Audit logs that must be complete
  • Any workflow where missing an event causes permanent data inconsistency

For reliable event delivery, use Streams. Configure your application to write to a Stream on every relevant Redis write, and process the Stream with consumer groups and ACK semantics.


Performance Impact

Every Redis write command that matches the configured notification flags generates an internal event and publishes it to the relevant Pub/Sub channels. This adds CPU overhead to the write path.

Rough overhead:

  • "Ex" (expiry only): minimal — only triggered on TTL-based key deletion
  • "KEA" (all events): 5–15% additional CPU per write operation for a write-heavy instance

Do not enable "KEA" on a write-heavy production instance without benchmarking.

For most use cases:

  • "Ex" for expiry hooks (lowest overhead)
  • "K$" for String write monitoring
  • "Kxg" for expiry + generic commands (DEL, EXPIRE) monitoring

Key Miss Events (m flag)

notify-keyspace-events "KEm"

Key miss events fire whenever a command accesses a key that does not exist. This can generate enormous event volume on a cache with a non-trivial miss rate. Do not enable key miss events unless you have a specific reason and have measured the overhead.


Keyspace Notifications vs Streams for Event-Driven Design

ConcernKeyspace NotificationsRedis Streams
Delivery guaranteeAt-most-once (Pub/Sub)At-least-once (with ACK)
Replay missed eventsNoYes
Event sourceRedis internal eventsApplication-defined events
Configurationnotify-keyspace-events in redis.confApplication writes to stream
OverheadCPU on every matching writeStorage per event
Best forBest-effort hooks (cache warming)Authoritative event processing

The pattern for reliable event-driven Redis:

  1. Your application writes data to Redis
  2. Your application also writes an event to a Redis Stream
  3. Consumers process the Stream with consumer groups and ACK

Keyspace notifications are a convenience layer — they are fine for non-critical triggers where occasional missed events are acceptable.


Summary

  • Enable with CONFIG SET notify-keyspace-events "flags" — disabled by default due to CPU overhead
  • Two channel types: Keyspace (__keyspace@{db}__:{key} → event name) and Keyevent (__keyevent@{db}__:{event} → key name)
  • For expiry hooks: subscribe to __keyevent@0__:expired with flag "Ex"
  • Critical limitation: at-most-once delivery — notifications are lost if the subscriber is disconnected
  • Not suitable for authoritative event processing, audit logs, or any workflow requiring guaranteed delivery
  • Overhead scales with event flag breadth — "Ex" is minimal; "KEA" is significant on write-heavy instances
  • For reliable event-driven architectures: write to Redis Streams instead and use consumer groups

Next: P-9 — Session Management Patterns — storing sessions as Hashes vs JSON strings, sliding expiry, multi-device sessions, and the consistency trade-offs when reading from replicas.


Knowledge Check

A team needs to build a "Session Expiry Hook" to clean up database records whenever a Redis session key naturally expires via TTL. They want to listen for expiration events across all keys matching the session:user:* pattern. What is the most efficient configuration for notify-keyspace-events to achieve this?


An e-commerce platform implements a strict compliance requirement: every single modification (write, delete, expire) to their Redis pricing:catalog must be logged to a PostgreSQL audit table. They implement a Node.js microservice that subscribes to __keyspace@0__:pricing:catalog with notify-keyspace-events "KEA". During a massive Black Friday traffic spike, the microservice crashes due to OOM and takes 30 seconds to restart. What is the state of their audit log for those 30 seconds?


A developer wants to proactively monitor cache misses to tune their caching strategy. They enable notify-keyspace-events "KEm" in production to receive an event every time a command requests a non-existent key. What is the most likely consequence of this change on a heavily loaded web application with a 15% cache miss rate?

Test your knowledge with more question sets

Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.