halide / storage /cache.py
Lonelyguyse1's picture
Upload Halide Space: app, models, pipeline, storage, ui packages
a823f6a verified
Raw
History Blame Contribute Delete
2.55 kB
"""Caching. In-process LRU cache for diagnosis results keyed by image hash.
For privacy, we hash the image bytes; the image itself is never persisted
in the cache. Identical scans produce identical hashes, giving us a simple
content-addressed cache.
"""
from __future__ import annotations
import hashlib
import json
import logging
import time
from collections import OrderedDict
from threading import Lock
from typing import Any
logger = logging.getLogger(__name__)
class DiagnosisCache:
"""Thread-safe LRU cache for diagnosis results."""
def __init__(self, max_size: int = 64, ttl_seconds: int = 3600) -> None:
self._max_size = max_size
self._ttl = ttl_seconds
self._store: OrderedDict[str, tuple[float, dict]] = OrderedDict()
self._lock = Lock()
self._hits = 0
self._misses = 0
@staticmethod
def hash_image(image_bytes: bytes) -> str:
return hashlib.sha256(image_bytes).hexdigest()
def get(self, image_bytes: bytes) -> dict | None:
key = self.hash_image(image_bytes)
now = time.time()
with self._lock:
entry = self._store.get(key)
if entry is None:
self._misses += 1
return None
ts, value = entry
if now - ts > self._ttl:
del self._store[key]
self._misses += 1
return None
self._store.move_to_end(key)
self._hits += 1
logger.info("Cache hit for %s", key[:12])
return value
def put(self, image_bytes: bytes, value: dict) -> None:
key = self.hash_image(image_bytes)
now = time.time()
with self._lock:
self._store[key] = (now, value)
self._store.move_to_end(key)
while len(self._store) > self._max_size:
self._store.popitem(last=False)
def stats(self) -> dict:
with self._lock:
return {
"size": len(self._store),
"max_size": self._max_size,
"hits": self._hits,
"misses": self._misses,
}
def clear(self) -> None:
with self._lock:
self._store.clear()
self._hits = 0
self._misses = 0
_default_cache: DiagnosisCache | None = None
def get_cache() -> DiagnosisCache:
global _default_cache
if _default_cache is None:
_default_cache = DiagnosisCache()
return _default_cache
__all__ = ["DiagnosisCache", "get_cache"]