fix: cleanup pending acks on timeout to prevent memory leak

Related: https://github.com/socketio/socket.io/issues/4984
This commit is contained in:
Damien Arrachequesne
2026-03-11 18:09:54 +01:00
parent ba9cd6900d
commit 37aad11417
2 changed files with 26 additions and 0 deletions

View File

@@ -13,6 +13,9 @@ import type {
FirstNonErrorArg,
EventNamesWithError,
} from "./typed-events";
import debugModule from "debug";
const debug = debugModule("socket.io:broadcast-operator");
export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
implements TypedEventBroadcaster<EmitEvents>
@@ -235,6 +238,17 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
const timer = setTimeout(() => {
timedOut = true;
debug("operation has timed out");
// @ts-expect-error
const packetId = packet.id;
if (packetId !== undefined) {
this.adapter.nsp.sockets.forEach((socket) => {
socket.acks.delete(packetId);
});
}
ack.apply(this, [
new Error("operation has timed out"),
this.flags.expectSingleResponse ? null : responses,
@@ -246,6 +260,13 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
let expectedClientCount = 0;
const checkCompleteness = () => {
debug(
"responses: servers: %d / %d ; clients: %d / %d",
actualServerCount,
expectedServerCount,
responses.length,
expectedClientCount,
);
if (
!timedOut &&
expectedServerCount === actualServerCount &&

View File

@@ -534,6 +534,11 @@ describe("messaging many", () => {
// @ts-ignore
expect(err.responses).to.contain(1, 2);
for (const [, serverSocket] of io.of("/").sockets) {
// @ts-ignore accessing private acks map to verify cleanup
expect(serverSocket.acks.size).to.be(0);
}
success(done, io, socket1, socket2, socket3);
}
});