""" Redis connection client for caching. Provides async Redis client with connection pooling for position ratings and other cached data. Author: Claude Date: 2025-11-03 Phase: 3E-Final """ import logging from typing import Optional import redis.asyncio as redis from redis.asyncio import Redis from redis.exceptions import RedisError logger = logging.getLogger(f'{__name__}.RedisClient') class RedisClient: """ Async Redis client with connection pooling. Singleton pattern - use redis_client instance. """ def __init__(self): self._redis: Optional[Redis] = None self._url: Optional[str] = None async def connect(self, redis_url: str = "redis://localhost:6379/0") -> None: """ Connect to Redis server. Args: redis_url: Redis connection URL Format: redis://host:port/db Example: redis://localhost:6379/0 """ if self._redis is not None: logger.warning("Redis client already connected") return try: self._url = redis_url self._redis = await redis.from_url( redis_url, encoding="utf-8", decode_responses=True, max_connections=10 # Connection pool size ) # Test connection await self._redis.ping() logger.info(f"Redis connected successfully: {redis_url}") except RedisError as e: logger.error(f"Failed to connect to Redis at {redis_url}: {e}") self._redis = None raise async def disconnect(self) -> None: """Disconnect from Redis server.""" if self._redis is not None: await self._redis.close() await self._redis.connection_pool.disconnect() self._redis = None logger.info("Redis disconnected") @property def client(self) -> Redis: """ Get Redis client instance. Returns: Redis client Raises: RuntimeError: If not connected """ if self._redis is None: raise RuntimeError( "Redis client not connected. " "Call await redis_client.connect() first." ) return self._redis @property def is_connected(self) -> bool: """Check if Redis is connected.""" return self._redis is not None async def ping(self) -> bool: """ Test Redis connection. Returns: True if Redis is responding, False otherwise """ if not self.is_connected: return False try: await self._redis.ping() return True except RedisError as e: logger.error(f"Redis ping failed: {e}") return False # Singleton instance redis_client = RedisClient()