67 lines
2.1 KiB
Python
Raw Normal View History

2026-07-23 01:15:04 +02:00
# retoor <retoor@molodetz.nl>
from __future__ import annotations
import time
from collections import OrderedDict
class TTLCache:
def __init__(self, ttl: int, max_size: int = 0):
self.ttl = ttl
self.max_size = max_size
self._store: OrderedDict[str, tuple[object, float]] = OrderedDict()
def get(self, key: str):
entry = self._store.get(key)
if entry is None:
return None
value, expiry = entry
if time.time() >= expiry:
self._store.pop(key, None)
return None
self._store.move_to_end(key)
return value
def set(self, key: str, value) -> None:
self._store[key] = (value, time.time() + self.ttl)
self._store.move_to_end(key)
if self.max_size and len(self._store) > self.max_size:
self._store.popitem(last=False)
def pop(self, key: str) -> None:
self._store.pop(key, None)
def clear(self) -> None:
self._store.clear()
class CacheStateGate:
def __init__(self, *, ttl: int = 60, max_staleness: float = 5.0):
self.ttl = ttl
self.max_staleness = max_staleness
self._cache = TTLCache(ttl=ttl)
self._versions: dict[str, int] = {}
self._bumped_at: dict[str, float] = {}
def get_version(self, name: str) -> int:
return int(self._versions.get(name, 0))
def bump(self, name: str) -> int:
next_version = self.get_version(name) + 1
self._versions[name] = next_version
self._bumped_at[name] = time.time()
self._cache.clear()
return next_version
def get(self, key: str, version_name: str, loader):
bumped_at = self._bumped_at.get(version_name, 0.0)
if bumped_at and (time.time() - bumped_at) < self.max_staleness:
self._cache.pop(key)
version_key = f"{version_name}:{self.get_version(version_name)}"
cached = self._cache.get(version_key)
if cached is not None:
return cached
value = loader()
self._cache.set(version_key, value)
return value