From fc68538be5cf616f0896eed325cc420a04056ec6 Mon Sep 17 00:00:00 2001 From: boskodev790 <233739930+boskodev790@users.noreply.github.com> Date: Wed, 22 Jul 2026 14:54:41 -0400 Subject: [PATCH] #6501 Prune connections map on game teardown The in-memory `connections` map was inserted into on host/join/reconnect but a `game_code` entry was never removed, unlike the parallel `game_spectators` map, leaking one entry per finished game. Remove the entry in both reaper teardown loops (grace-expiry and lobby-expiry), mirroring `specs.remove` and preserving state -> connections -> spectators lock order. Closes #6501 --- crates/phase-server/src/main.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/phase-server/src/main.rs b/crates/phase-server/src/main.rs index 44b059bf84..d4df405c37 100644 --- a/crates/phase-server/src/main.rs +++ b/crates/phase-server/src/main.rs @@ -1084,7 +1084,7 @@ async fn main() { } } // Notify connected players and clean up persistence - let conns = bg_connections.lock().await; + let mut conns = bg_connections.lock().await; for game_code in &expired { info!(game = %game_code, reason = "disconnect_expired", "game over"); if let Some(players) = conns.get(game_code) { @@ -1100,6 +1100,10 @@ async fn main() { if let Err(e) = bg_game_db.delete_session(game_code) { error!(game = %game_code, error = %e, "failed to delete persisted session"); } + // Prune the connections entry for the finished game so the map + // does not grow unbounded (mirrors the game_spectators cleanup + // below). The immutable `conns.get` borrow above has ended. + conns.remove(game_code); } let mut specs = bg_game_spectators.lock().await; for game_code in &expired { @@ -1135,6 +1139,15 @@ async fn main() { } } drop(mgr); + // Prune the connections entries for the expired lobby games so the + // map does not grow unbounded (mirrors the game_spectators cleanup + // below). Lock order: state (dropped) -> connections -> spectators. + { + let mut conns = bg_connections.lock().await; + for game_code in &expired_lobby { + conns.remove(game_code); + } + } let mut specs = bg_game_spectators.lock().await; for game_code in &expired_lobby { specs.remove(game_code);