Skip to content

Fix deploy CDC recovery and ClickHouse staleness - #1173

Merged
Asherlc merged 8 commits into
mainfrom
Asherlc/fix-deploy-failure-v4
May 23, 2026
Merged

Asherlc merged 8 commits into
mainfrom
Asherlc/fix-deploy-failure-v4

Conversation

@Asherlc

@Asherlc Asherlc commented May 23, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Fixes ClickHouse CDC setup so absent raw analytics mirrors do not force destructive truncation or a full initial copy when destination data already exists.
  • Makes production deploy more robust by increasing Netdata memory, checking data services after stack deploy, and running migrations in the foreground.
  • Documents the production incident and adds a runbook for stale ClickHouse body measurements, including bounded gap backfill steps.

Verification

  • pnpm lint
  • pnpm tsc --noEmit
  • cd packages/server && pnpm tsc --noEmit
  • cd packages/web && pnpm tsc --noEmit
  • Full pnpm test was started but skipped at user request.

Summary by Sourcery

Improve ClickHouse CDC raw analytics recovery behavior and harden the web deployment workflow against data-service and migration failures, while documenting recent production incidents and recovery steps.

Bug Fixes:

  • Prevent absent raw analytics CDC mirrors from forcing destructive truncation or full initial copies when ClickHouse destination tables already contain data by basing initial-copy decisions on existing row counts.
  • Ensure raw analytics mirror reconciliation handles non-empty destinations safely and drives appropriate PeerDB mirror configuration without re-snapshotting production data.

Enhancements:

  • Refine ClickHouse CDC setup to compute per-mirror initial-copy flags, wire them through the PeerDB SQL template, and use ClickHouse system tables to detect existing destination rows.
  • Simplify the deploy migration step to run the migration container in the foreground with a bounded timeout, avoiding brittle detached-container inspection logic.
  • Add post-stack readiness checks for Postgres writability and ClickHouse reachability before running PeerDB, Temporal, and CDC configuration steps.
  • Increase the Netdata service memory limit to avoid OOM-style exits during stack deploy convergence.

Documentation:

  • Extend the production incident baseline with detailed timelines and analyses for recent staging and production deploy failures and CDC incidents.
  • Add a dedicated runbook for diagnosing and repairing stale ClickHouse body measurements, and link it from the deployment README.

Tests:

  • Expand ClickHouse CDC tests with a helper ClickHouse client stub and a regression case covering absent raw analytics mirrors with non-empty ClickHouse destinations.

Summary by CodeRabbit

Release Notes

  • Chores

    • Enhanced deployment process with improved migration timeout handling and readiness checks for database connectivity.
    • Increased memory allocation for monitoring service.
  • Documentation

    • Added troubleshooting guide for diagnosing and resolving data synchronization staleness issues.
    • Documented recent production incidents and recovery procedures.
  • Bug Fixes

    • Improved data replication to correctly handle scenarios where destination tables already contain data.

Review Change Stack

Copilot AI review requested due to automatic review settings May 23, 2026 00:37
@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 23, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adjusts ClickHouse CDC setup to avoid destructive resnapshots when raw analytics mirrors are absent but destination data exists, hardens the deploy workflow around migrations and post-stack data-service readiness, increases Netdata memory limits, and documents/operationalizes recent production incidents including a ClickHouse body-measurement staleness runbook.

Sequence diagram for raw analytics CDC mirror reconciliation based on ClickHouse row counts

