Skip to content

Remove sensor priority tables from CDC and clean up legacy validation mirror - #1171

Merged
Asherlc merged 5 commits into
mainfrom
rhetorical-streetcar
May 22, 2026
Merged

Asherlc merged 5 commits into
mainfrom
rhetorical-streetcar

Conversation

@Asherlc

@Asherlc Asherlc commented May 22, 2026 •

Copy link
Copy Markdown
Owner

Root Cause

PR #1168 added sensor_provider_priority and sensor_device_priority to the CDC mirror table mappings. On deploy, reconcileRawAnalyticsMirrors detected these tables were missing from the existing mirror config, dropped the entire dofek_fitness_raw_analytics mirror, and recreated it with do_initial_copy = true. This triggered a full initial snapshot of all 10 tables in that mirror, exhausting server resources and crashing production.

The server crash also corrupted two other replication slots (dofek_metric_stream_cdc, dofek_provider_inventory_raw_analytics), leaving them in lost state.

Impact

  • Production server unresponsive for ~15 minutes (no SSH, no HTTP, no ping)
  • Body composition and other ClickHouse-backed data stale for ~2 hours while CDC caught up
  • Three PeerDB mirrors destroyed; two replication slots permanently lost

Fix

Prevent recurrence

  • Remove sensor_provider_priority and sensor_device_priority from CDC mirror mappings — no ClickHouse read model consumes them, so they should never have been in the mirror. The ClickHouse raw tables (created by migration 0018) remain for future use.
  • Document mirror reconciliation behavior — the deploy CDC setup drops and recreates mirrors when table mappings change, triggering full snapshots. This is now documented as a production risk.

Clean up legacy validation mirror

  • Remove dofek_metric_stream_cdc mirror from PeerDB SQL template — it wrote to peerdb.metric_stream as a validation target during initial PeerDB rollout. Analytics reads exclusively from postgres_fitness.metric_stream via dofek_metric_stream_analytics. The validation mirror was dead weight.
  • Remove dofek_clickhouse peer (database=peerdb) — only used by the removed mirror.
  • Remove CREATE DATABASE IF NOT EXISTS peerdb from CDC setup — no longer needed.
  • Remove clickHouseDatabase from PeerDbSqlTemplateValues interface.

Manual remediation (already done on production)

  • Dropped lost replication slots: peerflow_slot_dofek_metric_stream_cdc, peerflow_slot_dofek_provider_inventory_raw_analytics
  • Dropped broken mirrors: dofek_metric_stream_cdc, dofek_provider_inventory_raw_analytics
  • dofek_metric_stream_analytics reconnected and is draining WAL lag

Docs

  • Updated docs/clickhouse-metric-stream.md: removed peerdb.metric_stream references, added mirror reconciliation warning
  • Updated README.md and deploy/README.md: reflect current PeerDB architecture

Summary by Sourcery

Remove unused sensor priority tables from PeerDB CDC mirror mappings and clean up legacy PeerDB validation infrastructure to prevent destructive mirror reinitialization.

Enhancements:

  • Drop the legacy dofek_metric_stream_cdc mirror and associated dofek_clickhouse peer from the PeerDB SQL template so only the analytics mirror to postgres_fitness remains.
  • Remove the clickHouseDatabase value and database placeholder from PeerDB CDC configuration and template handling, no longer creating or targeting a separate peerdb database in ClickHouse.
  • Exclude sensor_provider_priority and sensor_device_priority tables from analytics and raw analytics mirror mappings while retaining their raw ClickHouse tables via a dedicated migration.
  • Adjust CDC setup tests and expectations to reflect the streamlined PeerDB topology and removal of the peerdb database assumption.

Documentation:

  • Update ClickHouse metric stream docs and READMEs to describe the current PeerDB architecture and to document mirror reconciliation behavior and its operational risks.

Tests:

  • Update CDC and migration tests to match the new mirror set, table mappings, migration count, and removal of the peerdb database creation.

Summary by CodeRabbit

Release Notes

  • Documentation

    • Clarified PeerDB CDC architecture: now explicitly states that PeerDB replicates metric_stream and raw fitness tables into analytics read models for reporting.
    • Added deployment mirror reconciliation guidance to ensure mirror table configurations align with analytics requirements.
  • Chores

    • Refined ClickHouse CDC service configuration by removing unnecessary database creation steps and streamlining the replication setup process.

Review Change Stack

Asherlc added 4 commits May 22, 2026 11:22
The sensor_provider_priority and sensor_device_priority tables were added
to buildPostgresFitnessRawTableStatements() in #1168 but no new ClickHouse
migration was created, so existing environments (where migration 0008 already
ran) never created these tables. The CDC step then fails with:

  Could not find table: sensor_provider_priority

