github MBombeck/HealthLog v1.4.39
v1.4.39 — Mood, medication-compliance, and cumulative-sum rollup tiers

latest releases: v1.4.39.4, v1.4.39.3, v1.4.39.2...
5 hours ago

Mood, medication-compliance, and cumulative-sum rollup tiers

The v1.4.38 chain settled the measurement-rollup fast-path. v1.4.39 extends the same "raw data stays untouched, a derived second layer serves the read path" posture to two more endpoints that still walked the source table on every cold mount — /api/mood/analytics and /api/medications/intake?scope=compliance — and folds a cumulative sum_value column into the existing measurement_rollups 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. The three new tiers are derived caches that self-heal via boot-time backfill on first reach.

Added

  • mood_entry_rollups — per-(user, granularity, bucket) mood stats. Synchronous DAY-tier write hook on every MoodEntry mutation; WEEK / MONTH / YEAR folded asynchronously through pg-boss. Boot-time backfill queue (mood-rollup-full-backfill) discovers legacy accounts and converges on first worker boot.
  • medication_compliance_rollups — per-(user, medication, day) scheduled / taken / skipped ledger. day is a user-timezone-anchored YYYY-MM-DD string. Hook fires on every intake-event mutation plus the reminder-worker mint path. Boot-time backfill queue (medication-compliance-full-backfill).
  • measurement_rollups.sum_value — nullable cumulative-metric column populated alongside mean / count in every rollup fold. Existing rows backfill on next reach via the extended rollup-full-backfill discovery query.
  • rollup-read-wmy.ts — WEEK / MONTH / YEAR reader helpers with an auto-router that picks the largest granularity resolving the requested window (90 d → DAY, 365 d → MONTH, 1 095 d → YEAR). Coverage-miss fall-through. Ready for the v1.5 multi-year trend card.
  • rollup-read-cumulative.tsreadCumulativeDaySums / readCumulativeDaySumsBatch / resolveBucketSum helpers with legacy-NULL fallback.

Performance

Expected on accounts with several hundred thousand measurements. Numbers anchored on the .planning/round-v1438-perf-analysis.md audit. Live perf-verify rides the post-deploy window.

  • /api/mood/analytics cold mount: 12.7 s → ~200 ms. Was an unbounded MoodEntry.findMany walk + JS aggregation; now a bounded rollup read. Live-fallback retained for coverage misses and pre-aggregates daily means before summarize() so summary.mean / latest / min / max / avg7 / avg30 / slope30 stay byte-identical between the two branches on multi-entry days.
  • /api/medications/intake?scope=compliance cold mount: 3.2 s → ~200 ms. Coverage probe counts rolled days vs days with intake events (partial-coverage cases route to the live fallback, not the rollup). Atomic upsert closes the read-aggregate-then-upsert window under concurrent reminder-worker and Telegram intake.
  • /api/dashboard/summary cumulative sparkline: ~500 ms → ~300 ms. Reads sum_value directly instead of recomputing via mean × count.
  • /api/measurements?groupBy=day cumulative path consumes sum_value directly; eliminates per-type JS aggregation on ACTIVITY_STEPS / FLIGHTS_CLIMBED / WALKING_RUNNING_DISTANCE / TIME_IN_DAYLIGHT / ACTIVE_ENERGY.
  • /api/analytics live-fallback row cap: ~347 k → ~5 k. Trailing 425-day since cap on the fetchMeasurementSeriesChunked per-type loop. Defense-in-depth — the v1.4.38.8 per-type fast-path gate makes this path unreachable in the common case, but a regression that re-triggers it can no longer pull the entire row history. The 425 d window preserves summary.avg30LastYear (year-ago baseline tile).

Fixed

  • Mood + medication-compliance DAY recompute race closed via single atomic INSERT … SELECT … ON CONFLICT DO UPDATE.
  • Mood rollup async worker enqueue no longer awaits the user response; WEEK / MONTH / YEAR enqueue is fire-and-forget.
  • Partial-coverage zero-fill on /api/medications/intake?scope=compliance. Probe now compares rolled-day count to days-with-events.
  • Coverage-miss backfill enqueue scoped to the caller's user. Cluster-wide discovery stays on the worker boot path.
  • dashboard-summary nested ternary in the sparkline branch flattened.

Operator notes

  • Migrations 0070, 0071, 0072 are additive only. 0072 adds a nullable DOUBLE PRECISION column — 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.
  • Boot-time backfills converge automatically on first reach. Operator trigger remains available via POST /api/admin/rollups/recompute.
  • Tests: 4 524 → 4 640 unit (+116). Integration suite green.

Changelog

Don't miss a new HealthLog release

NewReleases is sending notifications on new releases.