-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathttl_cache.py
More file actions
65 lines (48 loc) · 1.95 KB
/
Copy pathttl_cache.py
File metadata and controls
65 lines (48 loc) · 1.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""Lightweight TTL cache for reducing redundant API calls within a bot cycle.
Provides two classes:
- TTLCacheEntry: single-value cache with TTL (e.g. meta, user_state)
- TTLCacheMap: keyed cache with TTL (e.g. per-coin market data, per-DEX mids)
Both use time.monotonic() for timing.
"""
import time
from typing import Generic, Hashable, Optional, TypeVar
T = TypeVar('T')
K = TypeVar('K', bound=Hashable)
class TTLCacheEntry(Generic[T]):
"""Single-value cache with TTL."""
def __init__(self, ttl: float) -> None:
self.ttl = ttl
self._value: Optional[T] = None
self._time: float = 0.0
def get(self) -> Optional[T]:
"""Return cached value if still fresh, else None."""
if self._value is not None and (time.monotonic() - self._time) < self.ttl:
return self._value
return None
def set(self, value: T) -> None:
"""Store a value with the current timestamp."""
self._value = value
self._time = time.monotonic()
def invalidate(self) -> None:
"""Clear the cached value."""
self._value = None
class TTLCacheMap(Generic[K, T]):
"""Keyed cache where each entry has a shared TTL."""
def __init__(self, ttl: float) -> None:
self.ttl = ttl
self._data: dict[K, tuple[float, T]] = {}
def get(self, key: K) -> Optional[T]:
"""Return cached value for *key* if still fresh, else None."""
entry = self._data.get(key)
if entry is not None and (time.monotonic() - entry[0]) < self.ttl:
return entry[1]
return None
def set(self, key: K, value: T) -> None:
"""Store a value for *key* with the current timestamp."""
self._data[key] = (time.monotonic(), value)
def invalidate(self, key: K) -> None:
"""Remove a single key from the cache."""
self._data.pop(key, None)
def invalidate_all(self) -> None:
"""Clear all cached entries."""
self._data.clear()