perf(stats): cut the dashboard tick's page misses by 43% - #276
Merged
Merged
Conversation
The dashboard pays for its readings every 10 seconds. Two things made that tick expensive: - Totals and blocks came from one statement, and cache hits and latency from a second, but both walked the same 30 days of idx_query_logs_ts_metrics. summary_multi_since answers both from one scan by putting the allowed-only filter inside each CASE instead of the WHERE. - top_upstreams_since reads upstream and response_ms, which no index held, so it looked up the table row for every forwarded query. Migration 14 adds idx_query_logs_ts_upstream, a partial index on (timestamp, upstream, response_ms) WHERE upstream IS NOT NULL. It puts timestamp first so each logger batch is written in one place. On a 370k-row database a tick drops from 8 304 to 4 755 page misses. The index is 5.7 MiB, and a 500-row insert batch writes 56 pages instead of 53. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #276 +/- ##
==========================================
+ Coverage 91.26% 91.27% +0.01%
==========================================
Files 31 31
Lines 11329 11342 +13
==========================================
+ Hits 10339 10352 +13
Misses 990 990 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
While a dashboard is open, the server builds its stats snapshot every 10 seconds. On a 370k-row database each tick read 8 304 pages cold. It now reads 4 755 (−43%).
count_queries_multi_since(totals, blocks) andcache_stats_multi_since(cache hits, latency) each walked the same 30 days ofidx_query_logs_ts_metrics.summary_multi_sinceanswers both in one statement by putting the allowed-only filter inside eachCASEinstead of theWHERE. The two old functions had no other callers and are removed.upstreamandresponse_mswere in no index, so the query looked up the table row for every forwarded query in the last 24h. Migration 14 addsidx_query_logs_ts_upstream ON (timestamp, upstream, response_ms) WHERE upstream IS NOT NULL, and the query names it withINDEXED BY.Why this index shape
Measured with page misses on the same database. Four candidates were compared: timestamp-first vs upstream-first, full vs partial. The "pages written" column counts WAL frames for one committed 500-row batch, averaged over three batches.
(upstream, timestamp, response_ms)(timestamp, upstream, response_ms)(upstream, …) WHERE upstream IS NOT NULL(timestamp, …) WHERE upstream IS NOT NULLPartial, because 56% of rows (blocked or cached) have no upstream. Timestamp-first, because the logger appends at the newest end; an upstream-first index would split every batch across one insertion point per upstream.
One tick, before and after
Each statement runs on a fresh connection, as the read pool's 2 MiB caches effectively leave them. The new statements return exactly the same results as the old ones (diffed).
Tests
stats_page_miss_test:the_dashboard_summary_is_one_metrics_scanandthe_top_upstreams_never_read_the_log_tableboth failed before the change (225 pages vs 111 for one scan; 1470 of 1892 pages) and pass after. The shared seed now forwards half its rows so the second test can measure anything.stats_db_test:summary_multi_sincegives each window's figures and leaves blocked rows out of the latency average. An empty window reports zeroes.db::tests::migration_v14_adds_the_upstream_index: a version-13 database gains the index, and top upstreams still answers correctly through it.E2E was not run locally; the dashboard's rendering is unchanged and CI runs the suite.
🤖 Generated with Claude Code