sequenceDiagram
  participant Setup as setupClickHouseCdc
  participant PeerDB as PeerDbClient
  participant ClickHouse as ClickHouseCommandClient

  Setup->>PeerDB: ensureAnalyticsPublication()
  Setup->>PeerDB: ensureMetricStreamNoImuPublication()
  Setup->>PeerDB: reconcileMetricStreamAnalyticsMirror()
  Setup->>PeerDB: reconcileRawAnalyticsMirrors()
  activate PeerDB
  PeerDB-->>Setup: flows rows (raw_analytics_mirror_config)
  deactivate PeerDB

  loop each rawAnalyticsMirrorNames
    alt mirror absent in flows
      Setup->>ClickHouse: clickHouseDestinationTablesHaveRows(tableNames)
      activate ClickHouse
      ClickHouse->>ClickHouse: query({ query: SELECT coalesce(sum(rows)) AS row_count FROM system.parts ... })
      ClickHouse-->>Setup: row_count
      deactivate ClickHouse
      alt row_count > 0
        Setup->>Setup: rawAnalyticsInitialCopyValues[mirror] = false
      else row_count == 0
        Setup->>Setup: rawAnalyticsInitialCopyValues[mirror] = true
      end
    else mirror present in flows
      Setup->>Setup: compare mirror config to rawAnalyticsMirrorTableMappings
      alt config differs
        Setup->>PeerDB: query(DROP MIRROR ...)
        Setup->>ClickHouse: truncateClickHouseDestinationTables()
      end
    end
  end

  Setup->>Setup: renderPeerDbSqlTemplate(templateSql, templateValues, rawAnalyticsInitialCopyValues)
  Setup-->>Setup: renderedSql
  loop splitPeerDbSqlStatements(renderedSql)
    Setup->>PeerDB: query(statement)
  end
Loading

File-Level Changes

Change Details Files
Make migrations run as a single bounded foreground docker run instead of a detached container with an inspect/log polling loop.
  • Replace detached migration container plus manual docker inspect loop with a timeout-wrapped foreground docker run that directly returns the migration exit status.
  • Simplify cleanup logic to a single trap-based rm -f and remove custom log streaming/output file handling.
  • Preserve the four-hour upper bound and treat timeout as a hard migration failure with a clear error message.
.github/workflows/deploy-web-stack.yml
Add post-stack readiness checks for Postgres and ClickHouse before PeerDB/Temporal and CDC steps.
  • Add a looped health check that ensures Postgres is writable (pg_is_in_recovery() = false) after docker stack deploy, with a 180s bound.
  • Add a looped health check that ensures ClickHouse responds to /ping on the overlay network after stack deploy, with a 180s bound.
  • Wire these checks into the deploy workflow before existing PeerDB/Temporal readiness and CDC configuration steps.
.github/workflows/deploy-web-stack.yml
Refine raw analytics CDC reconciliation to inspect ClickHouse destination rows and conditionally disable initial copy for absent mirrors, plumbed through the PeerDB SQL template.
  • Introduce typed rawAnalyticsMirrorNames and a defaultRawAnalyticsInitialCopyValues map to track per-mirror do_initial_copy decisions.
  • Add clickHouseDestinationTablesHaveRows which queries system.parts for active rows in mapped raw analytics tables, with integer coercion and error handling.
  • Change reconcileRawAnalyticsMirrors to compute RawAnalyticsInitialCopyValues: leave initial copy true for existing mirrors and set it false for absent mirrors with existing ClickHouse rows, still truncating destinations only for stale-config mirrors.
  • Extend buildTemplateReplacements/renderPeerDbSqlTemplate to accept RawAnalyticsInitialCopyValues and expose FITNESS_RAW_ANALYTICS_DO_INITIAL_COPY and PROVIDER_INVENTORY_RAW_ANALYTICS_DO_INITIAL_COPY placeholders.
  • Update metric-stream-cdc.sql to use the new template placeholders instead of hardcoded do_initial_copy = true.
  • Return RawAnalyticsInitialCopyValues from reconcileRawAnalyticsMirrors and pass it into renderPeerDbSqlTemplate in setupClickHouseCdc so PeerDB mirrors are created with the correct initial-copy behavior.
src/db/clickhouse-cdc.ts
src/db/peerdb/metric-stream-cdc.sql
Extend ClickHouse CDC tests to cover the new raw-analytics behavior and shared ClickHouse client usage.
  • Add a ClickHouseCommandClient-based createTestClickHouseClient helper that records commands and fakes a JSON row_count response for system.parts queries.
  • Mock clickHouseClient.query in the Vitest hoisted mocks and default it to a zero-row JSON response in beforeEach.
  • Refactor existing tests to use createTestClickHouseClient, reducing inline mock duplication for command-only clients.
  • Add a regression test that verifies absent raw analytics mirrors with existing destination rows are recreated without initial copy, do not trigger destination truncation, and still create other raw mirrors with do_initial_copy = true.
