Module F-8·18 min read

SUBSCRIBE, PUBLISH, PSUBSCRIBE for pattern channels, the no-persistence guarantee, keyspace notifications, horizontal scaling across app servers, and when to use Streams instead of Pub/Sub.

F-7 — Pub/Sub and the Message Fanout Model

Who this module is for: You have heard of Redis Pub/Sub and may have used it for real-time notifications or chat. But you have not understood its most important limitation — messages are not persisted and any subscriber that is offline loses them permanently. This module covers the full Pub/Sub model, pattern-based subscriptions, its correct use cases, and when to use Streams instead.


What Pub/Sub Is

Redis Pub/Sub is a message fanout system. Publishers send messages to named channels. Subscribers listening on those channels receive every message in real time. Redis acts as the broker — it receives the message from the publisher and immediately delivers it to all connected subscribers.

text

The key characteristic: delivery is best-effort and immediate. If no subscriber is connected when a message is published, the message is gone. If a subscriber disconnects and reconnects, it misses all messages published during the disconnection window.


Commands

Subscribing

SUBSCRIBE channel [channel ...]

Once a client issues SUBSCRIBE, it enters a special "subscriber mode." In this mode, the only commands it can issue are SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PING, and RESET. The connection is dedicated to receiving messages.

text

Publishing

PUBLISH channel message

Returns the number of subscribers that received the message (0 if nobody is subscribed).

text

Unsubscribing

UNSUBSCRIBE [channel ...] → unsubscribe from channels; no args = unsubscribe from all

Pattern Subscriptions

PSUBSCRIBE subscribes to channels matching a glob pattern:

text
text

Pattern messages include the matched channel name in the received message:

text

Inspecting Active Pub/Sub State

text
text

Message Delivery Guarantees (and the Lack Thereof)

This is the most important thing to understand about Redis Pub/Sub:

There are no delivery guarantees.

  • If a subscriber is offline when a message is published → message lost
  • If a subscriber's connection drops mid-delivery → message lost (TCP retransmission handles byte-level loss, but Redis does not retry at the application level)
  • If the Redis server goes down → all pending messages lost
  • Messages are not stored anywhere — there is no way to replay past messages
  • A slow subscriber receiving messages faster than it can process them will have messages buffered in Redis's output buffer. If the buffer exceeds client-output-buffer-limit, Redis disconnects the client and the remaining buffered messages are lost.

PUBLISH returns immediately regardless of whether subscribers received the message.

This is fundamentally different from a message queue (BullMQ, RabbitMQ, Kafka). A queue persists messages until a consumer acknowledges them. Pub/Sub does not.


Building a Real-Time Notification System

Here is a typical Pub/Sub pattern in a Node.js backend:

typescript

Why two separate connections? A subscribed connection is in subscriber mode — you cannot issue normal commands on it. You need a separate connection for everything else (including PUBLISH).

Pattern Subscription in Node.js

typescript

Horizontal Scaling: One Subscriber Per Server

When you run multiple application servers behind a load balancer, each server has its own WebSocket clients. A message published on server A's Redis connection needs to reach WebSocket clients on servers B and C.

The solution is to have every application server subscribe to the relevant channels. When a message is published (by any server), Redis delivers it to all subscribers — including the subscribers on every application server:

text

This is the correct pattern for scaling real-time features across application servers. Each server maintains its own subscriber connection per channel family.


Pub/Sub vs Streams

The question comes up constantly: "should I use Pub/Sub or Streams for this?" Here is the honest answer:

ConcernPub/SubStreams
Message persistenceNo — fire and forgetYes — messages stored until explicitly deleted
Replay missed messagesNoYes — consumers can read from any position
Consumer groups (competing consumers)NoYes — multiple consumers sharing a stream
Message acknowledgementNoYes — XACK
Offline consumersMessages lostMessages waiting when consumer reconnects
Delivery guaranteeAt-most-onceAt-least-once (with ACK)
LatencyLowest possibleSlightly higher (storage + ACK overhead)
ComplexitySimpleModerate

