"""In-memory TTL cache for expensive scoring and metrics calculations. The cache is a simple dict with timestamps. It is invalidated when tests are validated, scores change, or an explicit ``invalidate`` call is made. Thread-safe: each worker process has its own dict, and the TTL ensures stale data does not persist longer than ``CACHE_TTL`` seconds. """ import time from typing import Any, Optional CACHE_TTL = 300 # 5 minutes _cache: dict[str, dict[str, Any]] = {} def get(key: str) -> Optional[Any]: """Return cached value if present and not expired, else None.""" entry = _cache.get(key) if entry is None: return None if time.time() - entry["ts"] > CACHE_TTL: _cache.pop(key, None) return None return entry["data"] def put(key: str, data: Any) -> None: """Store *data* under *key* with the current timestamp.""" _cache[key] = {"data": data, "ts": time.time()} def invalidate(key: Optional[str] = None) -> None: """Remove one key or clear the whole cache.""" if key is None: _cache.clear() else: _cache.pop(key, None) # ── High-level helpers ──────────────────────────────────────────────── def get_organization_score_cached(db): """Cached wrapper around ``calculate_organization_score``.""" from app.services.scoring_service import calculate_organization_score cached = get("org_score") if cached is not None: return cached result = calculate_organization_score(db) put("org_score", result) return result def get_operational_metrics_cached(db): """Cached wrapper around operational metrics (MTTD, MTTR, efficacy).""" from app.services.operational_metrics_service import ( calculate_mttd, calculate_mttr, calculate_detection_efficacy, calculate_alert_fidelity, calculate_coverage_velocity, calculate_validation_throughput, calculate_rejection_rate, ) cached = get("op_metrics") if cached is not None: return cached result = { "mttd": calculate_mttd(db), "mttr": calculate_mttr(db), "detection_efficacy": calculate_detection_efficacy(db), "alert_fidelity": calculate_alert_fidelity(db), "coverage_velocity": calculate_coverage_velocity(db), "validation_throughput": calculate_validation_throughput(db), "rejection_rate": calculate_rejection_rate(db), } put("op_metrics", result) return result