"""Redis client factories. ``settings.REDIS_URL`` selects the default logical database (usually ``0``). Token blacklist and application cache use separate logical DBs on the same Redis instance (``REDIS_TOKEN_BLACKLIST_DB``, ``REDIS_CACHE_DB``) so keys never collide and TTL policies can differ per workload. Usage:: from app.infrastructure.redis_client import get_redis, get_redis_blacklist get_redis().set("key", "value", ex=300) get_redis_blacklist().setex("blacklist:…", ttl, "1") """ from __future__ import annotations import logging from urllib.parse import urlparse, urlunparse import redis from app.config import settings logger = logging.getLogger(__name__) _clients: dict[str, redis.Redis] = {} def _redis_url_with_db(base_url: str, db_index: int) -> str: """Return *base_url* with its path replaced by ``/{db_index}``.""" parsed = urlparse(base_url) path = f"/{db_index}" return urlunparse( (parsed.scheme, parsed.netloc, path, "", "", ""), ) def _get_client(url: str) -> redis.Redis: if url not in _clients: _clients[url] = redis.from_url(url, decode_responses=True) logger.info("Redis client connected to %s", url) return _clients[url] def get_redis() -> redis.Redis: """Default Redis connection (URL from ``settings.REDIS_URL``).""" return _get_client(settings.REDIS_URL) def get_redis_blacklist() -> redis.Redis: """Redis DB used for JWT revocation (``jti`` keys with TTL).""" url = _redis_url_with_db( settings.REDIS_URL, settings.REDIS_TOKEN_BLACKLIST_DB, ) return _get_client(url) def get_redis_cache() -> redis.Redis: """Redis DB reserved for shared cache (scores, queues, etc.).""" url = _redis_url_with_db( settings.REDIS_URL, settings.REDIS_CACHE_DB, ) return _get_client(url)