Use Pub/Sub when:

  • You need real-time fanout to many subscribers simultaneously
  • Missing messages during disconnection is acceptable (or impossible, e.g., the subscriber is always connected)
  • You are broadcasting to WebSocket/SSE clients where reconnecting clients will reload state anyway
  • Cache invalidation signals (you do not need to replay past invalidations)
  • Live activity feeds where new users see only new events, not history

Use Streams when:

  • Messages must not be lost
  • Consumers may go offline and must catch up on missed messages
  • You need multiple independent consumers processing the same stream (consumer groups)
  • You need durable event sourcing or audit logs
  • Job queues where every job must be processed exactly once

Streams are covered in depth in P-4.


Client Output Buffer Limits

Redis has a protection mechanism for slow Pub/Sub subscribers. If a subscriber cannot read messages fast enough, they accumulate in Redis's output buffer for that client connection. If the buffer exceeds the configured limit, Redis forcibly disconnects the slow subscriber.

Default configuration:

client-output-buffer-limit pubsub 32mb 8mb 60

This means: disconnect a Pub/Sub subscriber if their output buffer exceeds 32MB at any point, or if it stays above 8MB for more than 60 consecutive seconds.

This is a safety valve — a slow subscriber should not cause Redis to run out of memory buffering messages for them. In practice, if your subscriber is consistently hitting this limit, it means your publisher is producing faster than the subscriber can consume, and you should consider Streams with consumer groups instead.


Keyspace Notifications: Pub/Sub for Redis Events

Redis can publish internal events to Pub/Sub channels when keys are modified — when a key expires, is deleted, or is written. This feature is called keyspace notifications.

Enable it in redis.conf or at runtime:

CONFIG SET notify-keyspace-events "KEA"

The value is a combination of flags:

  • K — keyspace events (channel: __keyspace@{db}__:{key})
  • E — keyevent events (channel: __keyevent@{db}__:{event})
  • g — generic commands (DEL, EXPIRE, RENAME)
  • $ — String commands
  • l — List commands
  • z — Sorted Set commands
  • x — expired events
  • e — evicted events
  • A — alias for g$lszxet (all events)
text

Keyspace notifications are useful for:

  • Cache warming triggers (when a key expires, recompute and re-cache it)
  • Session expiry hooks (notify your app when a session token expires)
  • Monitoring and alerting (react when specific keys are deleted or modified)

Caution: Keyspace notifications add overhead to every Redis write operation. Enable them only for the event types you actually need, and disable them if you are not using them.


Summary

  • Pub/Sub is fire-and-forget fanout: messages are delivered to all connected subscribers instantly and not persisted
  • SUBSCRIBE puts a connection in subscriber-only mode; you need a separate connection for PUBLISH and other commands
  • PSUBSCRIBE subscribes to channels matching a glob pattern
  • There are no delivery guarantees — offline subscribers miss messages permanently
  • For real-time WebSocket/SSE fanout across app servers, have every server subscribe — Redis delivers to all
  • Use Streams when you need durability, replay, or consumer groups
  • Client output buffer limits (client-output-buffer-limit pubsub) protect Redis from slow subscribers
  • Keyspace notifications let you subscribe to Redis's internal events (key expiry, deletion, etc.)

Next: F-8 — Streams: Append-Only Logs and Consumer Groups — the durable alternative to Pub/Sub that gives you persistent message storage, consumer groups, and at-least-once delivery guarantees.


Knowledge Check

A developer is building a real-time chat application using Redis Pub/Sub. When a user sends a message, it is published to the chat:global channel. A user complains that when they momentarily lose their internet connection and reconnect a minute later, they do not see the messages that were sent while they were offline. Why does this happen, and what is the proper architectural fix?


In a Node.js microservice, you need to listen for global configuration updates via Pub/Sub while occasionally writing new configurations back to Redis. Why do you need to instantiate *two* separate Redis client connections in your code?


An application relies on Redis Keyspace Notifications to trigger a background job whenever a caching key expires. The team enabled this by running CONFIG SET notify-keyspace-events "Ex". Under heavy load, they notice some background jobs are never triggering, even though the keys are definitely expiring. Given the mechanics of Pub/Sub, what is the fundamental flaw in this architecture?

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.