mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
Rollup rows were grouped by (type, day) only, so two sources reporting the same standard vital for one day (e.g. WHOOP + Apple Watch resting heart rate) were AVG-blended into a single row instead of resolving to the source-priority ladder's canonical reading. Re-grain the rollup table to (type, day, source): the writer now groups by source and delete-then-inserts each affected partition (so a source that stops reporting a bucket cannot strand a stale row), and the read path will collapse overlapping sources to the canonical reading in a follow-up commit. Cumulative metrics (steps, energy) keep summing per source. Migration 0115 purges the legacy source-blind aggregates; the boot backfill + read-time self-heal re-mint per-source rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
33 lines
1.9 KiB
SQL
33 lines
1.9 KiB
SQL
-- v1.11.1 — source-aware measurement rollups.
|
|
--
|
|
-- `measurement_rollups` is a derived cache; the authoritative readings live in
|
|
-- `measurements`. Existing rollup rows are SOURCE-BLIND aggregates: they group
|
|
-- by (type, day) only, so two sources reporting the same standard vital for one
|
|
-- day (e.g. WHOOP + Apple Watch resting heart rate) were AVG-blended into one
|
|
-- row. Re-grain the table to (type, day, source) so the read path can collapse
|
|
-- overlapping sources to the ladder-canonical reading while cumulative metrics
|
|
-- (steps, energy) still sum per source.
|
|
--
|
|
-- The existing rows cannot be re-grained in place (their source is unknown —
|
|
-- they already blend N sources), so purge them. This is non-destructive of user
|
|
-- data: the boot-time rollup backfill (`rollup-full-backfill`) and the
|
|
-- read-time self-heal (`ensureUserRollupsFresh` + the /api/measurements
|
|
-- coverage fallback) re-mint per-source rows on next worker boot / first read.
|
|
-- Bounded, idempotent, multi-tenant-safe; no operator action required.
|
|
DELETE FROM "measurement_rollups";
|
|
|
|
-- Additive column. DEFAULT only to satisfy NOT NULL during the (now empty)
|
|
-- table rewrite, then dropped so every writer must supply an explicit source.
|
|
ALTER TABLE "measurement_rollups"
|
|
ADD COLUMN "source" "measurement_source" NOT NULL DEFAULT 'MANUAL';
|
|
ALTER TABLE "measurement_rollups" ALTER COLUMN "source" DROP DEFAULT;
|
|
|
|
-- Re-grain the primary key to include source. The hot descending index
|
|
-- (user_id, type, granularity, bucket_start DESC) is unchanged: readers fetch
|
|
-- every source for a (user, type) range and collapse in application code, so
|
|
-- they never filter by source in SQL.
|
|
ALTER TABLE "measurement_rollups" DROP CONSTRAINT "measurement_rollups_pkey";
|
|
ALTER TABLE "measurement_rollups"
|
|
ADD CONSTRAINT "measurement_rollups_pkey"
|
|
PRIMARY KEY ("user_id", "type", "granularity", "bucket_start", "source");
|