Ak001z's picture
auto-deploy from CI (8426602)
45ebbd2 verified
Raw
History Blame Contribute Delete
2.31 kB
"""Redis client singleton with graceful memory fallback."""
import asyncio
import contextlib
import os
import structlog
logger = structlog.get_logger(__name__)
_redis = None
_fallback = False
try:
import redis.asyncio as aioredis
except ImportError:
aioredis = None # type: ignore[assignment]
_ENVIRONMENT = os.getenv("ENVIRONMENT", "production")
_MAX_RETRIES = 3
_RETRY_DELAY = 1.0
async def init_redis() -> None:
global _redis, _fallback
url = os.getenv("REDIS_URL")
if not url or aioredis is None:
msg = "REDIS_URL not set or redis not installed"
if _ENVIRONMENT == "production":
logger.warning(msg + " — production mode without Redis, using memory fallback")
else:
logger.warning(msg + " — falling back to memory (dev mode)")
_fallback = True
return
for attempt in range(1, _MAX_RETRIES + 1):
try:
_redis = aioredis.from_url(
url,
decode_responses=True,
max_connections=50,
socket_connect_timeout=5,
socket_timeout=5,
retry_on_timeout=True,
)
await _redis.ping()
_fallback = False
logger.info("Redis connected", url=url.split("@")[-1])
return
except Exception as exc:
logger.warning(
"Redis connection attempt failed",
attempt=attempt,
max_retries=_MAX_RETRIES,
error=str(exc)[:120],
)
if attempt < _MAX_RETRIES:
await asyncio.sleep(_RETRY_DELAY * attempt)
# All retries exhausted
if _ENVIRONMENT == "production":
logger.warning(
"Redis unreachable after %d retries — production mode using memory fallback",
_MAX_RETRIES,
)
else:
logger.warning("Redis unreachable — falling back to memory (dev mode)")
_redis = None
_fallback = True
async def close_redis() -> None:
global _redis, _fallback
if _redis:
with contextlib.suppress(Exception):
await _redis.aclose()
_redis = None
_fallback = True
def get_redis():
"""Return Redis client or None (dev fallback only)."""
return _redis