diff --git a/CHANGELOG.md b/CHANGELOG.md index 419c03e45..8527900d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +- Aircraft track backfill (`/api/opensky-track`, `/api/adsblol/trace`) now + answers 502 when the upstream body exceeds the 5 MB cap, instead of a 200 + whose error body the client read as an empty track. The failure is cached + like other upstream errors, so retries inside the 60 s window do not spend + OpenSky credits (Raushankumar0720, #720, #722). + - Saving a key from Provider Settings works again on Macs where Nix or Homebrew coreutils sit ahead of `/bin` on `PATH`. The credential hardener now spawns Apple's `/bin/chmod -N` by absolute path; GNU `chmod` has no `-N`, diff --git a/server/providers/aircraft/tracks.js b/server/providers/aircraft/tracks.js index d9f2a36d5..2c7b78a78 100644 --- a/server/providers/aircraft/tracks.js +++ b/server/providers/aircraft/tracks.js @@ -55,9 +55,9 @@ export function trackBackfillProxies() { } else { body = text; } - if (!tooLarge) { - cachePut(key, { at: Date.now(), status, body }); - } + // Cache the 502 like any upstream error: /tracks/all costs OpenSky + // credits, so a retry inside the window must not re-download the body. + cachePut(key, { at: Date.now(), status, body }); res.statusCode = status; res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.setHeader('Cache-Control', 'no-store'); diff --git a/src/tooling/liveProviders.test.mjs b/src/tooling/liveProviders.test.mjs index de8681490..38953eff9 100644 --- a/src/tooling/liveProviders.test.mjs +++ b/src/tooling/liveProviders.test.mjs @@ -354,27 +354,17 @@ test('military cooldown bounds untrusted Retry-After and defaults server errors' } }); -test('track backfill proxy returns 502 and skips cache when upstream response exceeds body cap', async (t) => { +test('track backfill proxy returns 502 on an oversized upstream body and caches it as an error', async (t) => { const tracks = install(providers.trackBackfillProxies(), true); let callCount = 0; t.mock.method(globalThis, 'fetch', async () => { callCount++; - if (callCount === 1) { - return { - ok: true, - status: 200, - headers: new Map(), - body: (async function* () { - yield Buffer.alloc(6 * 1024 * 1024, 'x'); - })(), - }; - } return { ok: true, status: 200, headers: new Map(), body: (async function* () { - yield Buffer.from(JSON.stringify({ path: [] })); + yield Buffer.alloc(6 * 1024 * 1024, 'x'); })(), }; }); @@ -385,9 +375,9 @@ test('track backfill proxy returns 502 and skips cache when upstream response ex error: 'Upstream track response too large', }); - // Verify failure was NOT cached: second request invokes upstream again and succeeds + // Cached as a 502 (never a 200): a retry inside the window neither + // reads as an empty track nor spends OpenSky credits on another download. const res2 = await tracks('/api/opensky-track', '?icao24=def456'); - assert.equal(res2.statusCode, 200); - assert.deepEqual(JSON.parse(res2.body), { path: [] }); - assert.equal(callCount, 2); + assert.equal(res2.statusCode, 502); + assert.equal(callCount, 1); });