Skip to content

Commit b315cba

Browse files
Add cache age tracking for staleness warnings
- cache.py: age_seconds(key) returns seconds since cache entry was written - search.py: cache_age_seconds field on SearchReport and ScoredSearchReport, populated when results come from cache (max age across providers) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d717cd8 commit b315cba

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

src/opensky/cache.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,24 @@ def put(key: str, value: Any, ttl: int = _DEFAULT_TTL) -> None:
9393
tmp.replace(path)
9494

9595

96+
def age_seconds(key: str) -> int | None:
97+
"""Return seconds since this cache entry was written, or None if not cached/expired."""
98+
path = _CACHE_DIR / f"{key}.json"
99+
if not path.exists():
100+
return None
101+
try:
102+
data = json.loads(path.read_text())
103+
expires_at = data.get("expires_at")
104+
if not expires_at:
105+
return None
106+
age = int(_DEFAULT_TTL - (expires_at - time.time()))
107+
if age < 0:
108+
return None # entry expired
109+
return age
110+
except Exception:
111+
return None
112+
113+
96114
def clear() -> None:
97115
_ensure_cache_dir()
98116
with _cache_lock:

src/opensky/search.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ class SearchReport:
4040
results: list[FlightResult]
4141
successful_providers: list[str] = field(default_factory=list)
4242
failed_providers: list[ProviderFailure] = field(default_factory=list)
43+
cache_age_seconds: int | None = None
4344

4445

4546
@dataclass
4647
class ScoredSearchReport:
4748
results: list[ScoredFlight]
4849
successful_providers: list[str] = field(default_factory=list)
4950
failed_providers: list[ProviderFailure] = field(default_factory=list)
51+
cache_age_seconds: int | None = None
5052

5153

5254
@dataclass
@@ -162,6 +164,7 @@ def search_one_report(
162164
all_results: list[FlightResult] = []
163165
successful_providers: list[str] = []
164166
failed_providers: list[ProviderFailure] = []
167+
max_cache_age: int | None = None
165168

166169
for provider in self._providers:
167170
ck = cache.cache_key(
@@ -175,6 +178,9 @@ def search_one_report(
175178
if cached is not None:
176179
successful_providers.append(provider.name)
177180
all_results.extend(cached)
181+
age = cache.age_seconds(ck)
182+
if age is not None:
183+
max_cache_age = max(max_cache_age or 0, age)
178184
continue
179185

180186
try:
@@ -210,6 +216,7 @@ def search_one_report(
210216
results=all_results,
211217
successful_providers=successful_providers,
212218
failed_providers=failed_providers,
219+
cache_age_seconds=max_cache_age,
213220
)
214221

215222
def search_scored(
@@ -282,6 +289,7 @@ def search_scored_report(
282289
results=scored,
283290
successful_providers=search_report.successful_providers,
284291
failed_providers=search_report.failed_providers,
292+
cache_age_seconds=search_report.cache_age_seconds,
285293
)
286294

287295
def search_round_trip(

0 commit comments

Comments
 (0)