1
0

fix(flights): give adsb.lol a timeout its tail actually fits in

The sky over California has been intermittently fake and nothing said so.

`http.ts` gives every upstream 6 s. Five consecutive calls to
`api.adsb.lol/v2/point` from the box that serves this deployment came back in
1.2 s, 5.7 s, 24.3 s, 3.5 s and 4.2 s — all `200`, all correct, and three of the
five past the shared default. The feed is a volunteer aggregator answering a
geographic query and its tail is long in a way an NWS observation's is not.

The resulting failure was invisible in the worst way. The fetch did not error, it
timed out, `getJson` returned `null` as designed, and `flights/index.ts` did the
right thing with a `null` — served the simulated plan. So visitors got fabricated
aircraft, `/health` still reported `flights: adsb` because the source was
configured and reachable, and `degraded[]` stayed empty because nothing had
degraded at boot. Every indicator this service has said it was fine.

25 s, above the slowest measured answer, and only for this upstream. It costs
nothing when the feed is quick: the call runs behind `createUpstream`'s TTL, not
on any visitor's request path. On a board whose entire claim is that the aircraft
are live, a slow real answer beats a fast invented one.

Not a regression from the studio build — `http.ts` is untouched by it; this was
already happening.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 19:49:48 -07:00
parent db074e9cf7
commit ab41302993
+25 -1
View File
@@ -83,7 +83,7 @@ export async function fetchAdsb(
);
return null;
}
const body = await getJson<AircraftEnvelope>(url);
const body = await getJson<AircraftEnvelope>(url, { timeoutMs: ADSB_TIMEOUT_MS });
if (body === null) return null;
return normalise(body, (dropped) =>
log?.warn(`flights:adsb: feed sent ${dropped + MAX_ROWS} aircraft; kept the first ${MAX_ROWS}`),
@@ -126,6 +126,30 @@ export async function readDump1090(path: string, log?: AdsbLog): Promise<Flights
*/
const MAX_ROWS = 5000;
/**
* How long this one upstream is given to answer, overriding `http.ts`'s 6 s.
*
* Not a guess. Five consecutive calls to `api.adsb.lol/v2/point` measured from
* the box that actually serves this deployment came back in 1.2 s, 5.7 s,
* 24.3 s, 3.5 s and 4.2 s — all of them `200`, none of them wrong, and three of
* the five outside the shared default. The feed is a volunteer-run aggregator
* answering a geographic query, and its tail is long in a way an NWS
* observation's is not.
*
* The failure that caused was invisible in the worst way: the request did not
* error, it timed out, and `flights/index.ts` did exactly what it should with a
* `null` — served the simulated plan. So the sky over California quietly stopped
* being real for whoever loaded the page in that window, the health endpoint
* still reported `flights: adsb` because the *source* was configured and
* reachable, and `degraded[]` stayed empty because nothing was degraded at boot.
*
* 25 s is above the slowest measured answer. It costs nothing when the feed is
* quick, because this runs behind `createUpstream`'s TTL and not on the request
* path of every visitor, and a slow real answer is worth more than a fast
* fabricated one on a board whose whole claim is that the aircraft are live.
*/
const ADSB_TIMEOUT_MS = 25_000;
/**
* One envelope, turned into a snapshot — or `null` if it is not an envelope.
*