mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
Two reads behind the metric tiles scanned far more than the rows they return, because PostgreSQL 16 has no index skip scan: a query that wants one row per measurement type but never names a type cannot be served by any index keyed on (user_id, type, ...). The latest-reading read used DISTINCT ON (type), so it produced every live row of the account in (type, measured_at DESC) order before keeping one row per group. On a multi-year account that is a sequential scan plus a sort that spills to disk once it outgrows work_mem. It is now a LATERAL join over the type list, one lookup per type against the live-row partial index from migration 0183, each stopping at the first row. CROSS JOIN LATERAL is an inner join, so a type with no live reading drops out as DISTINCT ON dropped it, and ORDER BY reproduces the same enum ordering. The sparkline read filtered measurement_rollups on user_id and granularity only, skipping the second column of the table's key, so on a single-account host neither the primary key nor the user/type/ granularity/bucket index could drive it and every stored granularity got scanned. It now names the full canonical enum, which filters nothing out and restores the equality the key needs; the existing index serves it. Measured on a fixture matching a real account (454k measurements, 79% tombstoned, 75.5k rollup rows): the first read goes from a seq scan plus a 3.2 MB external-merge sort at 112 ms to a nested loop at 0.7 ms, and the second from a seq scan discarding 72k rows at 6.9 ms to a bitmap index scan at 1.6 ms. Same rows, same order, same types. Both reads move out of the route into src/lib/dashboard/summary-reads.ts so the integration suite can exercise the SQL itself rather than a copy of it, the same seam buildMedsTodayBlock already uses. No schema change: adding an index on (user_id, granularity, bucket_start) would have landed in the same range while leaving the existing index unused and adding a fourth write target to a table the rollup workers upsert into constantly.