perf(analysis): bound the hop-counts scan; fix(ui): surface telemetry request failures - #4580
Conversation
|
Claude finished @Yeraze's task —— View job PR ReviewTasks
SummaryThis is a clean, well-scoped pair of fixes. Both changes are correct and the reasoning is sound. Notes below are minor.
|
Follow-up from the #4570 review. `getHopCounts` fetched every traceroute row for the requested sources and reduced to one row per node in JS. Traceroute history is capped per node-pair by TRACEROUTE_HISTORY_LIMIT (default 50, env-tunable higher), so the scan read roughly 50x the rows it needed: a 500-node mesh pulled ~25,000 rows to produce ~500 entries, on every Map Analysis load. Pending rows — one per outstanding request, continuously generated by auto-traceroute — were fetched only to be skipped. Narrow both halves with `route IS NOT NULL` and pick the newest per (sourceId, toNodeNum) via GROUP BY + INNER JOIN. Plain standard SQL rather than a window function, so there is no dialect branch to get wrong. `AnalysisRepository` was only ever constructed with 'sqlite' across all 15 existing cases, so this query had no PostgreSQL or MySQL coverage at all — unacceptable for a change whose entire risk is dialect compatibility. Adds analysis.hopCounts.multiBackend.test.ts, which runs the same seven behaviours on all three backends (21 assertions, none skipped, with the containers up). Behavior change: the newest-answered row is now chosen in SQL, which cannot judge whether `route` is valid JSON. A corrupt newest row therefore yields grey rather than falling back to an older row. Both writers store JSON.stringify(route), so this needs manual DB editing to reach, and grey is the honest answer for unreadable data. Pinned by a test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtJnjbUgYwJfNU6XXACbFf
Follow-up flagged during the #4568 review. `handleRequestTelemetry` throws on any non-TX-disabled HTTP error, and that throw — along with a genuine network failure — lands in a catch that only called logger.error. The spinner cleared and the user was told nothing: the same silent-failure shape #4568 fixed for neighbor info. Unlike that endpoint there is no 403/429 to explain specially. The server answers 400 (validation), 503 (not connected) or 500, and the thrown Error already carries `detail.error` when the server sent one, so surface that message directly rather than adding a status-mapping helper. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtJnjbUgYwJfNU6XXACbFf
b19d77f to
e329a2d
Compare
|
Claude finished @Yeraze's task —— View job PR ReviewTasks
SummaryThis is a clean, well-scoped pair of fixes. Both changes are correct and the reasoning is sound. Notes below are minor.
|
Summary
The two follow-ups the #4570 and #4568 reviews flagged. Independent changes, one per commit — reviewable separately.
1.
perf(analysis)— bound the hop-counts scan (27d018a6)getHopCountsfetched every traceroute row for the requested sources and reduced to one row per node in JS.Traceroute history is capped per node-pair by
TRACEROUTE_HISTORY_LIMIT(default 50, env-tunable higher), so the scan read roughly 50× the rows it needed — a 500-node mesh pulls ~25,000 rows to produce ~500 entries, on every Map Analysis load. Pending rows, generated continuously by auto-traceroute, were fetched only to be discarded.Now narrowed with
route IS NOT NULLon both halves and the newest row per(sourceId, toNodeNum)picked viaGROUP BY+INNER JOIN. Plain standard SQL rather than a window function, so there's no dialect branch to get wrong.The coverage gap this exposed:
AnalysisRepositorywas constructed with'sqlite'in all 15 existing cases — this query had zero PostgreSQL or MySQL coverage. That's not acceptable for a change whose entire risk is dialect compatibility, so this addsanalysis.hopCounts.multiBackend.test.ts: the same seven behaviours on all three backends, following thenewsCache.test.tspattern. 21 assertions, 0 skipped with the containers up — verified all three actually ran rather than skipping vacuously.Behavior change worth a reviewer's eye: picking the newest answered row now happens in SQL, which can't judge whether
routeis valid JSON. A corrupt newest row therefore yields grey instead of falling back to an older row. Both writers storeJSON.stringify(route), so reaching this needs manual DB editing — and grey is the honest answer for data we can't read. Pinned by a test.2.
fix(ui)— surface telemetry request failures (b19d77fc)handleRequestTelemetrythrows on any non-TX-disabled HTTP error, and that throw — plus any genuine network failure — landed in acatchthat only calledlogger.error. Spinner cleared, user told nothing: the identical silent-failure shape #4568 just fixed for neighbor info.Unlike that endpoint there's no 403/429 needing special explanation — the server answers 400 (validation), 503 (not connected) or 500 — and the thrown
Erroralready carriesdetail.errorwhen the server sent one. So it surfaces that directly rather than adding a second status-mapping helper.Test plan
npx tsc --noEmitandnpx tsc -p tsconfig.server.json --noEmit— cleannpm run lint:ci— clean, no baseline growthanalysis.hopCounts.multiBackend.test.ts— 7 behaviours × 3 backendsanalysis.test.tssuite still green, including every Map Analysis hop shading falls back to 0-hop/local on missing traceroute data #4570 assertionNot covered
No browser validation. The hop-shading outcome is asserted at the repository layer, and the telemetry toast is not exercised by a rendering test (
App.tsxisn't renderable in this suite — the same limitation that let the silent failure survive). The telemetry toast in particular is a one-line behaviour I could not unit-test without extracting it, and unlike #4568's status mapping there was no logic worth extracting.Generated by Claude Code
https://claude.ai/code/session_01EtJnjbUgYwJfNU6XXACbFf