From 13a717e569edc439309a9f2ba9de4f6dffa48e3b Mon Sep 17 00:00:00 2001 From: evanpelle Date: Tue, 22 Jul 2025 11:26:26 -0700 Subject: [PATCH] fix server memory leak (#1524) ## Description: After v24 release, the server had has a bad memory leak, causing worker memory to rise to 1gb after ~12 hours. I believed this is caused by not removing listeners on websocket when kicking a client. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I have read and accepted the CLA aggreement (only required once). ## Please put your Discord username so you can be contacted if a bug or regression is found: evan --- src/server/GameServer.ts | 7 +++---- src/server/Worker.ts | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index c1093dd27..e8480e7d0 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -183,9 +183,7 @@ export class GameServer { this.allClients.set(client.clientID, client); - client.ws.removeAllListeners("message"); - client.ws.removeAllListeners("close"); - client.ws.removeAllListeners("error"); + client.ws.removeAllListeners(); client.ws.on( "message", gatekeeper.wsHandler(client.ip, async (message: string) => { @@ -405,7 +403,7 @@ export class GameServer { // Close all WebSocket connections clearInterval(this.endTurnIntervalID); this.allClients.forEach((client) => { - client.ws.removeAllListeners("message"); + client.ws.removeAllListeners(); if (client.ws.readyState === WebSocket.OPEN) { client.ws.close(1000, "game has ended"); } @@ -559,6 +557,7 @@ export class GameServer { this.activeClients = this.activeClients.filter( (c) => c.clientID !== clientID, ); + client.ws.removeAllListeners(); this.kickedClients.add(clientID); }, 100); } else { diff --git a/src/server/Worker.ts b/src/server/Worker.ts index 2c6476b57..96efde652 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -457,6 +457,8 @@ export function startWorker() { // Handle other message types } catch (error) { + ws.removeAllListeners(); + ws.close(1011, "Internal server error"); log.warn( `error handling websocket message for ${ipAnonymize(ip)}: ${error}`.substring( 0, @@ -473,6 +475,9 @@ export function startWorker() { ws.close(1002, "WS_ERR_UNEXPECTED_RSV_1"); } }); + ws.on("close", () => { + ws.removeAllListeners(); + }); }); // The load balancer will handle routing to this server based on path