added dedicated test file and moved timed_cache to tools

This commit is contained in:
Mystical
2025-03-15 13:19:59 +05:30
committed by Paul Robinson
parent bf699351e1
commit c86f3d0467
12 changed files with 201 additions and 67 deletions

View File

@ -424,7 +424,6 @@ class GossipsubFactory(factory.Factory):
degree = GOSSIPSUB_PARAMS.degree
degree_low = GOSSIPSUB_PARAMS.degree_low
degree_high = GOSSIPSUB_PARAMS.degree_high
time_to_live = GOSSIPSUB_PARAMS.time_to_live
gossip_window = GOSSIPSUB_PARAMS.gossip_window
gossip_history = GOSSIPSUB_PARAMS.gossip_history
heartbeat_initial_delay = GOSSIPSUB_PARAMS.heartbeat_initial_delay
@ -448,6 +447,7 @@ class PubsubFactory(factory.Factory):
router: IPubsubRouter,
cache_size: int,
seen_ttl: int,
sweep_interval: int,
strict_signing: bool,
msg_id_constructor: Callable[[rpc_pb2.Message], bytes] = None,
) -> AsyncIterator[Pubsub]:
@ -456,6 +456,7 @@ class PubsubFactory(factory.Factory):
router=router,
cache_size=cache_size,
seen_ttl=seen_ttl,
sweep_interval=sweep_interval,
strict_signing=strict_signing,
msg_id_constructor=msg_id_constructor,
)
@ -470,7 +471,8 @@ class PubsubFactory(factory.Factory):
number: int,
routers: Sequence[IPubsubRouter],
cache_size: int = None,
seen_ttl: int = None,
seen_ttl: int = 120,
sweep_interval: int = 60,
strict_signing: bool = False,
security_protocol: TProtocol = None,
muxer_opt: TMuxerOptions = None,
@ -488,6 +490,7 @@ class PubsubFactory(factory.Factory):
router,
cache_size,
seen_ttl,
sweep_interval,
strict_signing,
msg_id_constructor,
)
@ -503,6 +506,7 @@ class PubsubFactory(factory.Factory):
number: int,
cache_size: int = None,
seen_ttl: int = 120,
sweep_interval: int = 60,
strict_signing: bool = False,
protocols: Sequence[TProtocol] = None,
security_protocol: TProtocol = None,
@ -520,6 +524,7 @@ class PubsubFactory(factory.Factory):
floodsubs,
cache_size,
seen_ttl,
sweep_interval,
strict_signing,
security_protocol=security_protocol,
muxer_opt=muxer_opt,
@ -567,7 +572,6 @@ class PubsubFactory(factory.Factory):
degree=degree,
degree_low=degree_low,
degree_high=degree_high,
time_to_live=time_to_live,
gossip_window=gossip_window,
heartbeat_interval=heartbeat_interval,
)

View File

View File

@ -0,0 +1,54 @@
from abc import (
ABC,
abstractmethod,
)
import threading
import time
class BaseTimedCache(ABC):
"""Base class for Timed Cache with cleanup mechanism."""
cache: dict[bytes, int]
def __init__(self, ttl: int, sweep_interval: int = 60) -> None:
"""
Initialize a new BaseTimedCache with a time-to-live for cache entries
:param ttl: no of seconds as time-to-live for each cache entry
"""
self.ttl = ttl
self.sweep_interval = sweep_interval
self.lock = threading.Lock()
self.cache = {}
self._stop_event = threading.Event()
self._thread = threading.Thread(target=self._background_cleanup, daemon=True)
self._thread.start()
def _background_cleanup(self) -> None:
while not self._stop_event.wait(self.sweep_interval):
self._sweep()
def _sweep(self) -> None:
"""Removes expired entries from the cache."""
now = time.time()
with self.lock:
keys_to_remove = [key for key, expiry in self.cache.items() if expiry < now]
for key in keys_to_remove:
del self.cache[key]
def stop(self) -> None:
"""Stops the background cleanup thread."""
self._stop_event.set()
self._thread.join()
def length(self) -> int:
return len(self.cache)
@abstractmethod
def add(self, key: bytes) -> bool:
"""To be implemented in subclasses."""
@abstractmethod
def has(self, key: bytes) -> bool:
"""To be implemented in subclasses."""

View File

@ -0,0 +1,20 @@
import time
from .base_timed_cache import (
BaseTimedCache,
)
class FirstSeenCache(BaseTimedCache):
"""Cache where expiry is set only when first added."""
def add(self, key: bytes) -> bool:
with self.lock:
if key in self.cache:
return False
self.cache[key] = int(time.time()) + self.ttl
return True
def has(self, key: bytes) -> bool:
with self.lock:
return key in self.cache

View File

@ -0,0 +1,22 @@
import time
from .base_timed_cache import (
BaseTimedCache,
)
class LastSeenCache(BaseTimedCache):
"""Cache where expiry is updated on every access."""
def add(self, key: bytes) -> bool:
with self.lock:
is_new = key not in self.cache
self.cache[key] = int(time.time()) + self.ttl
return is_new
def has(self, key: bytes) -> bool:
with self.lock:
if key in self.cache:
self.cache[key] = int(time.time()) + self.ttl
return True
return False