mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-09-30 03:18:30 +00:00
Closes #1980. `app.js` opens a WebSocket on every page load and fans messages out through `onWS()`/`offWS()`. `live.js` ignored that channel and opened its own socket to the same endpoint. `Hub.Broadcast` does no per-client filtering, so both carried the identical full packet stream and **every viewer on the live map pulled it twice**. That page is the one people leave open for hours, so it was a standing multiplier on origin bandwidth rather than a burst. ## Measured, before and after Against a running instance, leaving the live map and returning while counting WebSocket constructions: | | new sockets on re-entry | constructed by | |---|---|---| | before | 1 | `at connectWS (live.js:3315)` | | after | 0 | nothing | And with one viewer on the live map, the server now reports **one** WebSocket client for that viewer, with the live feed counter climbing normally (7 to 31 over one navigation cycle, 71 on a fresh load). ## The change The live map subscribes to the shared channel like every other view, and unsubscribes in `destroy()` rather than closing a socket the rest of the app still needs. `connectWS()` drops any existing registration before adding a fresh one. An early return looks like the natural guard against double subscription, but it keeps the previous visit's closure registered, and re-registering without dropping the old one renders every packet twice. Dropping first is idempotent either way and always binds the current page. Reconnection becomes `app.js`'s business, since it owns the socket. That moved `WS_RECONNECT_MS` out of the only place that honoured it, so `app.js` now uses it too. It comes from `roles.js` and operators set it as `wsReconnectMs`; after this change it applies to the one socket everyone shares, or to nothing at all. ## Tests Three, in the sandbox that already loads `live.js` with a Leaflet mock: - the page registers exactly one listener on the shared channel and constructs no WebSocket of its own - re-entering leaves exactly one listener, and it is the new one rather than the previous visit's - the handler ignores messages that carry no packet `node test-frontend-helpers.js` passes (726 assertions), `node test-packet-filter.js` passes. ## One note on the history The second commit on this branch claims the early-return guard broke rendering on re-entry, citing a real measurement. The measurement happened, the attribution was wrong: the zero counter came from the WebSocket constructor patch I had installed to count sockets, which interfered with the page it was measuring. The third commit records that rather than rewriting it away. The change is kept because dropping the old registration first is the clearer contract, not because the guard was broken. ## Rule 0 Strictly less work than before: one socket per viewer instead of two, one JSON parse instead of two per packet, and no second reconnect loop. Nothing is added to the hot path; a listener already existed for every other view.