In this lesson, you will learn how to architect a robust system to track and broadcast the real-time online or offline status of users across your distributed WebSocket platform.
The Challenge of Presence
In a distributed environment, "presence" isn't just knowing if a socket is open; it’s about maintaining a single source of truth for a user’s state across multiple server instances. Since a user might be connected to one server while their friend is on another, we cannot rely on local in-memory variables. Instead, we must combine our WebSocket connections with a fast, shared data store like Redis.
Implementing the Presence Pattern
The standard approach is to treat a user’s connection as a "heartbeat" event. When a client successfully establishes a connection, we update a shared key in Redis (e.g., user:presence:{userId}) with a TTL (Time-To-Live). If the connection drops—either gracefully or due to a network timeout—we update the status or let the TTL expire to mark them as offline.
Here is how you might handle the connection state:
// When a socket connection is established
socket.on('connection', async (client) => {
const userId = client.handshake.user.id;
// Update status in Redis and broadcast
await redis.set(`presence:${userId}`, 'online', 'EX', 60);
io.emit('user-status-change', { userId, status: 'online' });
// Heartbeat mechanism to keep status 'online'
client.on('heartbeat', () => {
redis.expire(`presence:${userId}`, 60);
});
client.on('disconnect', async () => {
// Optionally delete key immediately on graceful shutdown
await redis.del(`presence:${userId}`);
io.emit('user-status-change', { userId, status: 'offline' });
});
});
Handling Distributed Broadcasts
Since your servers are load-balanced, you should use Redis Pub/Sub or an asynchronous message queue bridge to ensure that status updates on "Server A" are immediately propagated to "Server B". This ensures that when a user logs in, every other connected user receives the user-status-change event regardless of which server they are currently pinned to. By offloading the status persistence to Redis, you keep your memory usage lean, allowing your application to scale horizontally without losing track of who is currently active.
Key Takeaway: User presence is a shared state problem solved by pairing short-lived heartbeat signals with a centralized, high-speed data store like Redis.