Add migration 0018 to create the missing tables.
sensor_provider_priority and sensor_device_priority are not read by any
ClickHouse read model. Including them in the dofek_fitness_raw_analytics
mirror mapping caused reconcileRawAnalyticsMirrors to drop and recreate
the entire mirror on deploy, triggering a full initial snapshot that
exhausted server resources and crashed production.

Remove the two tables from:
- analyticsSourceTables (Postgres publication)
- rawAnalyticsMirrorTableMappings (reconcile check)
- PeerDB SQL template (mirror definition)

The ClickHouse raw tables (created by migration 0018) remain for future
use but are not CDC-replicated.
The dofek_metric_stream_cdc mirror wrote to peerdb.metric_stream as a
validation target during initial PeerDB rollout. Analytics now reads
exclusively from postgres_fitness.metric_stream via the
dofek_metric_stream_analytics mirror, making the validation mirror
dead weight.

Its replication slot was lost during the 2026-05-22 incident. Rather
than recreating it (which would trigger a full metric_stream snapshot
and crash the server again), remove it entirely:

- Remove dofek_clickhouse peer (database=peerdb) from SQL template
- Remove dofek_metric_stream_cdc mirror from SQL template
- Remove CREATE DATABASE IF NOT EXISTS peerdb from CDC setup
- Remove clickHouseDatabase from PeerDbSqlTemplateValues interface
- Document mirror reconciliation behavior in ClickHouse docs
- Update README and deploy docs to reflect current architecture
Copilot AI review requested due to automatic review settings May 22, 2026 20:40
@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 commented May 22, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Removes unused sensor priority tables from ClickHouse CDC mirror mappings, deletes a legacy PeerDB validation mirror and its dedicated ClickHouse peer/database, adjusts CDC setup/tests accordingly, adds a migration for sensor priority raw tables, and updates documentation to reflect the current PeerDB architecture and mirror reconciliation behavior.

File-Level Changes

Change Details Files
Remove sensor priority tables from CDC analytics and raw mirror mappings to prevent destructive mirror reconciliation.
  • Drop sensor_provider_priority and sensor_device_priority from analyticsSourceTables.
  • Drop sensor_provider_priority and sensor_device_priority from rawAnalyticsMirrorTableMappings for dofek_fitness_raw_analytics.
src/db/clickhouse-cdc.ts
Remove legacy validation mirror and dedicated ClickHouse peer/database from PeerDB SQL template and CDC setup.
  • Delete dofek_clickhouse peer and associated CLICKHOUSE_DATABASE template placeholder.
  • Remove dofek_metric_stream_cdc validation mirror definition and keep only the analytics mirror.
  • Ensure remaining mirrors all target dofek_clickhouse_postgres_fitness and retain existing exclusions/publication settings.
  • Stop issuing CREATE DATABASE IF NOT EXISTS peerdb from setupClickHouseCdc.
src/db/peerdb/metric-stream-cdc.sql
src/db/clickhouse-cdc.ts
Align CDC setup tests with new PeerDB topology and removal of peerdb database creation.
  • Remove clickHouseDatabase from PeerDbSqlTemplateValues usages in tests.
  • Update expectations for number and content of PeerDB queries (peers/mirrors) to match new SQL template without validation mirror or extra peer.
  • Assert that CREATE DATABASE IF NOT EXISTS peerdb is no longer sent to ClickHouse.
  • Tighten expectations around which mirrors include metric_stream exclusions and publication names.
src/db/clickhouse-cdc.test.ts
Add explicit migration for sensor priority raw tables so their ClickHouse schemas exist without being mirrored via CDC.
  • Register migration id 0018_sensor_priority_raw_tables in clickHouseMigrations.
  • Use buildPostgresFitnessRawTableStatements for this migration.
  • Update migration test to expect 18 migrations instead of 17.
src/db/clickhouse-migrations.ts
src/db/clickhouse-migrations.test.ts
Update documentation to match the current PeerDB CDC architecture and to document mirror reconciliation behavior and risks.
  • Remove references to peerdb.metric_stream as a validation target and describe only postgres_fitness.metric_stream as the CDC destination.
  • Adjust architecture diagrams/text to show a single analytics mirror instead of validation + analytics mirrors.
  • Document mirror reconciliation behavior, including dropping/recreating mirrors when mappings change and the associated production risk.
  • Align deploy README and main README descriptions of PeerDB with the new postgres_fitness.*-only replication model.
docs/clickhouse-metric-stream.md
README.md
deploy/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented May 22, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Asherlc has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 49 minutes and 17 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0f5bb0f9-143e-473f-8c72-fda86385388d

