Files
HealthLog/prisma/migrations/0070_v1439_mood_rollups/migration.sql
T
Marc-André Bombeck d84922cdc3 chore(release): v1.4.39 — mood, medication-compliance, and cumulative-sum rollup tiers
Squashed merge of the v1.4.39 wave work from develop. The release
extends the v1.4.38 fast-path posture to two more endpoints that
still walked the source table on every cold mount and folds a
cumulative `sum_value` column into the existing rollup tier so step /
flight / distance / daylight / active-energy sparklines no longer
re-derive their daily totals in Node.

Raw measurements / mood_entries / medication_intake_events tables are
byte-unchanged. Three new tiers (mood_entry_rollups,
medication_compliance_rollups, measurement_rollups.sum_value) are
derived caches that self-heal via boot-time backfill on first reach.

Migrations 0070, 0071, 0072 are additive only. The sum_value column
add is catalog-only DDL on PostgreSQL 11+ — no table rewrite, writes
resume in milliseconds. No environment-variable change. No API
contract break. No iOS contract change.

Expected wins (live perf-verify rides the post-deploy window):
- /api/mood/analytics cold 12.7 s -> ~200 ms
- /api/medications/intake?scope=compliance cold 3.2 s -> ~200 ms
- /api/dashboard/summary cumulative sparkline ~500 ms -> ~300 ms
- /api/analytics live-fallback row cap 347 k -> ~5 k (425 d cap
  preserves the year-ago baseline tile)

Tests 4 524 -> 4 640 (+116). Typecheck clean. Lint clean.

See CHANGELOG.md for the full v1.4.39 entry.
2026-05-21 07:39:10 +02:00

42 lines
1.8 KiB
SQL

-- v1.4.39 W-MOOD — persistent mood rollup table.
--
-- Second cache tier behind the 60-s LRU on /api/mood/analytics. Per-
-- (userId, granularity, bucketStart) stats so the read path skips the
-- unbounded MoodEntry.findMany walk that drove the 12.7 s cold mount on
-- power-user accounts.
--
-- Granularity reuses the existing `measurement_rollup_granularity` enum
-- so the worker / cross-granularity readers share a single type.
--
-- Additive only — no ALTER on existing tables, no DROP. The primary key
-- `(user_id, granularity, bucket_start)` guarantees idempotent upserts
-- under re-run; the IF NOT EXISTS guard makes the migration replay-safe
-- on partially-applied environments (mirrors 0061 / 0067 / 0068 / 0069).
CREATE TABLE IF NOT EXISTS "mood_entry_rollups" (
"user_id" TEXT NOT NULL,
"granularity" "measurement_rollup_granularity" NOT NULL,
"bucket_start" TIMESTAMPTZ(3) NOT NULL,
"count" INTEGER NOT NULL,
"mean" DOUBLE PRECISION NOT NULL,
"min_score" INTEGER NOT NULL,
"max_score" INTEGER NOT NULL,
"sd" DOUBLE PRECISION,
"computed_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "mood_entry_rollups_pkey"
PRIMARY KEY ("user_id", "granularity", "bucket_start")
);
CREATE INDEX IF NOT EXISTS "mood_entry_rollups_user_granularity_bucket_desc_idx"
ON "mood_entry_rollups" ("user_id", "granularity", "bucket_start" DESC);
DO $$ BEGIN
ALTER TABLE "mood_entry_rollups"
ADD CONSTRAINT "mood_entry_rollups_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION
WHEN duplicate_object THEN NULL;
END $$;