SaarthiAI / app /cache.py
parthmax24's picture
working proto 5
8b96826
Raw
History Blame Contribute Delete
2.05 kB
"""MongoDB key-value cache with TTL. Replaces SQLite. Fail-open on every operation."""
import functools
import hashlib
import json
import logging
from datetime import datetime, timezone, timedelta
from app import config
logger = logging.getLogger("saarthi.cache")
_collection = None
def _get_collection():
global _collection
if _collection is None:
from app.db import get_db
col = get_db()["api_cache"]
col.create_index("expires_at", expireAfterSeconds=0)
_collection = col
return _collection
def get(key: str):
"""Return cached value or None. Any failure is a miss."""
try:
doc = _get_collection().find_one({"key": key}, {"_id": 0, "value": 1})
return doc["value"] if doc else None
except Exception as error:
logger.warning("Cache read failed (treating as miss): %s", error)
return None
def set(key: str, value, ttl_seconds: int):
"""Write to cache. Any failure is silently skipped."""
try:
expires_at = datetime.now(timezone.utc) + timedelta(seconds=ttl_seconds)
_get_collection().replace_one(
{"key": key},
{"key": key, "value": value, "expires_at": expires_at},
upsert=True,
)
except Exception as error:
logger.warning("Cache write failed (skipping): %s", error)
def cached(ttl_seconds: int):
"""Decorator: cache a function's JSON-serializable result by its arguments."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
raw = json.dumps(
[func.__module__, func.__name__, args, kwargs],
sort_keys=True, default=str,
)
key = hashlib.sha256(raw.encode()).hexdigest()
hit = get(key)
if hit is not None:
return hit
result = func(*args, **kwargs)
if result is not None:
set(key, result, ttl_seconds)
return result
return wrapper
return decorator