CI / lint (push) Canceled after 0s
CI / typecheck (push) Canceled after 0s
CI / unit-tests (push) Canceled after 0s
CI / integration-tests (push) Canceled after 0s
CI / container-build (push) Canceled after 0s
CI / dependency-audit (push) Canceled after 0s
Production-grade media metadata backend per docs/aurora-core-spec.md: Aurora-owned UUIDv7 identity system, normalized movie/TV/people/credits schema, provider abstraction with a TMDb adapter, an idempotent async ingestion pipeline (normalize -> match -> upsert -> provenance), a Postgres FTS/trigram search layer, a stateless FastAPI v1 API with Redis caching/rate-limiting, arq workers, dev/prod Docker Compose topologies, unit + integration + load tests, CI, and architecture/ database/API/ERD documentation. Co-Authored-By: Claude Sonnet 5 <[email protected]>
62 lines
1.5 KiB
Python
62 lines
1.5 KiB
Python
"""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
|