"""Shared async Redis client. Redis is used as scratch / cache only — never as a system of record. We disable RDB/AOF in compose so a restart wipes state, which matches the "ephemeral pie" property: anything the server temporarily holds during /api/analyze or /api/portfolio/parse must not survive a restart. The client is module-singleton; FastAPI handlers get it via get_redis().""" from __future__ import annotations from typing import Optional import redis.asyncio as redis from app.config import get_settings _client: Optional[redis.Redis] = None def get_redis() -> redis.Redis: global _client if _client is None: s = get_settings() _client = redis.from_url( s.REDIS_URL, encoding="utf-8", decode_responses=True, socket_timeout=5, socket_connect_timeout=5, ) return _client async def close_redis() -> None: global _client if _client is not None: await _client.aclose() _client = None