Skip to content

Convert analytics read models to bounded microbatches - #1185

Merged
Asherlc merged 3 commits into
mainfrom
Asherlc/fix-rhr-population
May 26, 2026
Merged

Asherlc merged 3 commits into
mainfrom
Asherlc/fix-rhr-population

Conversation

@Asherlc

@Asherlc Asherlc commented May 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Adds bounded sleep, activity sensor, and activity location microbatch intermediates, plus compact activity aggregate intermediates.
  • Rewires resting heart rate and activity summary rows to read bounded intermediates and includes them in production DBT_SAFE_MODELS.
  • Adds SQL structure tests and updates analytics docs plus the production incident baseline.

Verification

  • pnpm lint
  • pnpm tsc --noEmit
  • cd packages/server && pnpm tsc --noEmit
  • cd packages/web && pnpm tsc --noEmit
  • bash -lc 'set -a; . ./.env.local; set +a; pnpm test'
  • selected local dbt build for the safe-model set: PASS=10 WARN=0 ERROR=0 SKIP=0

Summary by cubic

Converted analytics read models to bounded microbatches and added compact activity aggregates to make builds reliable and fast. Re-enables scheduled resting heart rate and activity summaries in production.

  • New Features

    • Added microbatch intermediates: sleep_heart_rate_sample, activity_sensor_sample, activity_location_sample (daily batches, short lookbacks).
    • Added aggregate intermediates: activity_sensor_summary_rows, activity_location_summary_rows (location picks the best provider per activity).
    • Expanded DBT_SAFE_MODELS to include the above plus resting_heart_rate_sleep_window and activity_summary_rows.
  • Refactors

    • activity_summary_rows now joins the aggregate intermediates instead of raw streams; preserves elevation defaults and uses max_threads=1 for offline builds.
    • resting_heart_rate_sleep_window now aggregates from sleep_heart_rate_sample and limits threads.
    • Added SQL structure tests to lock safe-model order and dependency boundaries; updated analytics docs, analytics sources (v_activity, v_activity_members), and the production incident baseline.

Written for commit 76d7387. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed Resting Heart Rate chart displaying stale data; metrics now refresh more consistently with underlying health data updates.
  • Documentation

    • Updated analytics architecture documentation to reflect improved data processing strategy.
  • Tests

    • Added validation tests for analytics data model dependencies and build order.

Review Change Stack

Copilot AI review requested due to automatic review settings May 26, 2026 21:24
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Asherlc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@surmado-code-review

surmado-code-review Bot commented May 26, 2026 •

Copy link
Copy Markdown

Automated Checks (advisory, non-blocking)

✅ All checks passed.


Surmado Code Review — Free tier limit reached

You've used all 10 free reviews this month. Deterministic checks (secrets, model strings) still ran above.

Upgrade to the Paid plan for 100 reviews/month + $15 per additional 100: https://app.surmado.com/checkout?plan=pr_review_starter

Or wait until your next monthly window for 10 more free reviews.


Surmado Code Review (v1.2-mt)

@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces a bounded microbatch architecture for analytics read models: three new incremental models bound sensor and location data to sleep/activity windows, two summary aggregation models compute per-activity metrics from those bounded inputs, two downstream models refactor to consume the precomputed summaries instead of raw streams, the safe model set expands to include all new models, and dependency-order validation tests are added.

Changes

Analytics Microbatch Architecture Refactor

