bgp: clamp cache age to zero against windows mtime clock skew

time.time() - path.stat().st_mtime went negative under load when NTFS
stamped a write's mtime slightly ahead of the wall clock read a moment
later, making a ttl_hours=0 cache lookup serve stale data instead of
refetching. surfaced as a flaky failure in test_cached_json_refetches_once_the_entry_expires
during the full suite run before task 40's smoke test.
This commit is contained in:
Andrew Pimenov 2026-08-09 03:39:35 +03:00
parent 0d6d5405b3
commit 10c5939bcb
2 changed files with 19 additions and 1 deletions

View file

@ -160,7 +160,8 @@ async def cached_json(
fetch: Callable[[], Awaitable[dict]], fetch: Callable[[], Awaitable[dict]],
) -> dict: ) -> dict:
path = Path(cache_dir) / f"{key}.json" path = Path(cache_dir) / f"{key}.json"
if path.exists() and (time.time() - path.stat().st_mtime) < ttl_hours * 3600: age = max(0.0, time.time() - path.stat().st_mtime) if path.exists() else None
if age is not None and age < ttl_hours * 3600:
try: try:
return json.loads(path.read_text(encoding="utf-8")) return json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError): except (OSError, ValueError):

View file

@ -185,6 +185,23 @@ async def test_cached_json_refetches_once_the_entry_expires(tmp_path: Path):
assert len(calls) == 2 assert len(calls) == 2
async def test_cached_json_refetches_when_mtime_reads_ahead_of_now(tmp_path: Path):
import os
calls = []
async def fetch() -> dict:
calls.append(1)
return {"value": len(calls)}
await cached_json(tmp_path, "k", ttl_hours=24, fetch=fetch)
future = (tmp_path / "k.json").stat().st_mtime + 5
os.utime(tmp_path / "k.json", (future, future))
result = await cached_json(tmp_path, "k", ttl_hours=0, fetch=fetch)
assert result == {"value": 2}
assert len(calls) == 2
async def test_cached_json_survives_a_corrupt_cache_file(tmp_path: Path): async def test_cached_json_survives_a_corrupt_cache_file(tmp_path: Path):
(tmp_path / "k.json").write_text("{not json", encoding="utf-8") (tmp_path / "k.json").write_text("{not json", encoding="utf-8")