| """Caching layer for HuggingFace Crypto Data Engine"""
|
| from __future__ import annotations
|
| from typing import Optional, Any
|
| from datetime import datetime, timedelta
|
| import time
|
| import json
|
| from dataclasses import dataclass
|
|
|
|
|
| @dataclass
|
| class CacheEntry:
|
| """Cache entry with TTL"""
|
| value: Any
|
| expires_at: float
|
|
|
|
|
| class MemoryCache:
|
| """In-memory cache with TTL support"""
|
|
|
| def __init__(self):
|
| self._cache: dict[str, CacheEntry] = {}
|
| self._hits = 0
|
| self._misses = 0
|
|
|
| def get(self, key: str) -> Optional[Any]:
|
| """Get value from cache"""
|
| if key not in self._cache:
|
| self._misses += 1
|
| return None
|
|
|
| entry = self._cache[key]
|
|
|
|
|
| if time.time() > entry.expires_at:
|
| del self._cache[key]
|
| self._misses += 1
|
| return None
|
|
|
| self._hits += 1
|
| return entry.value
|
|
|
| def set(self, key: str, value: Any, ttl: int):
|
| """Set value in cache with TTL in seconds"""
|
| expires_at = time.time() + ttl
|
| self._cache[key] = CacheEntry(value=value, expires_at=expires_at)
|
|
|
| def delete(self, key: str):
|
| """Delete key from cache"""
|
| if key in self._cache:
|
| del self._cache[key]
|
|
|
| def clear(self):
|
| """Clear all cache entries"""
|
| self._cache.clear()
|
| self._hits = 0
|
| self._misses = 0
|
|
|
| def get_stats(self) -> dict:
|
| """Get cache statistics"""
|
| total = self._hits + self._misses
|
| hit_rate = (self._hits / total) if total > 0 else 0
|
|
|
| return {
|
| "size": len(self._cache),
|
| "hits": self._hits,
|
| "misses": self._misses,
|
| "hitRate": round(hit_rate, 2)
|
| }
|
|
|
| def cleanup_expired(self):
|
| """Remove expired entries"""
|
| current_time = time.time()
|
| expired_keys = [
|
| key for key, entry in self._cache.items()
|
| if current_time > entry.expires_at
|
| ]
|
|
|
| for key in expired_keys:
|
| del self._cache[key]
|
|
|
|
|
|
|
| cache = MemoryCache()
|
|
|
|
|
| def cache_key(prefix: str, **kwargs) -> str:
|
| """Generate cache key from prefix and parameters"""
|
| params = ":".join(f"{k}={v}" for k, v in sorted(kwargs.items()))
|
| return f"{prefix}:{params}" if params else prefix
|
|
|
|
|
| async def get_or_set(
|
| key: str,
|
| ttl: int,
|
| factory: callable
|
| ) -> Any:
|
| """Get from cache or compute and store"""
|
|
|
| cached = cache.get(key)
|
| if cached is not None:
|
| return cached
|
|
|
|
|
| value = await factory()
|
|
|
|
|
| cache.set(key, value, ttl)
|
|
|
| return value
|
|
|