add redis backend for storage of gameboards
This commit is contained in:
+31
-12
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user