"""ResultCache — LRU + TTL 인메모리 캐시.""" import time from collections import OrderedDict from typing import Any, Optional class ResultCache: """ BridgeResult를 LRU + TTL로 캐시한다. 사용 예:: cache = ResultCache(max_size=100, ttl=3600) cache.set("workflow_id_1", result) result = cache.get("workflow_id_1") # None if expired """ def __init__(self, max_size: int = 100, ttl: float = 3600.0): """ Args: max_size: 최대 캐시 항목 수 (초과 시 오래된 항목부터 제거) ttl: 각 항목의 Time-To-Live (초), 0이면 만료 없음 """ self.max_size = max_size self.ttl = ttl self._cache: OrderedDict[str, tuple[Any, float]] = OrderedDict() def set(self, key: str, value: Any) -> None: """값을 캐시에 저장한다.""" if key in self._cache: del self._cache[key] self._cache[key] = (value, time.time()) if len(self._cache) > self.max_size: self._cache.popitem(last=False) def get(self, key: str, default: Any = None) -> Any: """캐시에서 값을 가져온다 (TTL 확인).""" if key not in self._cache: return default value, stored_at = self._cache[key] if self.ttl > 0 and time.time() - stored_at > self.ttl: del self._cache[key] return default self._cache.move_to_end(key) return value def has(self, key: str) -> bool: """값이 캐시에 있고 유효한지 확인한다.""" if key not in self._cache: return False _, stored_at = self._cache[key] if self.ttl > 0 and time.time() - stored_at > self.ttl: del self._cache[key] return False return True def delete(self, key: str) -> None: """캐시에서 값을 삭제한다.""" self._cache.pop(key, None) def clear(self) -> None: """전체 캐시를 비운다.""" self._cache.clear() def size(self) -> int: """유효한 항목의 개수를 반환한다.""" to_delete = [] for key, (_, stored_at) in self._cache.items(): if self.ttl > 0 and time.time() - stored_at > self.ttl: to_delete.append(key) for key in to_delete: del self._cache[key] return len(self._cache) def __repr__(self) -> str: return f"ResultCache(size={self.size()}, max_size={self.max_size}, ttl={self.ttl}s)"