speed up loading and saving to redis that the move request are getting a answer when switching workers, strip data that neats to get recomuted every turn
Build and Push Docker Container / build-and-push (push) Successful in 4m49s

This commit is contained in:
2026-04-07 12:13:11 +02:00
parent f479541c04
commit f6e19e18e6
6 changed files with 58 additions and 7 deletions
+5
View File
@@ -151,6 +151,11 @@ class GameBoard:
return {"name": self.type, "is_ladder": self.is_ladder}
def __getstate__(self):
state = self.__dict__.copy()
state['turns'] = [] # strip turn history — grows linearly, not needed for move computation
return state
async def save(self, store_class, **kwargs):
store = store_class(**kwargs)
await store.save(self)
+22 -4
View File
@@ -1,5 +1,5 @@
from typing import TYPE_CHECKING, cast
import json, time, os
import asyncio, json, time, os
from quart import Blueprint, request, jsonify
@@ -61,9 +61,27 @@ def create_battlesnake_blueprint(server:'Server') -> Blueprint:
server.metrics_collector.record_http_request('move')
game_state = await request.get_json()
move_started = time.perf_counter()
game_board = cast(GameBoard, await server.game_runtime.get_game_board(game_state))
next_move = game_board.snake_neat_make_a_move()
await server.game_runtime.persist_game_board(game_state['game']['id'], game_board)
game_id = game_state['game']['id']
timeout_ms = int(game_state.get('game', {}).get('timeout', 500))
budget_sec = max(0.05, (timeout_ms - 50) / 1000.0)
next_move = None
move_completed = False
game_board = None
try:
async with asyncio.timeout(budget_sec):
game_board = cast(GameBoard, await server.game_runtime.get_game_board(game_state))
loop = asyncio.get_running_loop()
next_move = await loop.run_in_executor(None, game_board.snake_neat_make_a_move)
move_completed = True
except TimeoutError:
await await_log(server.logger.warning(f'MOVE TIMEOUT: turn={game_state.get("turn")}, game={game_id}, returning fallback {next_move!r}'))
if move_completed:
await server.game_runtime.persist_game_board(game_id, game_board)
await server.gameplay_tracking.record_gameplay_turn(game_state, next_move, game_board)
elapsed_ms = (time.perf_counter() - move_started) * 1000.0
await server.metrics_collector.record_move(next_move, elapsed_ms)
@@ -1,5 +1,5 @@
from typing import TYPE_CHECKING
import inspect, pickle
import inspect, pickle, zlib
if TYPE_CHECKING:
from server.GameBoard import GameBoard
@@ -28,7 +28,7 @@ class RedisGameBoardStore:
async def save(self, game_id:str, game_board:'GameBoard') -> None:
redis = await self._get_redis()
payload = pickle.dumps(game_board, protocol=pickle.HIGHEST_PROTOCOL)
payload = zlib.compress(pickle.dumps(game_board, protocol=pickle.HIGHEST_PROTOCOL), level=1)
await redis.set(self._key(game_id), payload, ex=self.ttl_seconds)
async def load(self, game_id:str):
@@ -36,7 +36,10 @@ class RedisGameBoardStore:
payload = await redis.get(self._key(game_id))
if payload is None:
return None
return pickle.loads(payload)
try:
return pickle.loads(zlib.decompress(payload))
except zlib.error:
return pickle.loads(payload)
async def delete(self, game_id:str) -> None:
redis = await self._get_redis()