Make the ADS-B upstream configurable, with a fallback behind it - #20
Conversation
|
Reviewed the diff ( 1.
2. No CI job runs the new tests 3. 4. Minor: 5. Test coverage gap (nice-to-have, not blocking) Everything else looks solid: the shared-time-budget design is correctly bounded below the consumer's 5s timeout, the 🤖 Generated with Claude Code |
The upstream URL was hardcoded to api.adsb.lol, so pointing a node anywhere else meant editing the image. ADSB_UPSTREAMS takes an ordered, comma-separated list of adsb.lol-format base URLs and tries each in turn until one answers; it defaults to api.adsb.lol alone, so a node told nothing behaves exactly as before. The fallback trigger is a failed fetch. adsb.lol refuses roughly 88% of one node's requests with 429 (measured over an hour on jonathan-node-1: 1023 attempts, 119 succeeded), and today the proxy answers those by serving its cached copy for up to 60 s. A second source gives it something current to serve instead. The chain shares one time budget rather than a timeout per source. Two sources at ADSBLOL_TIMEOUT_MS each would be 6 s worst case, past blah2-api's 5 s client timeout, turning a slow upstream into a consumer-side failure instead of the stale-but-served degradation that timeout is chosen to give. X-Data-Source now names the host that answered rather than always saying adsb.lol. Adds the repo's first tests: 5 cases, node --test test/proxy-chain.test.js. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
d0bbff9 to
6b4fd6a
Compare
tar1090.adsb_upstreams is the ordered list of adsb.lol-format sources the tar1090 proxy tries until one answers, emitted to tar1090.env as ADSB_UPSTREAMS and consumed by offworldlabs/tar1090-node#20. It ships as "https://adsb.retina.fm,https://api.adsb.lol": RETINA's own service first, adsb.lol behind it. Both compose manifests must list the variable. The proxy reads it from its own environment, so a manifest that omits it leaves the proxy on its built-in default with no error anywhere. Verified against jonathan-node-1, whose running container carries only ADSBLOL_ENABLED and ADSBLOL_RADIUS today. No migration. The key is new, so no deployed node has it in its first-boot copy of user.yml and the shipped default reaches all of them. Changing this default later will need a migrate_*(), as the four existing ones do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review Clean, well-commented change overall — the shared time-budget reasoning and the local-file precedence are both preserved correctly, and the tests exercise real behavior via subprocess + fake HTTP servers rather than mocking internals. PR description doesnt match the implementation (important) The PR bodys central claim is that this chain "picks whichever currently has usable positions," gated by two new env vars: What That mismatch matters because it contradicts the PRs own worked example. The body states adsb.retina.fm answers ~100% of polls but only has usable positions 0-90% of the time on a ~10s cycle, and gives this log trace as evidence the chain adapts: "adsb.retina.fm: 179 aircraft, 0 usable (0%), trying next source" followed by "api.adsb.lol: 260 aircraft, 220 usable (85%)". With the code as written, if adsb.retina.fm is listed first in ADSB_UPSTREAMS, it will win the chain on nearly every request purely because it returns 200 — even during the part of its cycle where 0% of positions are usable — and api.adsb.lol would rarely if ever be tried as a fallback. The "trying next source" behavior shown in the transcript is not something this diff can produce as written. Also, the PR claims "Adds the repos first tests: 14 cases over the chain," but Other notes (minor)
Security / performance No concerns — the URL construction uses RECEIVER_LAT/LON and a fixed path template rather than interpolating anything from the upstream list into a shell or eval context, error response bodies are capped when reading error details (err.length < 200), and the shared in-flight promise plus cache-age gating still prevent request storms against a failing upstream. |
What
ADSB_UPSTREAMSreplaces the hardcodedapi.adsb.lolURL with an ordered, comma-separated list of adsb.lol-format base URLs, tried in turn until one answers. It defaults toapi.adsb.lolalone, so a node told nothing behaves exactly as before.Pairs with offworldlabs/retina-node#44, which sets it to
https://adsb.retina.fm,https://api.adsb.lol.Why
adsb.lol refuses roughly 88% of one node's requests with 429. Measured over an hour on jonathan-node-1 from the proxy's own logs: 1023 attempts, 119 succeeded, 904 rate-limited. Today the proxy answers those refusals by serving its cached copy for up to 60 s. A second source gives it something current to serve instead.
Details
ADSBLOL_TIMEOUT_MSeach would be 6 s worst case, past blah2-api's 5 s client timeout, turning a slow upstream into a consumer-side failure instead of the stale-but-served degradation that timeout is chosen to give.X-Data-Sourcenames the host that answered rather than always sayingadsb.lol.ADSB_UPSTREAMS.Testing
Adds the repo's first tests: 5 cases, run three times to check for flakiness.
Covers first-answering-source wins and the second is never polled, fall-through on a 429, a local receiver winning over every remote source with no upstream polled at all, the shared time budget, and the unconfigured default.
🤖 Generated with Claude Code