Pub/sub, queues and persistence
Fire-and-forget messaging versus durable streams, reliable queue patterns with lists, and the real durability guarantees of RDB and AOF.
Pub/sub and streams
# pub/sub: at most once, no storage, offline subscribers miss the message
SUBSCRIBE events:orders
PSUBSCRIBE events:*
PUBLISH events:orders '{"id":42,"status":"paid"}'
# streams: durable, replayable, with consumer groups and acknowledgements
XADD stream:orders '*' id 42 status paid
XLEN stream:orders
XRANGE stream:orders - +
XREAD COUNT 10 BLOCK 5000 STREAMS stream:orders $
XGROUP CREATE stream:orders workers $ MKSTREAM
XREADGROUP GROUP workers w1 COUNT 10 BLOCK 5000 STREAMS stream:orders >
XACK stream:orders workers 1726585200000-0
XPENDING stream:orders workers # what was delivered but not acked
XAUTOCLAIM stream:orders workers w1 60000 0-0 # reclaim idle messages| Mechanism | Delivery | Survives restart |
|---|---|---|
| Pub/sub channel | At most once, only to connected subscribers | No — messages are not stored |
| Streams | At least once with consumer groups and XACK | Yes, subject to persistence config |
| List as a queue | At most once if the worker crashes after popping | Depends on persistence |
| List with a processing list | At least once via LMOVE and manual cleanup | Depends on persistence |
⚠️
Pub/sub silently drops messages for any subscriber that is offline or slower than the publisher. If losing a message matters, use a stream with a consumer group — a channel is a notification, not a queue.
Reliable queue patterns
# naive: the message is gone if the worker dies after BRPOP returns
BRPOP queue:email 5
# reliable: move to a per-worker processing list atomically, then remove on success
LMOVE queue:email processing:w3 LEFT RIGHT
LREM processing:w3 1 "m-1" # on success
LPUSH queue:email "m-1" # on failure: put it back
# a separate sweeper re-queues anything stuck too long
LRANGE processing:w3 0 -1BRPOPremoves the element before the work is done, which makes it at-most-once.LMOVEinto a processing list turns it into at-least-once, at the cost of a sweeper for stuck entries.- At-least-once means your handler must be idempotent — the same message can be delivered twice after a retry.
- Streams give you the same guarantee without hand-rolling the processing list:
XPENDINGandXAUTOCLAIMcover worker failure. - Never put cache keys and job keys that must not be lost on the same instance with an
allkeys-*eviction policy.
Persistence and replication
# redis.conf
save 900 1 # RDB snapshot after 1 change in 900s
save 300 10
appendonly yes # AOF on
appendfsync everysec # fsync once per second: at most ~1s of writes lost
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# on a replica
REPLICAOF 10.0.0.10 6379
WAIT 1 100 # block until 1 replica acknowledges, max 100 ms| Option | How it works | What you can lose |
|---|---|---|
| RDB only | Forked point-in-time snapshots, fast restart | Everything since the last snapshot |
AOF everysec | Append each write, fsync about once per second | Up to about one second of writes |
AOF always | fsync on every write | Almost nothing, but much slower |
AOF no | Let the operating system flush | Whatever the OS has not written — can be a lot |
| RDB + AOF | AOF used for replay, snapshot for fast bootstrap | Same as the AOF setting |
Replication is asynchronous by default, so a primary that fails can lose writes that no replica has yet received. WAIT narrows the window but does not eliminate it, and it is not a substitute for a transactional commit protocol.
FAQ
Is Redis a database or a cache?
It can be either, and the durability settings are where you decide. With
appendfsync always and a replica you get a durable store; with RDB snapshots and an LRU policy you have a cache that happens to survive restarts sometimes.How do I process a stream from several workers?
Create one consumer group, then let each worker issue
XREADGROUP ... > under its own consumer name. Messages delivered but not acknowledged stay in the pending list, where XAUTOCLAIM can hand them to a healthy worker.Related
Expiry and caching patterns Data types and the commands that matter
Last refreshed 2026-09-18.