fix(log-explorer): resolve load-more, live-tail, column, and chart-fe…#446
Conversation
…tch bugs Fixes a cluster of LogList explorer + dashboard widget defects, each with a regression test (new vitest harness with a `transport` seam + controllable IntersectionObserver, backed by vitest-canvas-mock): - dedupe inclusive-boundary row across paginated/live pages (no duplicate rows) - empty load-more page stops pagination even if server still reports hasMore - refresh with empty result clears stale rows; drops inline-expanded aggregates - hidden/reordered columns survive load-more & 5s live-stream refetches - fetchGeneration guard prevents a stale load-more from contaminating a new query - per-kind loading-guard reset so concurrent fetches don't clear each other - live-stream listener/interval + worker callbacks cleaned up on disconnect (no orphaned polling loops or reject-after-teardown across HTMX remounts) - buildRecentFetchUrl preserves the `to` upper bound on bounded ranges - cursorFromTimestamp tolerates ns/µs/ms epochs (no year-55000 stall) - per-chart fetch sequencer discards stale /chart_data responses so a late failure can't reapply the error overlay over freshly rendered bars - column resize seeds from the column's real current width - drop unused ansi_up (v6) dependency
f67da17 to
92e597d
Compare
Code ReviewThis PR fixes a solid cluster of real bugs (dedup, generation guard, column preservation, cursor parsing, aggregate cleanup on refresh, cleanup on disconnect) and backs them with a well-structured vitest harness. The fixes are correct and the test seam ( Bugs1. Falsy-zero index in
// line 347 — buildRecentFetchUrl
const timestamp = firstItem?.data?.[this.colIdxMap['timestamp'] || this.colIdxMap['created_at']];
// line 434 — expandTimeRangeUrl
const oldestTimestamp = oldestItem?.data?.[this.colIdxMap['timestamp'] || this.colIdxMap['created_at']];
const timestampCol = this.colIdxMap['timestamp'] !== undefined ? 'timestamp' : 'created_at';Fix: use 2.
Fix: if (this.isNewResetTimer) {
clearTimeout(this.isNewResetTimer);
this.isNewResetTimer = null;
}3.
When Fix: add Cleanup / Succinctness4.
// replace 6x this.formatCount(...) → formatPatternCount(...)
// delete lines 615-6225. Spinner prematurely hidden when stale load-more is discarded mid-refresh
When a stale load-more's transport resolves and hits the 6.
if (isFullFetch) this.logsColumns = meta.cols; // guarded — good
this.colIdxMap = meta.colIdxMap; // unguarded — always overwrites
|
Code ReviewThis PR fixes a solid cluster of real bugs in the log explorer: dedup across paginated/live pages, stale load-more contamination via generation counter, per-kind loading guard reset, live-stream listener cleanup, column persistence across ticks, and the chart sequencer for out-of-order responses. The test harness with a transport seam and controllable IntersectionObserver is the right approach. A few actionable items below: Bug —
|
…telId Unrelated to the log-explorer changes, but on the CI hlint gate for this branch: - L.intercalate → intercalate (Relude); drop intercalate from the L import - BSL.toStrict → toStrict (Relude); drop the now-unused BSL import - singleLeg lambdas → tuple sections (,0)/(0,)
Code ReviewThis PR fixes a solid cluster of real bugs in the LogList explorer and dashboard widgets — the fetchGeneration guard, per-kind loading-guard reset, live-stream listener/interval cleanup, dedupe boundary row, and the chart-fetch sequencer are all genuine and well-targeted fixes. The new vitest harness with a 1.
|
…, interval null, empty-tick expand From PR review: - buildRecentFetchUrl + expandTimeRangeUrl now use cursorFromTimestamp (was raw new Date()/String(epochNs)) so ns/µs epochs don't stall live-tail / "show earlier" - null liveStreamInterval after clearInterval on mode-switch so re-enable restarts polling - don't set expandTimeRange on an empty recent tick (was flashing "Show earlier events") - dedupeById: explicit filter body instead of comma-operator side effect Also surfaced by the new tests: timestamp column extraction used `idx || fallback`, which falls through when timestamp is at column 0 → no cursor. Fixed to `??`. Regression tests added for each in log-list-bugs.test.ts.
Code ReviewThis PR fixes a solid cluster of real bugs in the log explorer (duplicate rows, stale load-more, live-tail leaks, column-persist regression, chart sequencer) and adds a good regression-test harness with a A few issues found: Bug —
|
…ses on Linux My earlier macOS `npm install` pruned the Linux wasm-fallback's transitive @emnapi/core + @emnapi/runtime entries (and bumped them out of sync), so CI's `npm ci` on Linux failed with "Missing @emnapi/core from lock file". Regenerated from master's lockfile inside a node:22 Linux container so all platform optionals are retained; validated `npm ci` green on Linux. Only deltas vs master remain the intended ones: ansi_up removed, vitest-canvas-mock added.
Code ReviewThis PR fixes a solid cluster of real bugs in the log explorer — the Bug —
|
This comment has been minimized.
This comment has been minimized.
… test config Two pre-existing integration failures (red since #437 switched these paths to the Eff Kafka producer; CI sets no Kafka creds): - Replay tests crashed with uncaught `KafkaError "sasl.username and sasl.password must be set"`. getOrInitKafkaProducer throws that via throwIO on producer init, which runErrorNoCallStack does NOT catch. runSharedProducer (the web-handler one-shot wrapper, sole caller = replayPostH) now wraps the action in tryAny so an unconfigured/unreachable Kafka surfaces as Left → the handler returns the "warning" status the test already expects, instead of a 500. - KafkaConsumerSpec decoupledLoop "flushes DLQ before commit" got 0 instead of 5: withTestResources left kafkaDeadLetterTopic unset (def = ""), so attempt-1 poison routed to topic "" while the test asserts on "otlp_deadletter". Set it in the test config (respecting .env when present). Verified locally via live-test-dev: both replay specs + all 3 decoupledLoop specs green.
Review: fix(log-explorer) — load-more, live-tail, column & chart bugsThis is a solid cluster of targeted fixes with excellent test coverage. The Performance — O(n²) dedup in
|
…leanups - addWithFlipDirection re-deduped the whole merged tree on every append (O(pages²·rows) over a long scroll). Maintain a persistent seenIds set and filter only the incoming page via mergeIntoTree; rebuild on full fetch/refresh, extend on child-expand splices. - buildRecentFetchUrl: on a bounded range, once the newest row reaches `to`, from=newest+10ms ≥ to → every 5s tick fetched an empty window and the live banner hung forever. Detect from≥to and stop live-tail with a toast. - dedupeById: widen id constraint to string|null|undefined so the null guard is honest about untyped server data instead of dead code. - handleColumnsChanged: O(n) Array.includes per iteration → Set lookup. - Regression tests for all four.
…tention-free Integration tests build a `monoscope_test_template` once (99 migrations, ~3.6s) and clone it per spec via `CREATE DATABASE ... TEMPLATE`. That clone failed with "source database is being accessed by other users" whenever TimescaleDB's background-worker scheduler was attached to the template — which it does on its ~5s launcher poll, because the template carries the timescaledb extension. Each collision forced the terminate + threadDelay + retry path (up to ~5s/spec) and was a flaky-failure source. Fix: keep the template `ALLOW_CONNECTIONS false` (exactly like Postgres's template0). The scheduler can't attach, so the clone is a deterministic ~0.1s (measured) with no retries. Since the locked template can't be connected to, the migration-fingerprint is stamped as the template's DB comment and read from the master connection instead of a table inside it. Net -72 LOC. Verified: template rebuilds + locks (datistemplate=t, datallowconn=f, checksum comment), child DBs clone + are fully usable, integration specs pass.
Code ReviewThis PR fixes a cluster of real, user-visible bugs in the LogList component and dashboard widgets, with a solid accompanying test suite. The A few things worth addressing: 1. Spurious network request after live-tail auto-stop (
|
Code ReviewThis PR is a solid, well-documented fix of a cluster of interconnected pagination/live-tail/lifecycle bugs. The Confirmed bugs1.
Fix — guard the if (!isNaN(toMs) && Date.parse(from) >= toMs && this.isLiveStreaming)
this.stopLiveStream('Reached the end of the selected time range — live tail paused.');The interval path still stops correctly (it only fires when 2.
Since the library throws a concrete runSharedProducer appCtx act =
try @K.KafkaError (runErrorNoCallStack @K.KafkaError (runSharedKafkaProducer appCtx act)) >>= \case
Left e -> pure $ Left (toText (show e))
Right r -> pure $ first (toText . show) rIf broader coverage is needed (e.g. a broker-down path throws a different exception type), prefer Minor / cleanup3.
const seq = (chartFetchSeq[chartId] = (chartFetchSeq[chartId] ?? 0) + 1);4. Buffer dedup is O(buffer) per 5-second tick The comment calls the buffer a "small bounded list" but it is unbounded — a user who scrolls away with live-tail running for 10 minutes at 50 rows/tick accumulates ~6 000 entries, and each tick re-scans the full array via 5. if (isFullFetch) this.logsColumns = meta.cols;
this.colIdxMap = meta.colIdxMap; // runs on every live tick / load-moreIf the server returns a |
…tch bugs
Fixes a cluster of LogList explorer + dashboard widget defects, each with a regression test (new vitest harness with a
transportseam + controllable IntersectionObserver, backed by vitest-canvas-mock):toupper bound on bounded rangesCloses #
How to test
Checklist