"""Redis cache client and helpers.""" import json from typing import Any import redis.asyncio as redis from app.config import get_settings settings = get_settings() async def get_redis() -> redis.Redis: """Return a Redis client instance.""" return redis.from_url(settings.redis_url, decode_responses=True) class CacheClient: """Wrapper around Redis for equipment caching.""" def __init__(self) -> None: self._redis: redis.Redis | None = None async def connect(self) -> None: self._redis = await get_redis() async def disconnect(self) -> None: if self._redis: await self._redis.close() self._redis = None async def get(self, key: str) -> Any | None: if not self._redis: await self.connect() assert self._redis is not None data = await self._redis.get(key) if data: return json.loads(data) return None async def set(self, key: str, value: Any, ttl: int = 3600) -> None: if not self._redis: await self.connect() assert self._redis is not None await self._redis.setex(key, ttl, json.dumps(value, default=str)) async def delete_pattern(self, pattern: str) -> int: if not self._redis: await self.connect() assert self._redis is not None keys = [] async for key in self._redis.scan_iter(match=pattern): keys.append(key) if keys: return await self._redis.delete(*keys) return 0 async def incr_rate(self, key: str, window: int = 60) -> int: if not self._redis: await self.connect() assert self._redis is not None count = await self._redis.incr(key) if count == 1: await self._redis.expire(key, window) return count cache = CacheClient()