src/db/clickhouse-cdc.test.ts
Increase Netdata container memory to avoid OOM exits during stack deploy.
  • Raise Netdata service memory limit from 400M to 768M in the Swarm stack definition to align with observed usage.
  • Leave other Netdata deployment parameters unchanged.
deploy/stack.yml
Document recent deploy/CDCR incidents and add an operational runbook for ClickHouse body-measurement staleness, referenced from deploy docs.
  • Append multiple detailed 2026-05-22/23 incident writeups to the production-incident-baseline, covering staging raw mirror recreation failure, Netdata OOM deploy timeout, Postgres DNS resolution race, migration inspection timeout, Temporal readiness failure during raw mirror snapshot, and production recovery after ClickHouse saturation.
  • Create clickhouse-body-measurement-staleness-runbook.md with a stepwise procedure to diagnose stale body measurements, mitigate host-saturating ClickHouse refreshes, identify CDC gaps, backfill a bounded body-measurement window, refresh v_body_measurement, and verify health.
  • Update deploy/README.md to link the new ClickHouse staleness runbook from the deployment runbooks section.
docs/production-incident-baseline.md
docs/clickhouse-body-measurement-staleness-runbook.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 23, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@Asherlc, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 41 minutes and 34 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, 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 trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ec37f68b-4419-49d1-b313-0c5f962fec98

📥 Commits

Reviewing files that changed from the base of the PR and between c0f0bbd and 4485611.

📒 Files selected for processing (2)
  • .github/workflows/deploy-web-stack.yml
  • src/db/clickhouse-cdc.ts
📝 Walkthrough

Walkthrough

Deploy workflow now runs migrations in foreground with explicit cleanup and 4-hour timeout, adds post-deploy readiness checks for Postgres/ClickHouse, and increases Netdata memory. ClickHouse CDC conditionally skips initial bulk copies when destination tables contain existing rows, determined by querying system.parts and templated into SQL. Tests and documentation capture the logic and incident response.

Changes

Production incident response and CDC improvements

Layer / File(s) Summary
Deploy workflow hardening
.github/workflows/deploy-web-stack.yml, deploy/README.md, deploy/stack.yml
Migration container executes in foreground (not detached) with timeout and trap-based cleanup; post-deploy readiness checks poll Postgres pg_is_in_recovery() and ClickHouse ping (36 attempts, 15s timeout each); Netdata service memory limit raised from 400M to 768M to prevent OOM exits.
CDC conditional initial-copy decisions
src/db/clickhouse-cdc.ts
Added types and constants for raw analytics mirror initial-copy decisions; reconcileRawAnalyticsMirrors now queries ClickHouse system.parts to detect existing destination rows and returns typed initial-copy values (true/false per mirror) instead of void; buildTemplateReplacements and renderPeerDbSqlTemplate accept and inject these decisions into SQL templates.
CDC SQL template parameterization
src/db/peerdb/metric-stream-cdc.sql
Fitness and provider-inventory raw analytics mirrors switch do_initial_copy from hardcoded true to template variables {{FITNESS_RAW_ANALYTICS_DO_INITIAL_COPY}} and {{PROVIDER_INVENTORY_RAW_ANALYTICS_DO_INITIAL_COPY}}.
CDC test infrastructure and coverage
src/db/clickhouse-cdc.test.ts
Added createTestClickHouseClient(commands, destinationRowCount) mock helper; refactored existing test stubs to use the helper; introduced new test verifying fitness mirror receives do_initial_copy = false and provider-inventory receives true when destination rows exist, confirming no TRUNCATE commands are issued.
Incident records and operational runbook
docs/clickhouse-body-measurement-staleness-runbook.md, docs/production-incident-baseline.md
New runbook documents symptom verification, CDC gap detection, refresh saturation mitigation, and backfill procedures for stale ClickHouse body measurements; production-incident-baseline.md records six incidents (staging CDC failure, deploy Netdata timeout, post-deploy DNS transience, migration container false-fail, Temporal timeout, recovery staleness) with root causes and implemented fixes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • Asherlc/dofek#1172: Both PRs modify raw analytics mirror reconciliation in src/db/clickhouse-cdc.ts—one adds conditional do_initial_copy based on ClickHouse row existence, the other truncates destination tables when a mirror is dropped.
  • Asherlc/dofek#1102: Both PRs change .github/workflows/deploy-web-stack.yml migration-container execution and log handling (main switches to foreground with timeout/trap cleanup).
  • Asherlc/dofek#1113: Both PRs extend the deploy workflow's migration timeout to ~4 hours and adjust migration execution strategy.

