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:
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user