Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
import hashlib
|
|
import json
|
|
import logging
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class InMemoryCache:
|
|
def __init__(self) -> None:
|
|
self._store: dict[str, tuple[str, datetime]] = {}
|
|
|
|
async def get(self, key: str) -> str | None:
|
|
if key not in self._store:
|
|
return None
|
|
value, expires_at = self._store[key]
|
|
if datetime.now(UTC) >= expires_at:
|
|
del self._store[key]
|
|
return None
|
|
return value
|
|
|
|
async def set(self, key: str, value: str, ttl: int = 600) -> None:
|
|
self._store[key] = (value, datetime.now(UTC) + timedelta(seconds=ttl))
|
|
|
|
async def ping(self) -> bool:
|
|
return True
|
|
|
|
|
|
class RedisCache:
|
|
def __init__(self, client) -> None:
|
|
self._client = client
|
|
|
|
async def get(self, key: str) -> str | None:
|
|
return await self._client.get(key)
|
|
|
|
async def set(self, key: str, value: str, ttl: int = 600) -> None:
|
|
await self._client.setex(key, ttl, value)
|
|
|
|
async def ping(self) -> bool:
|
|
try:
|
|
await self._client.ping()
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
_cache: InMemoryCache | RedisCache | None = None
|
|
|
|
|
|
async def get_cache() -> InMemoryCache | RedisCache:
|
|
global _cache
|
|
if _cache is not None:
|
|
return _cache
|
|
|
|
from axon.config import settings
|
|
|
|
if settings.redis_url:
|
|
try:
|
|
import redis.asyncio as aioredis
|
|
client = aioredis.from_url(settings.redis_url, decode_responses=True)
|
|
await client.ping()
|
|
_cache = RedisCache(client)
|
|
logger.info("Connected to Redis at %s", settings.redis_url)
|
|
return _cache
|
|
except Exception as exc:
|
|
logger.warning("Redis unavailable (%s), falling back to in-memory cache", exc)
|
|
|
|
_cache = InMemoryCache()
|
|
return _cache
|
|
|
|
|
|
def make_cache_key(prefix: str, **kwargs) -> str:
|
|
payload = json.dumps(kwargs, sort_keys=True, default=str)
|
|
digest = hashlib.sha256(payload.encode()).hexdigest()
|
|
return f"{prefix}:{digest}"
|