fix(server): mark games abandoned on room teardown + staging leaderboard
When handle_player_leave emptied a room or handle_end_game was invoked,
the room was removed from memory without touching games_v2. Periodic
cleanup only scans in-memory rooms, so those rows were stranded as
status='active' forever — staging had 42 orphans accumulated over 5h.
- event_store.update_game_abandoned: guarded UPDATE (status='active' only)
- GameLogger.log_game_abandoned{,_async}: fire-and-forget wrapper
- handle_end_game + handle_player_leave: flip status before remove_room
- LEADERBOARD_INCLUDE_TEST_DEFAULT: env override so staging can show
soak-harness accounts by default; prod keeps them hidden
Verified on staging: 42 orphans swept on restart, soak accounts now
visible on /api/stats/leaderboard (rank 1-4).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -432,6 +432,23 @@ class EventStore:
|
||||
winner_id,
|
||||
)
|
||||
|
||||
async def update_game_abandoned(self, game_id: str) -> None:
|
||||
"""
|
||||
Mark a game as abandoned — used when a room empties before the game
|
||||
reaches GAME_OVER, so games_v2 never leaks as stranded 'active'.
|
||||
Only flips rows that are still active so a legitimately completed game
|
||||
never gets reverted.
|
||||
"""
|
||||
async with self.pool.acquire() as conn:
|
||||
await conn.execute(
|
||||
"""
|
||||
UPDATE games_v2
|
||||
SET status = 'abandoned', completed_at = NOW()
|
||||
WHERE id = $1 AND status = 'active'
|
||||
""",
|
||||
game_id,
|
||||
)
|
||||
|
||||
async def get_active_games(self) -> list[dict]:
|
||||
"""
|
||||
Get all active games for recovery on server restart.
|
||||
|
||||
Reference in New Issue
Block a user