Challenge 3: Find the Memory Leak — Possible Solution ==================================================================== THE SETUP ---------- joinRoom() and sendToRoom() are implemented correctly, but nothing calls leaveRoom() from the socket's "close" event — a room's Set only ever grows via joinRoom(), and nothing ever removes a socket from it when that client disconnects. WHAT ACCUMULATES, AND WHY IT'S HIDDEN AT FIRST ------------------------------------------------- - Every socket that ever joins a room stays in that room's Set FOREVER, even long after the underlying connection has closed and the client is gone. Over time — hours, days, however long the server process runs — every room's Set keeps growing, holding references to an ever-increasing number of dead, closed WebSocket objects that will never be used again. - This is invisible in casual testing because sendToRoom()'s readyState === socket.OPEN check silently SKIPS any closed socket when broadcasting. The room's messaging behavior looks completely correct — currently-connected members still receive every broadcast message exactly as expected — because the bug only affects sockets that are no longer live, and those are quietly filtered out of every send, not causing any visible error or dropped message to anyone who's actually still connected. - The actual damage is memory, not message delivery: each dead socket object sitting in a room's Set can't be garbage-collected (JavaScript can't reclaim memory for an object something is still referencing), and the room's Set itself grows without bound as clients join and disconnect over the process's lifetime — the loop is never closed on the "leave" side, only the "join" side. WHY IT TAKES A WHILE TO NOTICE --------------------------------- In a short-lived test or a quick manual check, only a handful of sockets ever connect and disconnect, so the leaked memory is trivially small and completely unnoticeable. The problem only becomes visible in production after the server has been running long enough, and enough clients have joined and left, for the accumulated dead-socket references to add up to a measurable amount of memory — at which point it may present as a slow, steady memory growth over days or weeks rather than any obvious crash or error message pointing directly at the cause. THE FIX -------- Wire leaveRoom(socket, roomName) — for every room in socket.rooms — into the socket's "close" event handler, exactly as this chapter's example does: socket.on("close", () => { for (const roomName of socket.rooms) { leaveRoom(socket, roomName); } }); This ensures every socket is properly removed from every room's Set the moment its connection actually ends, so dead sockets never accumulate in the first place.