from server.GameBoard import GameBoard import inspect import pickle class RedisGameBoardStore: def __init__(self, redis_url:str="redis://localhost:6379/0", key_prefix:str="snake:gameboard", ttl_seconds:int=900, **kwargs): self.redis_url = redis_url self.key_prefix = key_prefix self.ttl_seconds = max(60, int(ttl_seconds)) self._redis = None async def _get_redis(self): if self._redis is not None: return self._redis try: import redis.asyncio as aioredis # type: ignore[import-not-found] except ImportError as error: # pragma: no cover raise RuntimeError("Redis backend selected but 'redis' package with asyncio support is not installed") from error self._redis = aioredis.from_url(self.redis_url) return self._redis def _key(self, game_id:str) -> str: return f"{self.key_prefix}:{game_id}" 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) await redis.set(self._key(game_id), payload, ex=self.ttl_seconds) async def load(self, game_id:str): redis = await self._get_redis() payload = await redis.get(self._key(game_id)) if payload is None: return None return pickle.loads(payload) async def delete(self, game_id:str) -> None: redis = await self._get_redis() await redis.delete(self._key(game_id)) async def close(self) -> None: if self._redis is None: return aclose_method = getattr(self._redis, "aclose", None) if callable(aclose_method): maybe_result = aclose_method() if inspect.isawaitable(maybe_result): await maybe_result else: close_method = getattr(self._redis, "close", None) if callable(close_method): close_result = close_method() if inspect.isawaitable(close_result): await close_result self._redis = None