perf(battlesnake): defer ordered lifecycle bookkeeping
Build and Push Docker Container / build-and-push (push) Successful in 10m12s
Build and Push Docker Container / build-and-push (push) Successful in 10m12s
- Acknowledge start and end without waiting for persistence or dashboards. - Keep runtime creation and finished-game removal on the response path. - Serialize start, turn, and end bookkeeping per game in Quart tasks. - Capture turn thinking before scheduling deferred persistence. - Defer start metrics, logging, and stale-game pruning. - Run legacy history exports in a worker thread. - Record results and end metrics even when history export fails. - Drain pending bookkeeping before closing storage during shutdown. - Test sub-500 ms responses with slow backends and real SQLite. - Cover event ordering, export failures, and rejected user agents.
This commit is contained in:
@@ -5,7 +5,6 @@ from quart import Blueprint, request, jsonify
|
||||
|
||||
from quart_common.web.decorators import require_user_agent
|
||||
from quart_common.web.logger import await_log
|
||||
from server.database import StorageLoader
|
||||
from snakes import DEFAULT_SNAKE_CONFIG
|
||||
from server.GameBoard import GameBoard
|
||||
from server.Files import read_file
|
||||
@@ -47,14 +46,13 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
|
||||
@require_user_agent(("BattlesnakeEngine", "BattlesnakeArena"), abort_code=404)
|
||||
async def on_start():
|
||||
server.metrics_collector.record_http_request('start')
|
||||
await server.game_runtime.prune_stale_games()
|
||||
game_state = await request.get_json()
|
||||
game_board = await server.game_runtime.create_game_board(game_state)
|
||||
await server.gameplay_tracking.record_gameplay_start(game_state, game_board)
|
||||
await server.dashboard_query.push_dashboard_games_update(
|
||||
game_state, trigger='game_started',
|
||||
game_board = await server.game_runtime.create_game_board(game_state, publish_metrics=False)
|
||||
active_count = len(server.game_runtime.running_games)
|
||||
server.game_lifecycle.submit(
|
||||
game_state['game']['id'],
|
||||
lambda: server.game_lifecycle.started(game_state, game_board, active_count),
|
||||
)
|
||||
await await_log(server.logger.info(f'GAME START: {game_state['game']}'))
|
||||
return 'ok'
|
||||
|
||||
@blueprint.post('/move')
|
||||
@@ -79,8 +77,10 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
|
||||
except TimeoutError:
|
||||
await await_log(server.logger.warning(f'MOVE TIMEOUT: turn={game_state.get("turn")}, game={game_id}, returning fallback {next_move!r}'))
|
||||
|
||||
await server.gameplay_tracking.record_gameplay_turn(game_state, next_move, game_board)
|
||||
await server.dashboard_query.push_dashboard_game_replay_update(game_id)
|
||||
thinking = server.gameplay_tracking._extract_latest_snake_thinking(game_board)
|
||||
server.game_lifecycle.submit(
|
||||
game_id, lambda: server.game_lifecycle.turn(game_state, next_move, thinking),
|
||||
)
|
||||
elapsed_ms = (time.perf_counter() - move_started) * 1000.0
|
||||
await server.metrics_collector.record_move(next_move, elapsed_ms)
|
||||
|
||||
@@ -93,29 +93,16 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
|
||||
@require_user_agent(("BattlesnakeEngine", "BattlesnakeArena"), abort_code=404)
|
||||
async def on_end():
|
||||
server.metrics_collector.record_http_request('end')
|
||||
await server.game_runtime.prune_stale_games()
|
||||
game_state = await request.get_json()
|
||||
if server.store_game_state:
|
||||
game_board = cast(GameBoard, await server.game_runtime.get_game_board(game_state, end=True))
|
||||
if server.check_tls_security:
|
||||
await game_board.save(
|
||||
StorageLoader.build(server.storage_type),
|
||||
file_path=os.path.join(server.data_path, 'data'),
|
||||
database=os.getenv('EDGEDB_DATABASE', None),
|
||||
tls_security=None,
|
||||
)
|
||||
else:
|
||||
await game_board.save(
|
||||
StorageLoader.build(server.storage_type),
|
||||
file_path=os.path.join(server.data_path, 'data'),
|
||||
database=os.getenv('EDGEDB_DATABASE', None),
|
||||
)
|
||||
|
||||
await server.gameplay_tracking.record_gameplay_end(game_state)
|
||||
await server.dashboard_query.push_dashboard_games_update(game_state)
|
||||
await await_log(server.logger.info(f'GAME ENDED: Winner is {[x['name'] for x in game_state['board']['snakes']]}'))
|
||||
game_id = game_state['game']['id']
|
||||
game_board = server.game_runtime.running_games.get(game_id)
|
||||
if game_board is not None:
|
||||
game_board.read_game_data(game_state)
|
||||
game_board.end_game(game_state)
|
||||
await server.game_runtime.delete_game_board(game_state)
|
||||
await server.metrics_collector.record_game_end(game_state)
|
||||
server.game_lifecycle.submit(
|
||||
game_id, lambda: server.game_lifecycle.ended(game_state, game_board),
|
||||
)
|
||||
return 'ok'
|
||||
|
||||
# @blueprint.get('/cleanup')
|
||||
|
||||
Reference in New Issue
Block a user