📥 Commits

Reviewing files that changed from the base of the PR and between 86e55ee and 43c3cb4.

📒 Files selected for processing (1)
  • src/db/clickhouse-cdc.test.ts
📝 Walkthrough

Walkthrough

This PR restructures the PeerDB CDC architecture by removing the intermediate validation mirror (dofek_metric_stream_cdc), switching the peer definition from ClickHouse-target to Postgres-source, eliminating the clickHouseDatabase template placeholder, and narrowing analytics table scope by removing sensor_provider_priority and sensor_device_priority from the replication pipeline.

Changes

CDC Architecture Restructuring

Layer / File(s) Summary
PeerDB peer and mirror definitions
src/db/peerdb/metric-stream-cdc.sql
Peer definition changed from dofek_clickhouse (CLICKHOUSE-target) to dofek_postgres (POSTGRES-source); dofek_metric_stream_cdc validation mirror removed entirely; sensor_provider_priority and sensor_device_priority removed from dofek_fitness_raw_analytics mirror mapping.
ClickHouse CDC setup and template logic
src/db/clickhouse-cdc.ts
PeerDbSqlTemplateValues interface removes clickHouseDatabase field; analyticsSourceTables excludes sensor priority tables; rawAnalyticsMirrorTableMappings.dofek_fitness_raw_analytics narrows table list; buildRuntimeConfig() stops setting clickHouseDatabase; buildTemplateReplacements() no longer injects CLICKHOUSE_DATABASE; setupClickHouseCdc() removes CREATE DATABASE IF NOT EXISTS peerdb call.
Test fixture and assertion updates
src/db/clickhouse-cdc.test.ts
Removes sensor_provider_priority and sensor_device_priority from rawAnalyticsTables fixture; all setupClickHouseCdc() invocations no longer pass clickHouseDatabase option; assertions updated to expect fewer/different PeerDB queries, no CREATE DATABASE statement, removed expectations for dofek_metric_stream_cdc, and changed mirror/publication structure across all scenarios (template execution, metric stream CDC, environment-driven setup, host mapping).
Architecture documentation
README.md, deploy/README.md, docs/clickhouse-metric-stream.md
Updated architecture narrative to describe direct PeerDB replication into postgres_fitness.* for analytics (removing prior peerdb.metric_stream validation mirror language); removed peerdb.metric_stream from mirror inventory; added "Mirror reconciliation" section explaining deploy-time validation against rawAnalyticsMirrorTableMappings with destructive mirror reset on mismatch.
ClickHouse migration
src/db/clickhouse-migrations.ts, src/db/clickhouse-migrations.test.ts
Migration 0018_sensor_priority_raw_tables added with statements from buildPostgresFitnessRawTableStatements(); test updated to expect 18 pending migrations (was 17).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Asherlc/dofek#1087: Modifies ClickHouse CDC setup in src/db/clickhouse-cdc.ts to stabilize runtime config and template value handling, directly related to this PR's CDC configuration changes.
  • Asherlc/dofek#1089: Modifies PeerDB metric-stream CDC mirror wiring in src/db/peerdb/metric-stream-cdc.sql around dual-target mirroring and peer configuration.
  • Asherlc/dofek#1080: Modifies PeerDB metric-stream CDC setup and ClickHouse template handling in src/db/clickhouse-cdc.ts and SQL template configuration.

Suggested labels

area/db, type/bug, breaking-change

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning Title exceeds 70-character limit (76 chars) and lacks area prefix as specified in requirements. Shorten title to ≤70 chars and add area prefix (e.g., '[db]'). Example: '[db] Remove sensor priority tables from CDC mirror'
✅ Passed checks (1 passed)
Check name Status Explanation
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 22, 2026 •

Copy link
Copy Markdown
Contributor

Storybook previews for e06ae318 are ready:

This comment updates automatically on each PR push.

@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.

Hey - I've found 1 issue, and left some high level feedback:

  • The tests around peerDbQueries are quite index-sensitive (e.g., asserting specific items at positions 1–6); consider refactoring these to search by query content or labels to make them more robust to future peer/mirror reordering.
  • Given that analyticsSourceTables and rawAnalyticsMirrorTableMappings.dofek_fitness_raw_analytics now both hardcode the same table list minus provider inventory, it may be worth centralizing this list or deriving one from the other to avoid future drift when adding/removing tables.
  • Since mirror reconciliation can be destructive under resource constraints, you might consider adding a runtime guard (e.g., an explicit opt-in flag or dry-run mode) in reconcileRawAnalyticsMirrors instead of relying solely on documentation to prevent accidental mirror-wide rebuilds.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The tests around `peerDbQueries` are quite index-sensitive (e.g., asserting specific items at positions 1–6); consider refactoring these to search by query content or labels to make them more robust to future peer/mirror reordering.
