A user who loses connectivity for 30 seconds and reconnects finds the conversation with a gap — every message sent during their outage is gone with no indication it was missed. This is especially damaging in moderation workflows, incident channels, or any context where missing a message has operational consequences. ISO 25010 reliability specifically covers data persistence across failure events; dropping messages on disconnect is a reliability failure regardless of how brief the outage.
Medium because messages sent to disconnected users are permanently lost without a server-side queue, creating silent data gaps on reconnect.
Persist messages for offline users and flush the queue in delivery order on reconnect. For production use, back the queue with Redis lists rather than in-process Maps.
// On connect, deliver queued messages in order
socket.on('connect_post_auth', async () => {
const queued = await redis.lrange(`offline:${socket.userId}`, 0, -1);
for (const raw of queued) {
socket.emit('message', JSON.parse(raw));
}
await redis.del(`offline:${socket.userId}`);
});
// When a message is sent, queue for offline subscribers
const offlineUsers = await getOfflineChannelMembers(channel);
for (const uid of offlineUsers) {
await redis.rpush(`offline:${uid}`, JSON.stringify(message));
await redis.expire(`offline:${uid}`, 86400); // 24 h TTL
}
Set a TTL on the queue to prevent unbounded growth for users who never reconnect.
ID: community-realtime.message-delivery.offline-queue-on-reconnect
Severity: medium
What to look for: Count all offline message handling paths. Enumerate queueing mechanisms: database writes, in-memory cache, Redis lists, or message broker queues. Check that delivery order is preserved (messages delivered in the same order they were queued).
Pass criteria: Messages sent while a user is offline are queued via at least 1 persistence mechanism and delivered in order when the user reconnects. The queue must preserve monotonic ordering.
Fail criteria: Messages sent during disconnection are lost, or no queueing mechanism exists.
Skip (N/A) when: Never — message queueing on disconnect is essential for reliability.
Detail on fail: "No offline message queue. Messages sent while a user is disconnected are lost."
Remediation: Queue messages for offline users and deliver on reconnect:
interface QueuedMessage {
id: string;
userId: string;
channel: string;
content: string;
timestamp: number;
}
const messageQueue = new Map<string, QueuedMessage[]>();
socket.on('send_message', async (data) => {
const message = { id: nanoid(), userId: socket.userId, channel: data.channel, content: data.content, timestamp: Date.now() };
// Broadcast to online users
io.to(data.channel).emit('message', message);
// Queue for offline users (simplified example)
const users = await getChannelSubscribers(data.channel);
for (const user of users) {
if (!isUserOnline(user.id)) {
if (!messageQueue.has(user.id)) {
messageQueue.set(user.id, []);
}
messageQueue.get(user.id)!.push(message);
}
}
});
socket.on('reconnect', () => {
const queued = messageQueue.get(socket.userId) || [];
for (const msg of queued) {
socket.emit('message', msg);
}
messageQueue.delete(socket.userId);
});