Layer / File(s) Summary
Sleep heart rate bounded microbatch intermediary
analytics/models/read_models/sleep_heart_rate_sample.sql
New incremental microbatch model bounds heart-rate samples to active non-nap sleep sessions, excludes overlapping activity windows, and outputs samples with recorded_at event time and refresh versioning.
Activity sensor bounded microbatch intermediary
analytics/models/read_models/activity_sensor_sample.sql
New incremental microbatch model bounds sensor samples (all channels) to user activity windows, with 12-hour fallback for missing ended_at, composite unique key on (activity_id, channel, recorded_at), and refresh versioning.
Activity location bounded microbatch intermediary
analytics/models/read_models/activity_location_sample.sql
New incremental microbatch model bounds location metric-stream samples to activities, ranks providers per activity by sample count, extracts lat/lng from point geometry, and outputs with refresh versioning.
Activity sensor summary aggregation
analytics/models/read_models/activity_sensor_summary_rows.sql
New dirty-key incremental append model aggregates sensor channels (heart rate, power, speed, cadence, altitude deltas) per (activity_id, user_id) from bounded activity_sensor_sample, detects dirty keys from initial lookback/sample refresh/stale activities, and computes elevation gain/loss with max_threads=1.
Activity location summary aggregation
analytics/models/read_models/activity_location_summary_rows.sql
New dirty-key incremental append model computes per-activity location aggregates (great-circle distance between successive GPS points, centroid lat/lng) from bounded activity_location_sample, with dirty-key detection and max_threads=1 for safe scheduling.
Refactored resting heart rate sleep window
analytics/models/read_models/resting_heart_rate_sleep_window.sql
Refactors to compute resting_hr directly from bounded sleep_heart_rate_sample instead of raw samples, uses dirty-key incremental detection on sample refreshed_at, filters to samples with count ≥30, and marks rows deleted when sleep windows or resting_hr are missing.
Refactored activity summary rows
analytics/models/read_models/activity_summary_rows.sql
Refactors to join precomputed activity_sensor_summary_rows and activity_location_summary_rows summaries instead of recomputing from raw/deduped streams, removes query_plan_max_optimizations_to_apply tuning, and updates dirty-key detection to source from summary refreshed_at timestamps.
Configuration, safe models expansion, and validation
entrypoint.sh, analytics/README.md, README.md, docs/production-incident-baseline.md, analytics/models/read_models/read_model_microbatch.sql.test.ts
Expands DBT_SAFE_MODELS to include bounded intermediaries and summary models; updates architecture documentation describing sample→aggregate→join flow and dirty-key incremental strategies; documents production resting-heart-rate staleness incident; adds Vitest suite validating model dependency order, microbatch configuration, and lineage correctness via SQL pattern matching.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Asherlc/dofek#1180: Continues the incremental dbt migration by modifying the same analytics read models (resting_heart_rate_sleep_window, activity_summary_rows) and updating DBT_SAFE_MODELS to use bounded microbatch inputs.

  • Asherlc/dofek#1132: Both PRs refactor analytics.resting_heart_rate_sleep_window; the main PR changes how RHR is computed from new bounded sleep_heart_rate_sample intermediaries, while #1132 changes ClickHouse materialized-view logic to consume the precomputed RHR output.

  • Asherlc/dofek#1183: Both PRs update the shared entrypoint.sh DBT_SAFE_MODELS list to expand the models selected for safe scheduled builds.

Suggested labels

type/feature

Suggested reviewers

  • cubic-dev-ai
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed Title uses imperative mood ('Convert'), stays under 70 characters (53 chars), uses relevant area prefix pattern, and contains no trailing punctuation.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Storybook previews for 055ac4e0 are ready:

This comment updates automatically on each PR push.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR converts ClickHouse analytics read models to bounded microbatch intermediates and chained aggregates so scheduled production dbt builds stay reliable and fast, and re-enables previously excluded models in the production safe-model selection.

Changes:

  • Added bounded microbatch intermediates for sleep heart-rate, activity sensor samples, and activity location samples.
  • Added compact per-activity aggregate intermediates and rewired activity_summary_rows + resting_heart_rate_sleep_window to read from bounded inputs.
  • Expanded DBT_SAFE_MODELS, added SQL-structure tests, and updated analytics/ops documentation.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Updates architecture overview to reflect bounded microbatches and chained aggregates.