- Given that `analyticsSourceTables` and `rawAnalyticsMirrorTableMappings.dofek_fitness_raw_analytics` now both hardcode the same table list minus provider inventory, it may be worth centralizing this list or deriving one from the other to avoid future drift when adding/removing tables.
- Since mirror reconciliation can be destructive under resource constraints, you might consider adding a runtime guard (e.g., an explicit opt-in flag or dry-run mode) in `reconcileRawAnalyticsMirrors` instead of relying solely on documentation to prevent accidental mirror-wide rebuilds.

## Individual Comments

### Comment 1
<location path="src/db/clickhouse-cdc.ts" line_range="467" />
<code_context>
 export async function setupClickHouseCdc(options: SetupClickHouseCdcOptions): Promise<void> {
-  await options.clickHouseClient.command({ query: "CREATE DATABASE IF NOT EXISTS peerdb" });
   await ensureAnalyticsPeerDbColumns(options.clickHouseClient);
   await ensureAnalyticsPublication(options.sourcePostgresClient);
   await ensureMetricStreamNoImuPublication(options.sourcePostgresClient);
</code_context>
<issue_to_address>
**suggestion:** Re-evaluate whether `ensureAnalyticsPublication` is still needed now that the unfiltered metric_stream CDC mirror has been removed.

The SQL template now only uses the `peerdb_metric_stream_no_imu` publication for the analytics mirror; the old `peerdb_metric_stream_publication` used by `dofek_metric_stream_cdc` is no longer referenced. If that publication is deprecated, consider removing `ensureAnalyticsPublication` and any related publication-management logic to avoid unnecessary work on the source Postgres and ambiguity about which publication is active.

Suggested implementation:

```typescript
export async function setupClickHouseCdc(options: SetupClickHouseCdcOptions): Promise<void> {
  await ensureAnalyticsPeerDbColumns(options.clickHouseClient);
  await ensureMetricStreamNoImuPublication(options.sourcePostgresClient);

```

1. In `src/db/clickhouse-cdc.ts`, remove the import of `ensureAnalyticsPublication` (e.g. from a line like `import { ensureAnalyticsPeerDbColumns, ensureAnalyticsPublication, ensureMetricStreamNoImuPublication } from '...'`), keeping the other imports intact.
2. Locate the implementation of `ensureAnalyticsPublication` (likely in a helper module that manages Postgres publications) and:
   - Remove the function entirely if `peerdb_metric_stream_publication` is no longer used anywhere.
   - Delete any SQL or helper code that creates/updates/drops `peerdb_metric_stream_publication`.
3. Search the codebase for remaining references to `ensureAnalyticsPublication` and `peerdb_metric_stream_publication`:
   - If `dofek_metric_stream_cdc` or any other code still depends on that publication, either update it to use `peerdb_metric_stream_no_imu` or keep the publication and document its purpose.
   - If there are no remaining functional dependencies, remove those references to avoid dead code and ambiguity about which publication is active.
4. If there is configuration, migrations, or docs mentioning the old analytics publication, update or remove them to reflect that only `peerdb_metric_stream_no_imu` is now used for the analytics mirror.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/db/clickhouse-cdc.ts

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

The mock raw_analytics_mirror_config listed all current tables after
sensor priority removal, so no mismatch was detected and the DROP
never fired. Remove user_profile from the mock to create the expected
mismatch.
@github-actions

Copy link
Copy Markdown
Contributor

Review app is ready:

This environment runs on a dedicated Hetzner server for PR #1171 and updates on each push.

@Asherlc
Asherlc merged commit 64021a9 into main May 22, 2026
63 checks passed
@Asherlc
Asherlc deleted the rhetorical-streetcar branch May 22, 2026 21:03
Asherlc added a commit that referenced this pull request May 22, 2026
…ror (#1172)

* Truncate ClickHouse destination tables when reconciliation drops a CDC mirror

When reconcileRawAnalyticsMirrors detects a table mapping mismatch and
drops a mirror, the ClickHouse destination tables retain data from the
previous initial copy. PeerDB then rejects mirror recreation with
do_initial_copy=true because the destination tables are non-empty:

  table device_priority exists and is not empty

Truncate the destination tables after dropping the mirror so the
subsequent CREATE MIRROR IF NOT EXISTS succeeds.

Fixes staging deploy failure in #1171.

* Use Promise.all for parallel ClickHouse table truncation

Address review feedback: tables are independent and can be truncated
concurrently rather than sequentially.
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