"""Cache abstraction over Redis (section 21). Redis is disposable acceleration — every function here fails open: if Redis is unreachable, reads report a cache miss and writes are silently skipped, so the API keeps serving from PostgreSQL instead of failing. """ from __future__ import annotations import orjson from redis.asyncio import Redis from app.core.config import get_settings settings = get_settings() CACHE_VERSION = "v1" def build_key(*parts: str) -> str: return ":".join(("aurora", CACHE_VERSION, *parts)) async def cache_get_json(redis: Redis, key: str) -> dict | list | None: try: raw = await redis.get(key) except Exception: return None if raw is None: return None try: return orjson.loads(raw) except orjson.JSONDecodeError: return None async def cache_set_json( redis: Redis, key: str, value: dict | list, ttl: int | None = None ) -> None: ttl = ttl if ttl is not None else settings.CACHE_DEFAULT_TTL try: await redis.set(key, orjson.dumps(value), ex=ttl) except Exception: return async def cache_delete(redis: Redis, *keys: str) -> None: if not keys: return try: await redis.delete(*keys) except Exception: return async def cache_delete_prefix(redis: Redis, prefix: str) -> None: try: async for key in redis.scan_iter(match=f"{prefix}*"): await redis.delete(key) except Exception: return