fix(server): populate games_v2 metadata on game start

update_game_started (started_at, num_players, num_rounds, player_ids)
was defined in event_store but had zero callers. 289/289 staging games
had those fields NULL — queries that joined on them returned garbage,
and the denormalized player_ids GIN index was dead weight.

log_game_start now calls create_game THEN update_game_started in one
async task. If create fails, update is skipped (row doesn't exist).
handlers.py passes num_rounds and player_ids through at call time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-18 00:41:58 -04:00
parent d5f8eef6b3
commit 8030a3c171
3 changed files with 142 additions and 36 deletions

View File

@@ -63,22 +63,20 @@ class GameLogger:
self,
room_code: str,
num_players: int,
num_rounds: int,
player_ids: list[str],
options: "GameOptions",
game_id: Optional[str] = None,
) -> str:
"""
Log game start, return game_id.
Log game start. Writes the row via create_game and then populates
started_at/num_players/num_rounds/player_ids via update_game_started
so downstream queries don't see a half-initialized games_v2 row.
Creates a game record in games_v2 table.
Args:
room_code: Room code for the game.
num_players: Number of players.
options: Game options/house rules.
Returns:
Generated game UUID.
If create_game fails the update is skipped — the row doesn't exist.
"""
game_id = str(uuid.uuid4())
if game_id is None:
game_id = str(uuid.uuid4())
try:
await self.event_store.create_game(
@@ -87,9 +85,20 @@ class GameLogger:
host_id="system",
options=self._options_to_dict(options),
)
except Exception as e:
log.error(f"Failed to log game start (create): {e}")
return game_id
try:
await self.event_store.update_game_started(
game_id,
num_players,
num_rounds,
player_ids,
)
log.debug(f"Logged game start: {game_id} room={room_code}")
except Exception as e:
log.error(f"Failed to log game start: {e}")
log.error(f"Failed to log game start (update): {e}")
return game_id
@@ -97,6 +106,8 @@ class GameLogger:
self,
room_code: str,
num_players: int,
num_rounds: int,
player_ids: list[str],
options: "GameOptions",
) -> str:
"""
@@ -108,32 +119,29 @@ class GameLogger:
game_id = str(uuid.uuid4())
try:
loop = asyncio.get_running_loop()
# Already in async context - fire task, return ID immediately
asyncio.create_task(self._log_game_start_with_id(game_id, room_code, num_players, options))
asyncio.get_running_loop()
asyncio.create_task(
self.log_game_start_async(
room_code=room_code,
num_players=num_players,
num_rounds=num_rounds,
player_ids=player_ids,
options=options,
game_id=game_id,
)
)
return game_id
except RuntimeError:
# Not in async context - run synchronously
return asyncio.run(self.log_game_start_async(room_code, num_players, options))
async def _log_game_start_with_id(
self,
game_id: str,
room_code: str,
num_players: int,
options: "GameOptions",
) -> None:
"""Helper to log game start with pre-generated ID."""
try:
await self.event_store.create_game(
game_id=game_id,
room_code=room_code,
host_id="system",
options=self._options_to_dict(options),
return asyncio.run(
self.log_game_start_async(
room_code=room_code,
num_players=num_players,
num_rounds=num_rounds,
player_ids=player_ids,
options=options,
game_id=game_id,
)
)
log.debug(f"Logged game start: {game_id} room={room_code}")
except Exception as e:
log.error(f"Failed to log game start: {e}")
async def log_game_end_async(self, game_id: str) -> None:
"""