perf(metrics): a day of one metric reads from the index alone, built after boot (#5490)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m15s
CI / unit (push) Successful in 1m25s
CI / publish (push) Successful in 1m3s

#5490 "A day of one metric reads fast even cold". A cold 24 h series read
measured 18.3 s against 63 ms warm (#5485): every sample's row sat on its own
heap page. ix_plugin_metrics_series_value carries `value` (INCLUDE), so the
read is an index-only scan; it replaces the plain series index from 0023.

It is built once, with CREATE INDEX CONCURRENTLY from a task started after the
app is serving, never in a migration, so the boot isn't held while it builds
over the live table (#5220) and ingest and pages carry on. A build cut short
by a restart leaves an INVALID index; the next boot drops and rebuilds it.
Later boots find it present and only check. Postgres keeps it current on each
insert, as it did the index it replaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 13:23:46 -04:00
co-authored by Claude Opus 5.5
parent 57e040f1d0
commit 656e127fd8
5 changed files with 130 additions and 3 deletions
+10
View File
@@ -381,10 +381,20 @@ def create_app(
await _mark_interrupted_runs(app)
asyncio.create_task(_run_scheduler(app))
app._latest_backfill = asyncio.create_task(_backfill_latest(app))
app._series_index = asyncio.create_task(_ensure_series_index(app))
return app
async def _ensure_series_index(app: Quart) -> None:
from .core.metric_index import ensure_series_index
try:
await ensure_series_index(app._db_engine, app.logger)
except Exception:
# Reads still work on the plain index; the next boot tries again.
app.logger.exception("metric index: build failed; series reads use the plain index")
async def _backfill_latest(app: Quart) -> None:
from .core.metric_series import backfill_latest
try: