|
import asyncio |
|
import json |
|
import hashlib |
|
from typing import Any, Callable, Dict, Optional |
|
from functools import wraps |
|
import logging |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
_cache: Dict[str, Any] = {} |
|
_cache_lock = asyncio.Lock() |
|
|
|
def build_cache_key(namespace: str, *args, **kwargs) -> str: |
|
"""Build a cache key from namespace and parameters""" |
|
key_data = f"{namespace}:{args}:{sorted(kwargs.items())}" |
|
return hashlib.md5(key_data.encode()).hexdigest() |
|
|
|
def cached(expire: int = 300, key_builder: Optional[Callable] = None): |
|
""" |
|
Cache decorator for FastAPI endpoints |
|
|
|
Args: |
|
expire: Cache expiration time in seconds |
|
key_builder: Function to build cache key |
|
""" |
|
def decorator(func: Callable) -> Callable: |
|
@wraps(func) |
|
async def wrapper(*args, **kwargs): |
|
|
|
if key_builder: |
|
cache_key = key_builder(func, **kwargs) |
|
else: |
|
cache_key = build_cache_key(func.__name__, *args, **kwargs) |
|
|
|
|
|
async with _cache_lock: |
|
if cache_key in _cache: |
|
cached_data, timestamp = _cache[cache_key] |
|
import time |
|
if time.time() - timestamp < expire: |
|
logger.debug(f"Cache hit for key: {cache_key}") |
|
return cached_data |
|
else: |
|
|
|
del _cache[cache_key] |
|
|
|
|
|
logger.debug(f"Cache miss for key: {cache_key}") |
|
result = await func(*args, **kwargs) |
|
|
|
|
|
async with _cache_lock: |
|
import time |
|
_cache[cache_key] = (result, time.time()) |
|
|
|
return result |
|
return wrapper |
|
return decorator |
|
|
|
def setup_cache(): |
|
"""Setup cache configuration""" |
|
logger.info("FastAPI cache initialized with in-memory backend") |