patternjavascriptredisMajor
Redis pub/sub vs Streams: choosing the right messaging primitive
Viewed 0 times
redispubsubstreamsXADDXREADGROUPXACKconsumer groupmessage lossdurability
Error Messages
Problem
Redis PUBLISH/SUBSCRIBE drops messages if no subscriber is connected at publish time. Using pub/sub for work queues or guaranteed delivery causes silent message loss under deployment restarts or consumer lag.
Solution
Use pub/sub only for ephemeral real-time notifications where loss is acceptable (e.g. live cursor positions). Use Redis Streams (XADD/XREADGROUP) for durable, at-least-once delivery with consumer groups and acknowledgements.
Why
Pub/sub is fire-and-forget at the Redis level — the server keeps no history. Streams persist entries and track per-consumer delivery state with XACK, enabling retry on crash without a separate dead-letter queue.
Gotchas
- SUBSCRIBE blocks the connection; you need a dedicated Redis client for subscriptions
- Pattern subscriptions (PSUBSCRIBE) can match millions of channels and cause CPU spikes
- Streams grow unboundedly unless trimmed with XADD MAXLEN or XTRIM
Code Snippets
Redis Streams producer and consumer with ACK
// Producer
await redis.xadd('jobs', '*', 'type', 'email', 'to', 'user@example.com');
// Consumer group setup (once)
await redis.xgroup('CREATE', 'jobs', 'workers', '$', 'MKSTREAM');
// Consumer reads and acknowledges
const entries = await redis.xreadgroup(
'GROUP', 'workers', 'consumer-1',
'COUNT', 10, 'BLOCK', 2000,
'STREAMS', 'jobs', '>'
);
for (const [stream, messages] of entries) {
for (const [id, fields] of messages) {
await processJob(fields);
await redis.xack('jobs', 'workers', id);
}
}Revisions (0)
No revisions yet.