| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- """Async lifespan helper to initialize both an asyncio Redis client and a synchronous Redis client
- and attach them to app.state.redis (async) and app.state.redis_sync (sync).
- This lets synchronous request handlers use the sync client while async handlers use the asyncio client.
- """
- import os
- from contextlib import asynccontextmanager
- import redis.asyncio as aioredis
- import redis as redis_sync
- from fastapi import FastAPI
- import logging
- logger = logging.getLogger("app.redis")
- @asynccontextmanager
- async def lifespan(app: FastAPI):
- redis_url = os.getenv("REDIS_URL")
- redis_host = os.getenv("REDIS_HOST", "127.0.0.1")
- redis_port = int(os.getenv("REDIS_PORT", "6379"))
- redis_db = int(os.getenv("REDIS_DB", "0"))
- redis_password = os.getenv("REDIS_PASSWORD", None)
- # Create both async and sync clients
- if redis_url:
- async_client = aioredis.from_url(redis_url, decode_responses=True)
- sync_client = redis_sync.from_url(redis_url, decode_responses=True)
- else:
- async_client = aioredis.Redis(host=redis_host, port=redis_port, db=redis_db, password=redis_password, decode_responses=True)
- sync_client = redis_sync.Redis(host=redis_host, port=redis_port, db=redis_db, password=redis_password, decode_responses=True)
- try:
- # Verify connectivity for both clients
- await async_client.ping()
- try:
- sync_client.ping()
- except Exception:
- # If sync ping fails, log but still keep async client if it's ok
- logger.warning("Sync Redis ping failed during startup", exc_info=True)
- app.state.redis = async_client
- app.state.redis_sync = sync_client
- logger.info("Redis clients initialized and attached to app.state.redis (async) and app.state.redis_sync (sync) (host=%s port=%s db=%s)", redis_host, redis_port, redis_db)
- except Exception as e:
- logger.error("Failed to initialize Redis clients: %s", e, exc_info=True)
- app.state.redis = None
- app.state.redis_sync = None
- try:
- yield
- finally:
- # Close async client
- try:
- if getattr(app.state, "redis", None) is not None:
- await app.state.redis.close()
- logger.info("Async Redis client closed on shutdown")
- except Exception:
- logger.error("Error closing async Redis client", exc_info=True)
- # Close sync client
- try:
- if getattr(app.state, "redis_sync", None) is not None:
- try:
- app.state.redis_sync.close()
- except Exception:
- # Older redis-py may not have close(); disconnect pool as fallback
- try:
- app.state.redis_sync.connection_pool.disconnect()
- except Exception:
- pass
- logger.info("Sync Redis client closed on shutdown")
- except Exception:
- logger.error("Error closing sync Redis client", exc_info=True)
|