add redis backend for storage of gameboards

This commit is contained in:
2026-04-04 12:07:05 +02:00
parent bbdc8b288a
commit 4547e3443b
7 changed files with 232 additions and 13 deletions
+31 -12
View File
@@ -1,5 +1,6 @@
from server.Files import read_file
from server.GameBoard import GameBoard
from server.GameStateStore import GameStateStore
from snakes import SnakeBuilder
from quart_common.web.logger import await_log
from quart_common.web.logger import build_logger
@@ -20,7 +21,7 @@ class Server:
'version': '1.0.0',
}
def __init__(self, data_path:str, snake_type:str, storage_type:str, debug:bool=False, check_tls_security:bool=False):
def __init__(self, data_path:str, snake_type:str, storage_type:str, debug:bool=False, check_tls_security:bool=False, game_state_backend:str='memory', game_state_redis_url:str='redis://localhost:6379/0', game_state_ttl_sec:int=900):
self.debug = debug
self.snake_type = snake_type
self.storage_type = storage_type
@@ -30,6 +31,11 @@ class Server:
self.check_tls_security = check_tls_security
self.store_game_state = False
self.game_state_store = GameStateStore(
backend=game_state_backend,
redis_url=game_state_redis_url,
ttl_seconds=game_state_ttl_sec,
)
self.running_games:dict[str, GameBoard] = {}
self.game_move_counts:dict[str, int] = {}
@@ -100,6 +106,7 @@ class Server:
move_started = time.perf_counter()
game_board = await self._get_game_board(game_state)
next_move = game_board.snake_neat_make_a_move()
await self._persist_game_board(game_state['game']['id'], game_board)
elapsed_ms = (time.perf_counter() - move_started) * 1000.0
self.metrics['move_response_time_ms_total'] += elapsed_ms
self.metrics['move_response_time_ms_max'] = max(
@@ -142,7 +149,7 @@ class Server:
)
await await_log(self.logger.info(f'GAME ENDED: Winner is {[x['name'] for x in game_state['board']['snakes']]}'))
self._delete_game_board(game_state)
await self._delete_game_board(game_state)
return 'ok'
@self.app.after_request
@@ -150,6 +157,10 @@ class Server:
response.headers.set('server', 'battlesnake/gitea/snake-python')
return response
@self.app.after_serving
async def shutdown_state_storage():
await self.game_state_store.close()
@self.app.get('/cleanup')
async def cleanup():
results = self._cleanup_database()
@@ -231,6 +242,7 @@ class Server:
await new_game_board.start_game(game_state)
self.running_games[game_id] = new_game_board
await self.game_state_store.save(game_id, new_game_board)
self.game_move_counts[game_id] = 0
self.game_last_seen_unix[game_id] = int(time.time())
self.metrics['games_started'] += 1
@@ -241,19 +253,29 @@ class Server:
self.metrics['last_game_start_unix'] = int(time.time())
return new_game_board
def _delete_game_board(self, game_state:dict):
async def _persist_game_board(self, game_id:str, game_board:GameBoard):
self.running_games[game_id] = game_board
await self.game_state_store.save(game_id, game_board)
async def _delete_game_board(self, game_state: dict):
game_id = game_state['game']['id']
self.running_games.pop(game_id, None)
self.game_move_counts.pop(game_id, None)
self.game_last_seen_unix.pop(game_id, None)
await self.game_state_store.delete(game_id)
async def _get_game_board(self, game_state:dict, end:bool=False):
game_id = game_state['game']['id']
try:
game_board = self.running_games[game_id]
except KeyError:
game_board = await self._create_game_board(game_state)
self.metrics['games_autocreated'] += 1
persisted_board = await self.game_state_store.load(game_id)
if persisted_board is not None:
game_board = persisted_board
self.running_games[game_id] = game_board
else:
game_board = await self._create_game_board(game_state)
self.metrics['games_autocreated'] += 1
if not end:
self.metrics['total_moves'] += 1
@@ -265,6 +287,7 @@ class Server:
if end:
self._record_game_end(game_state)
game_board.end_game(game_state)
await self._persist_game_board(game_id, game_board)
return game_board
@@ -291,7 +314,7 @@ class Server:
self.game_last_seen_unix.pop(game_id, None)
self.metrics['games_stuck_removed'] += 1
def _record_game_end(self, game_state: dict):
def _record_game_end(self, game_state:dict):
self.metrics['games_ended'] += 1
self.metrics['last_game_end_unix'] = int(time.time())
@@ -426,17 +449,13 @@ class Server:
'# TYPE snake_http_requests_by_endpoint_total counter',
])
for endpoint, count in snapshot['http_requests_by_endpoint'].items():
lines.append(
f'snake_http_requests_by_endpoint_total{{endpoint="{endpoint}"}} {count}'
)
lines.append(f'snake_http_requests_by_endpoint_total{{endpoint="{endpoint}"}} {count}')
lines.extend([
'# HELP snake_moves_by_direction_total Move responses grouped by direction.',
'# TYPE snake_moves_by_direction_total counter',
])
for direction, count in snapshot['move_direction_counts'].items():
lines.append(
f'snake_moves_by_direction_total{{direction="{direction}"}} {count}'
)
lines.append(f'snake_moves_by_direction_total{{direction="{direction}"}} {count}')
return '\n'.join(lines) + '\n'