bio-nexus-api / app /services /cache.py
Samad14's picture
Add sequencing pipeline (FASTQ -> QC -> alignment -> variant calling -> report)
da793ed verified
Raw
History Blame Contribute Delete
2.29 kB
import logging
import redis
import hashlib
import json
import functools
from typing import Callable, Any
from app.config import settings
logger = logging.getLogger(__name__)
_redis = None
_cache_stats = {"hits": 0, "misses": 0}
def init_redis():
global _redis
try:
_redis = redis.from_url(settings.REDIS_URL, decode_responses=True)
_redis.ping()
logger.info("Redis connected")
except Exception:
_redis = None
logger.warning("Redis unavailable — caching disabled")
def get_redis():
return _redis
def cache_get(key: str) -> str | None:
r = get_redis()
if r:
val = r.get(key)
if val is not None:
_cache_stats["hits"] += 1
return val
_cache_stats["misses"] += 1
return None
def cache_set(key: str, value: str, ttl: int = 86400):
r = get_redis()
if r:
r.setex(key, ttl, value)
def get_cache_stats() -> dict:
return {**_cache_stats, "redis_connected": _redis is not None}
def reset_cache_stats():
_cache_stats["hits"] = 0
_cache_stats["misses"] = 0
def ttl_cache(ttl: int = 86400, prefix: str = "cache"):
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
async def wrapper(self, input: dict, *args, **kwargs):
raw = json.dumps(input, sort_keys=True)
key_hash = hashlib.sha256(raw.encode()).hexdigest()[:16]
cache_key = f"{prefix}:{key_hash}"
cached = cache_get(cache_key)
if cached is not None:
try:
result = json.loads(cached)
if isinstance(result, dict):
result["from_cache"] = True
return result
except (json.JSONDecodeError, TypeError):
pass
result = await func(self, input, *args, **kwargs)
try:
cache_set(cache_key, json.dumps(result), ttl=ttl)
except (TypeError, ValueError):
pass
if isinstance(result, dict):
result["from_cache"] = False
return result
return wrapper
return decorator