|
from fastapi_cache import FastAPICache |
|
from fastapi_cache.backends.inmemory import InMemoryBackend |
|
from fastapi_cache.decorator import cache |
|
from app.config import CACHE_TTL |
|
import logging |
|
from app.core.formatting import LogFormatter |
|
from typing import Optional, Any |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class CustomInMemoryBackend(InMemoryBackend): |
|
def __init__(self): |
|
"""Initialize the cache backend""" |
|
super().__init__() |
|
self.cache = {} |
|
|
|
async def delete(self, key: str) -> bool: |
|
"""Delete a key from the cache""" |
|
try: |
|
if key in self.cache: |
|
del self.cache[key] |
|
return True |
|
return False |
|
except Exception as e: |
|
logger.error( |
|
LogFormatter.error(f"Failed to delete key {key} from cache", e) |
|
) |
|
return False |
|
|
|
async def get(self, key: str) -> Any: |
|
"""Get a value from the cache""" |
|
return self.cache.get(key) |
|
|
|
async def set(self, key: str, value: Any, expire: Optional[int] = None) -> None: |
|
"""Set a value in the cache""" |
|
self.cache[key] = value |
|
|
|
|
|
def setup_cache(): |
|
"""Initialize FastAPI Cache with in-memory backend""" |
|
try: |
|
logger.info(LogFormatter.section("CACHE INITIALIZATION")) |
|
FastAPICache.init(backend=CustomInMemoryBackend(), prefix="fastapi-cache") |
|
logger.info(LogFormatter.success("Cache initialized successfully")) |
|
except Exception as e: |
|
logger.error(LogFormatter.error("Failed to initialize cache", e)) |
|
raise |
|
|
|
|
|
async def invalidate_cache_key(key: str): |
|
"""Invalidate a specific cache key""" |
|
try: |
|
backend = FastAPICache.get_backend() |
|
if hasattr(backend, "delete"): |
|
await backend.delete(key) |
|
logger.info(LogFormatter.success(f"Cache invalidated for key: {key}")) |
|
else: |
|
logger.warning( |
|
LogFormatter.warning("Cache backend does not support deletion") |
|
) |
|
except Exception as e: |
|
logger.error(LogFormatter.error(f"Failed to invalidate cache key: {key}", e)) |
|
|
|
|
|
def build_cache_key(*args) -> str: |
|
"""Build a cache key from multiple arguments""" |
|
return ":".join(str(arg) for arg in args if arg is not None) |
|
|
|
|
|
def cached(expire: int = CACHE_TTL, key_builder=None): |
|
"""Decorator for caching endpoint responses |
|
|
|
Args: |
|
expire (int): Cache TTL in seconds |
|
key_builder (callable, optional): Custom key builder function |
|
""" |
|
return cache(expire=expire, key_builder=key_builder) |
|
|