from __future__ import annotations import threading import time from collections import OrderedDict from dataclasses import dataclass @dataclass class DedupeConfig: ttl_seconds: float = 300.0 max_entries: int = 10000 class MessageDeduplicator: def __init__(self, config: DedupeConfig | None = None): self._config = config or DedupeConfig() self._cache: OrderedDict[str, float] = OrderedDict() self._lock = threading.Lock() def is_duplicate(self, message_id: str) -> bool: now = time.monotonic() with self._lock: self._evict_expired(now) if message_id in self._cache: return True if len(self._cache) >= self._config.max_entries: self._cache.popitem(last=False) self._cache[message_id] = now self._cache.move_to_end(message_id) return False def record(self, message_id: str) -> None: now = time.monotonic() with self._lock: self._evict_expired(now) self._cache[message_id] = now self._cache.move_to_end(message_id) def clear(self) -> None: with self._lock: self._cache.clear() def _evict_expired(self, now: float) -> None: expired = [k for k, t in self._cache.items() if now - t > self._config.ttl_seconds] for k in expired: del self._cache[k]