Description
Describe the bug
Due to chrome (and maybe other browsers) throttling timers when the device sleeps, the heartbeat detection logic doesn't always run in time, causing messages to be lost before reconnection happens late.
To Reproduce
Socket.IO server version: 4.7.5
Server
import { Server } from "socket.io";
const io = new Server(3000, { pingTimeout: 5_000, pingInterval: 25_000});
io.on("connection", (socket) => {
console.log(`connect ${socket.id}`);
socket.on("disconnect", () => {
console.log(`disconnect ${socket.id}`);
});
});
Socket.IO client version: 4.7.5
Client
import { io } from "socket.io-client";
// use a tunnel like the ports feature in vscode to make the connection work over the internet and use that host here
const socket = io("ws://<replace with host>:3000/", {});
socket.on("connect", () => {
console.log(`connect ${socket.id}`, new Date());
});
socket.on("disconnect", () => {
console.log("disconnect", new Date());
});
Expected behavior
- Load the page and wait for the connected message
- Close the laptop with it unplugged so that it goes to sleep
- Wait 60 seconds or so to make sure it's gone to sleep for a bit
- Open the laptop and go back to the page
- The latest connection should not have received a ping in the last 30 seconds, and therefore it should be closed. But because chrome froze the timer that's going to do the disconnection it will take a while longer before that actually happens
- If a message is sent before the reconnection it will probably go into the void and be lost
Proposed fixes
Check if connection is expired before sending every message
I opened a PR for this here: #5134
Let me know what you think
This doesn't cause the reconnect to happen immediately when the page is woken up, but it does mean attempting to send a message should trigger the reconnection, so outgoing messages shouldn't get lost.
Use setInterval
and periodically check if a ping has not been received instead of setTimeout
I think this and storing the Date.now()
of the last ping should be more accurate, and cause the reconnection to happen almost immediately when the page is woken up. Can also do the same on a visibilitychange
event when the page goes back to visible
.
Can look into a PR for this if you think it makes sense?
Platform:
- Device: Mac
- Browser: Chrome
- OS: Mac OS 14.5 (23F79)
Additional context
#3507 (comment) is another fix in the past related to throttled timers