PrepZone Logo
PrepZone

Redis Pub/Sub and Streams

Fire-and-forget messaging vs durable consumer groups for event pipelines.

Why this matters

  • Checkout emits events (payment captured, shipment queued) that multiple services must consume — Streams give durability and at-least-once delivery; Pub/Sub does not.
  • Confusing the two causes lost messages during deploys or network blips when nobody was subscribed.
  • Redis Streams are lighter than Kafka for moderate throughput but still require explicit consumer-group design.
StringSessions, counters
ListQueues, feeds
SetUnique tags
Sorted SetLeaderboards
HashUser profiles
StreamEvent log
Each type maps to a common backend pattern.

Pub/Sub: live notifications

Publishers send to channels; subscribers receive messages in real time. No persistence, no replay, no acks.

Java
SUBSCRIBE order:updates
# separate terminal:
PUBLISH order:updates "{\"orderId\":\"9001\",\"status\":\"SHIPPED\"}"
PSUBSCRIBE inventory:*
PUBLISH inventory:SKU-8842 "restocked"

VaultCommerce pushes "your order shipped" toast notifications to connected browsers via a WebSocket gateway subscribed to order:{userId} channels. If the gateway restarts, missed messages are acceptable — the user refreshes and reads Postgres.

Pub/Sub characteristics

  • Ephemeral — no message history; offline subscribers miss data.
  • Fan-out — every subscriber on the channel receives a copy.
  • Low latency — ideal for live dashboards and typing indicators.
  • No ordering guarantees across channels.

Streams: durable event log

Streams append entries with auto IDs (timestamp-sequence). Consumer groups partition work across workers with explicit acknowledgements.

Java
XADD order-events * orderId 9001 event PAYMENT_CAPTURED amount 129.99
XADD order-events * orderId 9001 event FULFILLMENT_QUEUED
XLEN order-events
XRANGE order-events - + COUNT 10

XGROUP CREATE order-events fulfillment $ MKSTREAM
XREADGROUP GROUP fulfillment worker-1 COUNT 5 STREAMS order-events >
XACK order-events fulfillment 1710000000000-0
XPENDING order-events fulfillment

The > ID reads only new messages for the group. Pending entries list (PEL) tracks in-flight work — replay with XCLAIM when a worker dies mid-processing.

VaultCommerce order pipeline

Java
import redis

r = redis.Redis(decode_responses=True)
STREAM = "order-events"
GROUP = "email-service"

def ensure_group():
    try:
        r.xgroup_create(STREAM, GROUP, id="0", mkstream=True)
    except redis.ResponseError as e:
        if "BUSYGROUP" not in str(e):
            raise

def process_batch(consumer: str, count: int = 10):
    entries = r.xreadgroup(GROUP, consumer, {STREAM: ">"}, count=count, block=2000)
    for _stream, messages in entries:
        for msg_id, fields in messages:
            send_receipt_email(fields)
            r.xack(STREAM, GROUP, msg_id)

Email and analytics services share the order-events stream via separate consumer groups — each group tracks its own progress.

Quick recall

Everything you need if you only revisit this box.

  • Pub/Sub: ephemeral fan-out for live UI updates — no persistence or replay.
  • Streams: append-only logs with IDs, range reads, and consumer groups.
  • XREADGROUP + XACK delivers at-least-once processing with a pending list.
  • VaultCommerce uses Pub/Sub for browser toasts and Streams for order side-effects.
  • Separate consumer groups let email and analytics progress independently.
  • Choose Streams when a missed message during restart is unacceptable.

Test yourself

Answer these before moving on — recall is what makes it stick.