Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)
Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 4 Min Lesezeit
0

The Bug That Taught Me What "Real-Time" Actually Means

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

When I first deployed ChatSphere, it looked done. Two tabs, two users, messages flying back and forth instantly. I recorded a demo, felt good about it, moved on.



Then I opened a third tab.






The bug



I logged in as the same user in two different tabs — one on my laptop, one on my phone, both signed in as renuka. I closed the laptop tab. The phone tab, still open and still connected, suddenly showed me as offline in the sidebar. I was still there. I just wasn't showing as online anymore.



The root cause was embarrassingly simple once I found it: my online-user tracking was keyed by socket ID, not by username.




CODE
// The naive version — one socket = one entry
const onlineUsers = new Map(); // socket.id -> username

socket.on('disconnect', () => {
onlineUsers.delete(socket.id);
broadcastOnlineUsers();
});






Every browser tab opens its own socket connection. So "one user, two tabs" was really "two sockets, no relationship between them" as far as my server was concerned. Close one tab, and the server had no way to know the user was still connected somewhere else — it only knew that socket had disconnected.






Why this matters more than it looks



This isn't just a cosmetic bug. In a real chat product, incorrect presence data breaks trust — imagine Slack showing your teammate as offline while they're actively typing to you. It also hints at a bigger category of problems: anything keyed by connection instead of identity breaks the moment a user has more than one connection, which is the normal case, not the edge case, once you have both a phone and a laptop.






The fix



The fix was to track presence per user, with each user mapped to a set of active socket connections:




CODE
const onlineUsers = new Map(); // username -> Set of socket ids

io.on('connection', (socket) => {
const { username } = socket;

if (!onlineUsers.has(username)) {
onlineUsers.set(username, new Set());
socket.broadcast.emit('userJoined', { username });
}
onlineUsers.get(username).add(socket.id);
broadcastOnlineUsers();

socket.on('disconnect', () => {
const sockets = onlineUsers.get(username);
if (sockets) {
sockets.delete(socket.id);
// Only mark the user offline once ALL their connections are gone
if (sockets.size === 0) {
onlineUsers.delete(username);
socket.broadcast.emit('userLeft', { username });
}
}
});
});






Now a user only disappears from the online list when their last connection drops, not their first. Multi-tab, multi-device — both work correctly.






The second problem this exposed: nothing stopped spam



While fixing this, I noticed a related gap: nothing was stopping a single connected client from firing off hundreds of messages a second, either by accident (a stuck key, a buggy client) or on purpose. There was no cost to sending — just an open pipe straight to the database.



I added a simple sliding-window rate limiter, scoped per socket connection:




CODE
const messageWindows = new Map(); // socket.id -> array of timestamps

function isRateLimited(socketId, maxMessages = 5, windowMs = 10000) {
const now = Date.now();
const timestamps = (messageWindows.get(socketId) || [])
.filter(t => now - t < windowMs);

if (timestamps.length >= maxMessages) {
messageWindows.set(socketId, timestamps);
return true;
}

timestamps.push(now);
messageWindows.set(socketId, timestamps);
return false;
}






No Redis, no extra dependency — just an in-memory sliding window, cleaned up on disconnect. It's not the solution I'd reach for at scale across multiple server instances (a distributed cache would be needed there, since in-memory state doesn't share across processes), but for a single-instance deployment it's the right amount of engineering for the actual problem.






What I'd do differently at real scale



If this needed to survive multiple server instances behind a load balancer, in-memory Maps stop working — two users on two different server instances wouldn't see each other's presence or rate-limit state. That's the point where I'd reach for Redis: a shared SET per username for presence, and Redis's INCR + EXPIRE for rate limiting, so all server instances read from the same source of truth.



I didn't build that here, because it would have been solving a problem I don't have yet. But knowing where the current design breaks — and why — feels like the more useful skill than defaulting to the "enterprise" solution everywhere out of habit.






The actual lesson



The bug wasn't really about Socket.io or Maps. It was a reminder that "it works when I test it" and "it works" are different claims, and the gap between them is usually hiding in the case you didn't think to test. Two tabs, same user, is not an exotic edge case — it's Tuesday. Production thinking means testing for Tuesday, not just the happy path.






Project:

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
Use custom web fonts in Google Sheets charts
2 Quellen
Introducing the new 1Password App for Google Chat
1 Quelle
Context-aware access controls are available for Gemini Enterprise in the Admin console
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten The Bug That Taught Me What "Real-Time" Actually Means

Thematisch verwandte Begriffe: That, Taught, What, RealTime · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...