""" Service Layer - CacheService(缓存服务) 使用 LRU 缓存 + TTL 过期策略,防止内存泄漏。 参考设计文档:第 2.3 节 """ import asyncio import time from collections import OrderedDict from typing import Optional, Any import fnmatch import logging logger = logging.getLogger(__name__) class CacheService: """缓存服务 - LRU 缓存 + TTL 过期""" def __init__(self, max_size: int = 1000): self.cache: OrderedDict[str, tuple] = OrderedDict() # key -> (value, expire_time) self.max_size = max_size self.lock = asyncio.Lock() async def get(self, key: str) -> Optional[Any]: """获取缓存值(异步,带锁)""" async with self.lock: if key not in self.cache: return None value, expire_time = self.cache[key] # 检查是否过期 if expire_time and time.time() > expire_time: del self.cache[key] logger.debug(f"Cache expired: {key}") return None # LRU: 移到末尾 self.cache.move_to_end(key) return value async def set(self, key: str, value: Any, ttl: int = 3600) -> None: """设置缓存值(异步,带锁)""" async with self.lock: expire_time = time.time() + ttl if ttl else None # 如果缓存已满,删除最旧的条目 if len(self.cache) >= self.max_size and key not in self.cache: self.cache.popitem(last=False) # FIFO 删除 logger.debug(f"Cache full, evicted oldest entry") self.cache[key] = (value, expire_time) self.cache.move_to_end(key) logger.debug(f"Cache set: {key} (TTL={ttl}s)") async def invalidate(self, pattern: str) -> int: """按模式清除缓存(支持 * 通配符)""" async with self.lock: if pattern == "*": count = len(self.cache) self.cache.clear() logger.info(f"Cache cleared: {count} entries") return count keys_to_delete = [k for k in self.cache.keys() if fnmatch.fnmatch(k, pattern)] for key in keys_to_delete: del self.cache[key] logger.info(f"Cache invalidated: {len(keys_to_delete)} entries matching '{pattern}'") return len(keys_to_delete) async def get_stats(self) -> dict: """获取缓存统计信息""" async with self.lock: now = time.time() expired_count = sum(1 for _, expire_time in self.cache.values() if expire_time and now > expire_time) return { "size": len(self.cache), "max_size": self.max_size, "expired_count": expired_count, "utilization": len(self.cache) / self.max_size if self.max_size > 0 else 0 } async def clear(self) -> None: """清空缓存""" async with self.lock: self.cache.clear() logger.info("Cache cleared")