Suggested labels

area/infra, area/db, type/bug

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title is imperative, concise (48 characters), uses [deploy] prefix, has no trailing punctuation, and accurately summarizes the main changes in the changeset.

✏️ 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 23, 2026 •

Copy link
Copy Markdown
Contributor

Storybook previews for 2c0fefee 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 reviewed your changes and they look great!


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.

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.

@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: 2

🤖 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 @.github/workflows/deploy-web-stack.yml:
- Around line 477-516: The post-deploy readiness loops (the "Wait for Postgres
writable after stack deploy" and "Wait for ClickHouse after stack deploy" blocks
that iterate with for attempt in $(seq 1 36), use timeout 15s and sleep 5s and
set recovery_output / clickhouse_output) have a real worst-case runtime ≈720s
but their error messages claim 180s; either make the loop actually enforce a
180s deadline (replace the fixed seq loop with a deadline-based loop using a
start timestamp/SECONDS and break when elapsed > 180) or update the failure
messages ("::error::Postgres did not become writable within 180s after stack
deploy" and "::error::ClickHouse did not become reachable within 180s after
stack deploy") to the correct budget (e.g., 720s) so the reported timeout
matches the loop behavior. Ensure changes touch the attempt loop logic and the
exact error strings so behavior and logs are consistent.

In `@src/db/clickhouse-cdc.ts`:
- Around line 483-506: In clickHouseDestinationTablesHaveRows, the ClickHouse
JSON is parsed ad-hoc; define a Zod schema matching ClickHouseRowCount (e.g., {
row_count: z.union([z.string(), z.number(), z.null()]) }) and use it to parse
the result of await result.json() (validate the array shape) before calling
readInteger; update the code paths that currently destructure const [row] =
await result.json() to first z.parse the value and then pass parsedRow.row_count
into readInteger so the boundary is validated (keep references to
ClickHouseCommandClient/query, ClickHouseRowCount, readInteger,
peerDbStringLiteral).
🪄 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: c8736024-a2c7-4a4b-a4d9-e5c673d6fec9

📥 Commits

Reviewing files that changed from the base of the PR and between ea856c9 and c0f0bbd.

📒 Files selected for processing (8)
  • .github/workflows/deploy-web-stack.yml
  • deploy/README.md
  • deploy/stack.yml
  • docs/clickhouse-body-measurement-staleness-runbook.md
  • docs/production-incident-baseline.md
  • src/db/clickhouse-cdc.test.ts
  • src/db/clickhouse-cdc.ts
  • src/db/peerdb/metric-stream-cdc.sql

Comment thread .github/workflows/deploy-web-stack.yml
Comment thread src/db/clickhouse-cdc.ts
@github-actions

Copy link
Copy Markdown
Contributor

Review app is ready:

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

@Asherlc
Asherlc enabled auto-merge (squash) May 23, 2026 00:58
@Asherlc
Asherlc merged commit 83801b5 into main May 23, 2026
66 of 67 checks passed
@Asherlc
Asherlc deleted the Asherlc/fix-deploy-failure-v4 branch May 23, 2026 01:04
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