entrypoint.sh Expands DBT_SAFE_MODELS to include new intermediates and re-enabled models.
docs/production-incident-baseline.md Documents the “resting heart rate chart tail stale” incident and mitigation plan.
analytics/README.md Updates analytics build docs to describe new bounded microbatch pipeline.
analytics/models/read_models/sleep_heart_rate_sample.sql New microbatch intermediary for sleep↔heart-rate membership.
analytics/models/read_models/resting_heart_rate_sleep_window.sql Reworks RHR aggregation to consume bounded sleep intermediary and limits threads.
analytics/models/read_models/read_model_microbatch.sql.test.ts Adds Vitest guardrails to enforce safe-model ordering and bounded-dependency boundaries.
analytics/models/read_models/activity_sensor_sample.sql New microbatch intermediary for activity↔sensor sample membership.
analytics/models/read_models/activity_location_sample.sql New microbatch intermediary for activity↔location sample membership + best-source selection.
analytics/models/read_models/activity_sensor_summary_rows.sql New per-activity sensor aggregate intermediate.
analytics/models/read_models/activity_location_summary_rows.sql New per-activity location aggregate intermediate.
analytics/models/read_models/activity_summary_rows.sql Rewires summary model to join compact intermediates instead of raw streams.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread analytics/models/read_models/resting_heart_rate_sleep_window.sql Outdated
Comment thread analytics/models/read_models/activity_location_sample.sql
Comment thread analytics/models/read_models/activity_sensor_summary_rows.sql
Comment thread analytics/models/read_models/activity_location_summary_rows.sql

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@analytics/models/read_models/activity_location_sample.sql`:
- Around line 18-24: The CTE activity_members references the hardcoded
analytics.v_activity_members which breaks dbt lineage; replace that direct
schema-qualified reference with a dbt macro call—use {{
ref('v_activity_members') }} if v_activity_members is a model in this project,
or {{ source('your_source_name','v_activity_members') }} if it’s an external
source defined in sources.yml—update the SELECT in the activity_members CTE to
use the appropriate ref/source call so dbt can track the dependency.
- Around line 46-61: In provider_counts, the window ORDER BY uses count()
directly which is less clear—use the computed alias sample_count instead; update
the row_number() OVER (PARTITION BY activity_members.activity_id ORDER BY
sample_count DESC, location_rows.provider_id ASC) expression so it references
sample_count (and keep the GROUP BY and count() aggregation as-is) to make the
intent in provider_counts / row_number clearer and avoid repeating the
aggregate.

In `@analytics/models/read_models/activity_sensor_sample.sql`:
- Around line 18-25: The CTE current_activity references the hardcoded
analytics.v_activity which breaks dbt lineage; replace that raw table reference
with the appropriate dbt call—use {{ ref('v_activity') }} if v_activity is a dbt
model (consistent with the existing {{ ref('deduped_sensor') }} usage) or use {{
source('analytics', 'v_activity') }} and add a sources.yml entry if it’s an
external/source table—then run dbt compile to ensure lineage is picked up.

In `@analytics/models/read_models/activity_summary_rows.sql`:
- Around line 180-188: The sensor_summary and location_summary CTEs currently
select all rows from {{ ref('activity_sensor_summary_rows') }} and {{
ref('activity_location_summary_rows') }} (using FINAL) and thus include
soft-deleted rows; update the sensor_summary and location_summary CTEs to
explicitly filter WHERE is_deleted = 0 so they only expose non-deleted summary
rows (this keeps the LEFT JOIN behavior intact but makes the intent explicit and
avoids joining to soft-deleted summary records).

In `@analytics/models/read_models/sleep_heart_rate_sample.sql`:
- Around line 25-31: Extract the duplicated multiIf nap-classification into a
dbt macro (e.g., macro name is_nap(sleep_type, duration_minutes)) that returns
the boolean expression currently used; replace the inline multiIf in
sleep_heart_rate_sample.sql (the expression labeled AS is_nap) and the identical
block in resting_heart_rate_sleep_window.sql with a call to that macro so both
models call is_nap(sleep_type, duration_minutes) instead of duplicating the
logic; ensure the macro uses the same precedence and coalesce logic (handling
null duration_minutes) so results remain identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e8c4f7d6-9e44-4070-b218-afb103a70d00

📥 Commits

Reviewing files that changed from the base of the PR and between 883b225 and 71b3243.

📒 Files selected for processing (12)
  • README.md
  • analytics/README.md
  • analytics/models/read_models/activity_location_sample.sql
  • analytics/models/read_models/activity_location_summary_rows.sql
  • analytics/models/read_models/activity_sensor_sample.sql
  • analytics/models/read_models/activity_sensor_summary_rows.sql
  • analytics/models/read_models/activity_summary_rows.sql
  • analytics/models/read_models/read_model_microbatch.sql.test.ts
  • analytics/models/read_models/resting_heart_rate_sleep_window.sql
  • analytics/models/read_models/sleep_heart_rate_sample.sql
  • docs/production-incident-baseline.md
  • entrypoint.sh

Comment thread analytics/models/read_models/activity_location_sample.sql
Comment thread analytics/models/read_models/activity_location_sample.sql
Comment thread analytics/models/read_models/activity_sensor_sample.sql
Comment thread analytics/models/read_models/activity_summary_rows.sql
Comment thread analytics/models/read_models/sleep_heart_rate_sample.sql

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 12 files

Confidence score: 2/5

  • High-risk merge at the moment: the issue in analytics/models/read_models/activity_location_sample.sql (8/10, high confidence) can drop output rows when provider_counts is empty, preventing tombstoning of previously materialized location samples after deletions.
  • analytics/models/read_models/activity_summary_rows.sql has a NULL-handling regression risk (missing coalesce on elevation fields) when the LEFT JOIN to sensor_summary has no match, which can change downstream behavior unexpectedly.
  • analytics/models/read_models/activity_location_summary_rows.sql may repeatedly emit tombstones for already-deleted keys unless existing_summary is filtered to non-deleted rows before deriving stale_dirty_keys.
  • Pay close attention to analytics/models/read_models/activity_location_sample.sql, analytics/models/read_models/activity_summary_rows.sql, analytics/models/read_models/activity_location_summary_rows.sql - deletion/tombstone semantics and NULL defaults need correction to avoid persistent data quality issues.

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread analytics/models/read_models/activity_location_sample.sql Outdated
Comment thread analytics/models/read_models/activity_summary_rows.sql Outdated
Comment thread analytics/models/read_models/activity_location_summary_rows.sql

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 8 files (changes from recent commits).

Requires human review: This PR refactors core analytics dbt models, adds new microbatch intermediates, and changes the production build set, which carries moderate risk of data staleness or incorrect aggregates and requires human review of the business logic and dependency changes.

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants