"""The series index on plugin_metrics, built in the background after boot (#5490). A read of one metric over a day (a rule's preview, query_metrics, a chart) walks the (source, resource, metric, recorded_at) index; without the value in the index, each sample is then fetched from its own heap page, interleaved with every other series, so a cold day measured 18.3 s against 63 ms warm (#5485). With `value` INCLUDEd the read is an index-only scan over adjacent index pages. The index is built with CREATE INDEX CONCURRENTLY from a task started after the app is serving, never in a migration: a build over the live table takes minutes on slow storage, and a migration would hold the boot with Steward down for all of it (#5220). Ingest and pages keep working while it builds. Once it is valid the index it replaces is dropped. Later boots find it present and only check. """ from __future__ import annotations import time from sqlalchemy import text SERIES_INDEX = "ix_plugin_metrics_series_value" # The plain series index the covering one replaces (migration 0023). REPLACES = "ix_plugin_metrics_module_resource_metric_recorded" _STATE = text(""" SELECT i.indisvalid FROM pg_class c JOIN pg_index i ON i.indexrelid = c.oid WHERE c.relname = :name """) async def ensure_series_index(engine, log) -> str: """Make sure the covering series index exists and is valid, then drop the one it replaces. Returns "present" when it already was, else "built". A build a restart interrupted leaves an INVALID index behind; that one is dropped and built again. Runs outside a transaction, as CONCURRENTLY requires.""" async with engine.connect() as conn: conn = await conn.execution_options(isolation_level="AUTOCOMMIT") valid = (await conn.execute(_STATE, {"name": SERIES_INDEX})).scalar() outcome = "present" if valid is not True: if valid is False: log.warning("metric index: %s was left half-built; dropping it to build again", SERIES_INDEX) await conn.execute(text(f"DROP INDEX CONCURRENTLY IF EXISTS {SERIES_INDEX}")) log.info("metric index: building %s in the background; pages and ingest carry on", SERIES_INDEX) started = time.monotonic() await conn.execute(text( f"CREATE INDEX CONCURRENTLY IF NOT EXISTS {SERIES_INDEX} ON plugin_metrics " "(source_module, resource_name, metric_name, recorded_at) INCLUDE (value)")) log.info("metric index: built %s in %.0fs", SERIES_INDEX, time.monotonic() - started) outcome = "built" if (await conn.execute(_STATE, {"name": REPLACES})).scalar() is not None: await conn.execute(text(f"DROP INDEX CONCURRENTLY IF EXISTS {REPLACES}")) log.info("metric index: dropped %s, which %s replaces", REPLACES, SERIES_INDEX) return outcome