redis_lifespan.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. """Async lifespan helper to initialize an asyncio Redis client and attach to app.state.redis.
  2. This separates the async redis client lifecycle from synchronous redis_client/redis_manager modules.
  3. """
  4. import os
  5. from contextlib import asynccontextmanager
  6. import redis.asyncio as aioredis
  7. import redis as redis_sync
  8. from fastapi import FastAPI
  9. import logging
  10. logger = logging.getLogger("app.redis")
  11. @asynccontextmanager
  12. async def lifespan(app: FastAPI):
  13. redis_url = os.getenv("REDIS_URL")
  14. redis_host = os.getenv("REDIS_HOST", "127.0.0.1")
  15. redis_port = int(os.getenv("REDIS_PORT", "6379"))
  16. redis_db = int(os.getenv("REDIS_DB", "0"))
  17. redis_password = os.getenv("REDIS_PASSWORD", None)
  18. if redis_url:
  19. async_client = aioredis.from_url(redis_url, decode_responses=True)
  20. sync_client = redis_sync.from_url(redis_url, decode_responses=True)
  21. else:
  22. client = redis.Redis(host=redis_host, port=redis_port, db=redis_db, password=redis_password, decode_responses=True)
  23. try:
  24. # Verify connectivity for both clients
  25. await async_client.ping()
  26. try:
  27. sync_client.ping()
  28. except Exception:
  29. logger.warning("Sync Redis ping failed during startup", exc_info=True)
  30. app.state.redis = async_client
  31. app.state.redis_sync = sync_client
  32. 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)
  33. except Exception as e:
  34. logger.error("Failed to initialize Redis clients: %s", e, exc_info=True)
  35. app.state.redis = None
  36. app.state.redis_sync = None
  37. try:
  38. yield
  39. finally:
  40. try:
  41. if getattr(app.state, "redis", None) is not None:
  42. await app.state.redis.close()
  43. logger.info("Async Redis client closed on shutdown")
  44. except Exception:
  45. logger.error("Error closing async Redis client", exc_info=True)
  46. # Close sync client
  47. try:
  48. if getattr(app.state, "redis_sync", None) is not None:
  49. try:
  50. app.state.redis_sync.close()
  51. except Exception:
  52. try:
  53. app.state.redis_sync.connection_pool.disconnect()
  54. except Exception:
  55. pass
  56. logger.info("Sync Redis client closed on shutdown")
  57. except Exception:
  58. logger.error("Error closing sync Redis client", exc_info=True)