Redis backend: lets several pusher_server nodes behave as one.
What lives in Redis
| Concern | How |
|---|---|
| Broadcasts | One pub/sub channel per app (<prefix>:events). A node delivers to its own sockets first, then PUBLISHes; every other node's subscriber re-delivers locally. Each message carries the sender's node_id so a node ignores its own. |
| Presence | Hash per channel (<prefix>:presence:<channel>, socket_id -> {user, info, node}). A Lua script decides atomically whether a join is the user's first connection (or a leave their last) across all nodes, so member_added / member_removed fire exactly once cluster-wide. |
| Occupancy | Hash per channel (<prefix>:subs:<channel>, node_id -> local subscriber count) so channel_occupied / channel_vacated fire once cluster-wide and subscription counts are cluster-wide. |
| Liveness | Each node refreshes a TTL key (<prefix>:node:<node_id>). A janitor (see PusherServer.Backend.Redis.Janitor) removes presence/occupancy left behind by nodes whose key expired. |
Failure behaviour
A Redis error never crashes a websocket connection. Operations log and fall back to node-local behaviour, so during a Redis outage each node keeps serving its own clients (cross-node delivery pauses). Pub/sub is at-most-once, like Pusher itself: messages published while a node is disconnected from Redis are not replayed.
Not supported
Redis Cluster (multi-key Lua scripts touch keys in different slots). Use a single Redis endpoint — standalone, Sentinel, or a managed primary/replica service all work.
Summary
Functions
Re-publishes this node's live state into Redis after Redis lost it (a restart, a flush, a failover to an empty replica) or after this node was cut off from Redis for a while. Called by the janitor.
Functions
Re-publishes this node's live state into Redis after Redis lost it (a restart, a flush, a failover to an empty replica) or after this node was cut off from Redis for a while. Called by the janitor.
Channel occupancy is rebuilt here in one pipeline, by setting this
node's subscriber count per channel to what the local Registry holds —
an overwrite, not an increment, so calling it again is harmless. Presence
entries need per-socket user info that only the sockets have, so each
socket is asked to restore its own (spread over a couple of seconds so
thousands of sockets don't hit a freshly restarted Redis at once).
Nothing is announced: clients were already told about these members, and
member_added for them again would be wrong.