Skip to content

[server] Fix ClickHouse provider deletion acknowledgements - #1676

Merged
Asherlc merged 4 commits into
mainfrom
Asherlc/fix-garmin-ch-sync
Jul 18, 2026
Merged

Asherlc merged 4 commits into
mainfrom
Asherlc/fix-garmin-ch-sync

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix ClickHouse provider tombstones by selecting the latest scoped row version before inserting logical deletes.
  • Add versioned deletion-event receipts so analytics jobs await durable sink acknowledgement instead of scanning the full raw table.
  • Add the acknowledgement migration, query cancellation and time limits, regression coverage, and production incident/runbook documentation.

Validation

  • pnpm lint
  • pnpm tsc --noEmit (root, server, and web)
  • Focused unit tests: 58 passed
  • ClickHouse integration tests: 4 passed
  • Targeted ClickHouse Stryker mutation score: 92.45%

Summary by CodeRabbit

  • Bug Fixes

    • Improved provider deletion reliability by waiting for metric-stream deletion acknowledgements before refreshing analytics.
    • Prevented deletion/refresh from stalling by removing ineffective tombstone behavior and avoiding unbounded ClickHouse rescans.
    • Tightened ClickHouse HTTP behavior by canceling disconnected read-only queries and enforcing a bounded per-query execution limit.
  • Improvements

    • Upgraded metric-stream deletion to versioned (V2) events with an eventId, including acknowledgement recording and event-scoped polling.
  • Documentation

    • Updated metric-stream and production incident documentation to reflect the acknowledgement-based deletion workflow and new ClickHouse safeguards.
  • Tests

    • Expanded coverage for acknowledgement creation, polling behavior, timeouts, and archived replay cases.

Provider deletion events could be consumed without writing tombstones, leaving analytics stale and retry scans saturating ClickHouse.

Record applied delete event IDs after scoped tombstone inserts so refresh jobs can await durable completion.
Copilot AI review requested due to automatic review settings July 18, 2026 02:47
@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 →

@cursor

cursor Bot commented Jul 18, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@codereviewbot-ai

codereviewbot-ai Bot commented Jul 18, 2026

Copy link
Copy Markdown

The PR successfully implements a more robust acknowledgement mechanism for metric stream deletions in ClickHouse by using a dedicated acknowledgement table instead of polling for row counts. This avoids the overhead and potential unreliability of FINAL queries.

The refactoring of markMetricStreamScopeDeletedInClickHouse to use argMax and GROUP BY id is a good performance improvement over the previous FINAL approach.

I've noted one critical issue regarding backward compatibility for in-flight jobs in the queue and a performance nit regarding ClickHouse index usage.


🤖 Reviewed by codereviewbot.ai - Catch bugs before your team does.

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

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

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 was unable to review this pull request because the user who requested the review has reached their quota limit.

@sourcery-ai

sourcery-ai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Implements durable, versioned ClickHouse tombstoning for metric-stream deletions and introduces an acknowledgement path and ClickHouse query guardrails so provider-delete analytics jobs wait on a small receipt table instead of re-scanning the large metric_stream table, along with tests, migrations, and documentation updates.

Sequence diagram for provider delete with ClickHouse deletion acknowledgement

sequenceDiagram
  actor User
  participant ProviderDetailRouter as providerDetailRouter
  participant MetricStreamWriter as replaceMetricStreamBatch
  participant Redpanda as MetricStreamEventPublisher
  participant ClickHouseSink as applyMetricStreamEventsToClickHouse
  participant MetricStream as ingest_metric_stream
  participant DeleteAck as ingest_metric_stream_delete_acknowledgement
  participant JobQueue as enqueueProviderDeleteAnalyticsRefresh
  participant AnalyticsJob as processActivityDeleteAnalyticsJob
  participant ReadModel as waitForMetricStreamDeleteAcknowledgement

  User->>ProviderDetailRouter: request provider-data-delete
  ProviderDetailRouter->>MetricStreamWriter: replaceMetricStreamBatch(scope, rows, sourceType)
  MetricStreamWriter->>Redpanda: replaceRows(scope, rows)
  Redpanda-->>MetricStreamWriter: MetricStreamReplacementPublishResult(deleted.eventId, rows)
  MetricStreamWriter-->>ProviderDetailRouter: MetricStreamReplacementReceipt(deletedEventId)
  ProviderDetailRouter->>JobQueue: enqueueProviderDeleteAnalyticsRefresh(userId, providerId, deletedEventId)

  Redpanda->>ClickHouseSink: MetricStreamRedpandaEvent(metric_stream_deleted, eventId)
  ClickHouseSink->>ClickHouseSink: applyMetricStreamEventsToClickHouse(client, events)
  ClickHouseSink->>MetricStream: markMetricStreamScopeDeletedInClickHouse(client, scope)
  ClickHouseSink->>DeleteAck: acknowledgeMetricStreamDeletion(client, eventId)

  JobQueue->>AnalyticsJob: processActivityDeleteAnalyticsJob(job)
  AnalyticsJob->>ReadModel: waitForMetricStreamDeleteAcknowledgement(client, metricStreamDeleteEventId)
  loop until acknowledgement_count > 0
    ReadModel->>DeleteAck: SELECT count() AS acknowledgement_count
  end
  ReadModel-->>AnalyticsJob: acknowledgement observed
  AnalyticsJob->>AnalyticsJob: rebuild ClickHouse models and invalidate cache
Loading

Entity-relationship diagram for metric_stream deletion acknowledgement table

erDiagram
  ingest_metric_stream {
    UUID id
    Int64 version
    UInt8 is_deleted
  }
  ingest_metric_stream_delete_acknowledgement {
    UUID event_id
    DateTime64 applied_at
  }
Loading

File-Level Changes

Change Details Files
Fix ClickHouse metric_stream tombstoning to delete the latest scoped version per ID and add deletion acknowledgements written only after tombstones apply.
  • Refactors the self-referential tombstone INSERT to aggregate latest rows per id using argMax over (version, ingested_at) and only insert new is_deleted=1 versions when the latest row is not already deleted.
  • Introduces a ClickHouse delete-acknowledgement insert helper that writes the deletion event_id into a new ReplacingMergeTree acknowledgement table once tombstones are applied.
  • Ensures the metric-stream sink applies delete events by flushing buffered inserts, issuing tombstones, then writing a corresponding acknowledgement row when the delete event carries an eventId.
src/metric-stream/clickhouse-sink.ts
src/metric-stream/clickhouse-table.ts
src/db/clickhouse-metric-stream-bootstrap.ts
Add a ClickHouse delete-acknowledgement table, migration, and bootstrap wiring so the new receipt path is deployed and available in all environments.
  • Defines buildMetricStreamDeleteAcknowledgementTableSql and wires it into native metric-stream bootstrap statements.
  • Adds ClickHouse migration 0045_metric_stream_delete_acknowledgement registering creation of the acknowledgement table and updates the migration registry and tests accordingly.
src/metric-stream/clickhouse-table.ts
src/db/clickhouse-metric-stream-bootstrap.ts
src/db/clickhouse-migrations/0045_metric_stream_delete_acknowledgement.ts
src/db/clickhouse-migrations/registry.ts
src/db/clickhouse-migrations/registry.test.ts
Introduce versioned metric-stream delete events with UUID event IDs while preserving compatibility with archived version-1 delete events.
  • Adds a v2 delete-event schema with eventId and updates the Redpanda event union, type guards, and creator to emit version=2 deletes with randomUUID IDs.
  • Extends tests to assert v2 delete-event shape and to accept v1 delete events during archive replay.
  • Updates producer and replacement publish result types to use the v2 delete-event type.
src/metric-stream/events.ts
src/metric-stream/events.test.ts
src/metric-stream/redpanda-producer.ts
Change provider delete analytics jobs to wait on ClickHouse deletion acknowledgements keyed by delete-event ID instead of scanning ingest.metric_stream FINAL for live rows.
  • Replaces countActiveProviderMetricStreamRows with countMetricStreamDeleteAcknowledgements and introduces waitForMetricStreamDeleteAcknowledgement polling the acknowledgement table by eventId with the same timeout/poll behavior.
  • Plumbs metricStreamDeleteEventId from replaceMetricStreamBatch through the provider delete queue payload into processActivityDeleteAnalyticsJob, and updates tests to assert the new wiring.
  • Removes tests that depended on provider-scoped FINAL scans and adds tests that exercise acknowledgement polling behavior and timeout semantics.
src/analytics/activity-read-model-build.ts
src/analytics/activity-read-model-build.test.ts
src/jobs/queues.ts
src/jobs/queues.test.ts
src/jobs/process-activity-delete-analytics-job.ts
src/jobs/process-activity-delete-analytics-job.test.ts
src/db/metric-stream-writer.ts
src/db/metric-stream-writer.test.ts
packages/server/src/routers/provider-detail.ts
packages/server/src/routers/provider-detail.test.ts
scripts/backfill-ride-with-gps-track-points.ts
Extend ClickHouse integration and unit tests to cover provider-scoped tombstoning and deletion acknowledgements end-to-end.
  • Adds integration tests to assert provider-scoped tombstones mark the latest-version row as deleted and that acknowledgement rows are written only after applying delete events via the sink.
  • Updates sink tests to verify command ordering, acknowledgement inserts, and call counts for delete-event handling.
  • Adjusts existing tests for replacement publishing and backfills to account for the new replacement receipt structure and delete-event version.
src/metric-stream/clickhouse-sink.integration.test.ts
src/metric-stream/clickhouse-sink.test.ts
src/db/metric-stream-writer.test.ts
Document the new ingestion path, deletion protocol, and the specific production incident motivating these changes.
  • Updates ClickHouse metric-stream documentation to reference ingest.metric_stream, describe the ReplacingMergeTree semantics, explain the delete-acknowledgement table, and outline the v2 deletion protocol.
  • Adds a detailed production-incident baseline entry describing the provider deletion timeout, root cause, and mitigations implemented here.
docs/clickhouse-metric-stream.md
docs/production-incident-baseline.md
Tighten ClickHouse query guardrails and dbt client timeouts to prevent abandoned long-running queries from accumulating.
  • Replaces the previous nullable-tuple-only ClickHouse user profile with a default profile that also enables cancel_http_readonly_queries_on_client_close and caps max_execution_time with immediate speed checks.
  • Rotates the Swarm config key and stack wiring to mount the new ClickHouse profile, and updates deployment documentation to describe the new query guardrails and their rationale.
  • Sets dbt ClickHouse profile send_receive_timeout to 300 seconds to align with the new server-side max_execution_time.
deploy/clickhouse/users.d/default-query-guardrails.xml
deploy/stack.yml
deploy/README.md
analytics/profiles.yml

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 Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Metric stream deletions now emit version 2 events with IDs, write ClickHouse tombstones and acknowledgement receipts, propagate those IDs through provider-delete jobs, and poll acknowledgements before analytics refresh. ClickHouse profiles also add bounded query execution and disconnect cancellation.

Changes

Metric stream deletion flow

Layer / File(s) Summary
Versioned deletion events and replacement receipts
src/metric-stream/events.ts, src/metric-stream/redpanda-producer.ts, src/db/metric-stream-writer.ts, related tests
Deletion events support version 2 UUIDs, while replacement operations return the deletion event ID and affected row count.
ClickHouse tombstones and acknowledgements
src/metric-stream/clickhouse-sink.ts, src/metric-stream/clickhouse-table.ts, src/db/clickhouse-migrations/*, related tests
The sink selects latest rows for tombstoning, records deletion acknowledgements, and provisions ingest.metric_stream_delete_acknowledgement.
Deletion event propagation and refresh coordination
src/jobs/*, src/analytics/activity-read-model-build.ts, packages/server/src/routers/provider-detail.ts, related tests
Provider-delete jobs carry the deletion event ID and wait for its ClickHouse acknowledgement before rebuilding analytics models and invalidating caches.
ClickHouse guardrails and documentation
deploy/*, analytics/profiles.yml, docs/*
Deployment and dbt profiles add query timeouts and disconnect cancellation; metric-stream and incident documentation describe the acknowledgement protocol.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProviderDeletion
  participant MetricStreamWriter
  participant Redpanda
  participant ClickHouseSink
  participant AnalyticsJob
  ProviderDeletion->>MetricStreamWriter: replaceMetricStreamBatch
  MetricStreamWriter->>Redpanda: publish version 2 deletion event
  Redpanda->>ClickHouseSink: deliver eventId
  ClickHouseSink->>ClickHouseSink: write tombstone and acknowledgement
  AnalyticsJob->>ClickHouseSink: poll acknowledgement
Loading

Possibly related PRs

  • Asherlc/dofek#1180: Also modifies the production ClickHouse dbt profile in analytics/profiles.yml.
  • Asherlc/dofek#1356: Also modifies metric-stream delete-scope handling and versioned ClickHouse rows.
  • Asherlc/dofek#1477: Also touches the activity analytics recompute pipeline and related job wiring.

Suggested labels: area/server, area/db, area/infra, type/bug

Suggested reviewers: cubic-dev-ai

🚥 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 The title is imperative, area-prefixed, under 70 characters, and accurately summarizes the main change.

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.

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for 4d1245ea 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 left some high level feedback:

  • The new metricStreamDeleteEventId field on ProviderDeleteAnalyticsJobData is required, but existing queued provider-delete-analytics-refresh jobs in Redis won’t have it; consider handling undefined in processActivityDeleteAnalyticsJob (e.g., falling back to the old wait logic or skipping the ClickHouse acknowledgement wait) to avoid deploy-time failures.
  • In acknowledgeMetricStreamDeletion, the error message says "ClickHouse metric-stream replacement requires a command-capable client" even though it’s called from the deletion path; updating the wording to mention deletion/acknowledgement would make failures easier to interpret.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `metricStreamDeleteEventId` field on `ProviderDeleteAnalyticsJobData` is required, but existing queued `provider-delete-analytics-refresh` jobs in Redis won’t have it; consider handling `undefined` in `processActivityDeleteAnalyticsJob` (e.g., falling back to the old wait logic or skipping the ClickHouse acknowledgement wait) to avoid deploy-time failures.
- In `acknowledgeMetricStreamDeletion`, the error message says "ClickHouse metric-stream replacement requires a command-capable client" even though it’s called from the deletion path; updating the wording to mention deletion/acknowledgement would make failures easier to interpret.

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/jobs/process-activity-delete-analytics-job.ts
@codereviewbot-ai

Copy link
Copy Markdown

[Review Note] File: src/metric-stream/clickhouse-sink.ts Line: 110

Nit: Wrapping user_id and activity_id in toString() prevents ClickHouse from using the primary key index for these columns, which can significantly degrade performance on large datasets. Since these columns are UUID and ClickHouse handles UUID strings in query parameters correctly when using the :UUID type hint, you should compare them directly.

  if (scope.userId) {
    queryParams.user_id = scope.userId;
    conditions.push("user_id = {user_id:UUID}");
  }

(And similarly for activity_id below).

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

🤖 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 `@src/analytics/activity-read-model-build.ts`:
- Around line 122-130: Update the query result handling in the
acknowledgement-count flow to validate the JSONEachRow response with Zod before
reading acknowledgement_count. Replace reliance on the client.query generic type
with a Zod schema that parses the returned rows, then preserve the existing
numeric fallback and Number conversion for a missing or invalid count according
to the established behavior.

In `@src/metric-stream/clickhouse-sink.integration.test.ts`:
- Around line 109-115: Replace the hardcoded table name in the acknowledgement
count query with the exported METRIC_STREAM_DELETE_ACKNOWLEDGEMENT_TABLE
constant, and update the clickhouse-table.ts import to include it alongside the
existing table constant.

In `@src/metric-stream/clickhouse-sink.ts`:
- Around line 174-200: The metric query applies scope predicates before
selecting the latest version, allowing stale scoped rows to represent an ID.
Update the query around the latest_row subquery to identify candidate IDs using
the scope conditions, select argMax for each candidate across all versions, then
apply the scope predicates to the resulting latest tuple before excluding
tombstones. Add a real ClickHouse integration fixture covering an older matching
version followed by a newer out-of-scope version, and verify deleting the old
scope preserves the newer row.

In `@src/metric-stream/events.ts`:
- Around line 108-120: Update the eventId validator in
metricStreamDeletedEventV2Schema from z.guid() to z.uuid(), preserving the
existing schema structure and strict object validation.
🪄 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: 4a3964a8-21ea-433d-bca8-e976d48768d6

📥 Commits

Reviewing files that changed from the base of the PR and between e55faf1 and ffab5e3.

📒 Files selected for processing (29)
  • analytics/profiles.yml
  • deploy/README.md
  • deploy/clickhouse/users.d/allow-experimental-nullable-tuple-type.xml
  • deploy/clickhouse/users.d/default-query-guardrails.xml
  • deploy/stack.yml
  • docs/clickhouse-metric-stream.md
  • docs/production-incident-baseline.md
  • packages/server/src/routers/provider-detail.test.ts
  • packages/server/src/routers/provider-detail.ts
  • scripts/backfill-ride-with-gps-track-points.ts
  • src/analytics/activity-read-model-build.test.ts
  • src/analytics/activity-read-model-build.ts
  • src/db/clickhouse-metric-stream-bootstrap.ts
  • src/db/clickhouse-migrations/0045_metric_stream_delete_acknowledgement.ts
  • src/db/clickhouse-migrations/registry.test.ts
  • src/db/clickhouse-migrations/registry.ts
  • src/db/metric-stream-writer.test.ts
  • src/db/metric-stream-writer.ts
  • src/jobs/process-activity-delete-analytics-job.test.ts
  • src/jobs/process-activity-delete-analytics-job.ts
  • src/jobs/queues.test.ts
  • src/jobs/queues.ts
  • src/metric-stream/clickhouse-sink.integration.test.ts
  • src/metric-stream/clickhouse-sink.test.ts
  • src/metric-stream/clickhouse-sink.ts
  • src/metric-stream/clickhouse-table.ts
  • src/metric-stream/events.test.ts
  • src/metric-stream/events.ts
  • src/metric-stream/redpanda-producer.ts
💤 Files with no reviewable changes (1)
  • deploy/clickhouse/users.d/allow-experimental-nullable-tuple-type.xml

Comment thread src/analytics/activity-read-model-build.ts Outdated
Comment thread src/metric-stream/clickhouse-sink.integration.test.ts
Comment thread src/metric-stream/clickhouse-sink.ts Outdated
Comment thread src/metric-stream/events.ts
@codereviewbot-ai

codereviewbot-ai Bot commented Jul 18, 2026

Copy link
Copy Markdown

🤖 Review aborted: the PR is too complex or took too long to analyze.

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

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

Comment thread src/analytics/activity-read-model-build.ts
Comment thread src/analytics/activity-read-model-build.ts
@codereviewbot-ai

Copy link
Copy Markdown

[Review Note] File: src/jobs/process-activity-delete-analytics-job.ts Line: 100

Why is the acknowledgement wait only implemented for provider deletes and not for activity deletes?

If an activity is deleted, its metrics are also removed from ClickHouse. If the analytics rebuild starts before those tombstones are applied, the resulting analytics might still include the deleted activity's metrics.

Consider adding metricStreamDeleteEventId to ActivityDeleteAnalyticsJobData and waiting for it here as well.

Validate deletion acknowledgements at the database boundary and tighten deletion event UUIDs.

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

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Repository rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours per repository. Upgrade to a paid plan for unlimited reviews.

@Asherlc Asherlc changed the title Fix ClickHouse provider deletion acknowledgements [server] Fix ClickHouse provider deletion acknowledgements Jul 18, 2026
@Asherlc

Asherlc commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

Review follow-up:

@Asherlc

Asherlc commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit full rereview

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

@Asherlc Full re-review requested for #1676, including the follow-up changes.

✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 12 minutes.

@Asherlc

Asherlc commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit full rereview

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

@Asherlc Full re-review requested for #1676, including the follow-up changes.

✅ Action performed

Full review finished.

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

🤖 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 `@src/metric-stream/clickhouse-sink.ts`:
- Around line 146-149: Update the scope condition logic around
metricStreamDeleteScopeSchema handling so externalId: null emits a ClickHouse IS
NULL predicate without binding external_id, while defined non-null values retain
the existing equality predicate and parameter. Add a minimal ClickHouse
integration test covering deletion of rows with external_id IS NULL, following
the repository’s database-semantic test requirements.
🪄 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: 8742e89c-adc1-481d-9eaf-5d701c92ec19

📥 Commits

Reviewing files that changed from the base of the PR and between e55faf1 and 1d16e7b.

📒 Files selected for processing (29)
  • analytics/profiles.yml
  • deploy/README.md
  • deploy/clickhouse/users.d/allow-experimental-nullable-tuple-type.xml
  • deploy/clickhouse/users.d/default-query-guardrails.xml
  • deploy/stack.yml
  • docs/clickhouse-metric-stream.md
  • docs/production-incident-baseline.md
  • packages/server/src/routers/provider-detail.test.ts
  • packages/server/src/routers/provider-detail.ts
  • scripts/backfill-ride-with-gps-track-points.ts
  • src/analytics/activity-read-model-build.test.ts
  • src/analytics/activity-read-model-build.ts
  • src/db/clickhouse-metric-stream-bootstrap.ts
  • src/db/clickhouse-migrations/0045_metric_stream_delete_acknowledgement.ts
  • src/db/clickhouse-migrations/registry.test.ts
  • src/db/clickhouse-migrations/registry.ts
  • src/db/metric-stream-writer.test.ts
  • src/db/metric-stream-writer.ts
  • src/jobs/process-activity-delete-analytics-job.test.ts
  • src/jobs/process-activity-delete-analytics-job.ts
  • src/jobs/queues.test.ts
  • src/jobs/queues.ts
  • src/metric-stream/clickhouse-sink.integration.test.ts
  • src/metric-stream/clickhouse-sink.test.ts
  • src/metric-stream/clickhouse-sink.ts
  • src/metric-stream/clickhouse-table.ts
  • src/metric-stream/events.test.ts
  • src/metric-stream/events.ts
  • src/metric-stream/redpanda-producer.ts
💤 Files with no reviewable changes (1)
  • deploy/clickhouse/users.d/allow-experimental-nullable-tuple-type.xml

Comment thread src/metric-stream/clickhouse-sink.ts Outdated
@codereviewbot-ai

codereviewbot-ai Bot commented Jul 18, 2026

Copy link
Copy Markdown

The PR introduces a robust acknowledgement mechanism for metric stream deletions, effectively resolving potential race conditions and visibility issues in ClickHouse.

Highlights:

  • Reliable Acknowledgement: The new metric_stream_delete_acknowledgement table provides a durable signal that tombstones have been applied, replacing the previous eventually-consistent row counting method.
  • Precise Tombstoning: The use of argMax in the deletion query ensures that tombstones are only applied to the latest version of a row if it still matches the deletion scope.
  • Improved Validation: Upgrading to v2 events with z.uuid() validation and using native UUID types in ClickHouse queries improves both data integrity and query performance.
  • Comprehensive Testing: The inclusion of integration tests for the acknowledgement flow and unit tests for versioned event handling ensures high confidence in the changes.

The implementation is clean and follows ClickHouse best practices for handling ReplacingMergeTree deletions. LGTM.


🤖 Reviewed by codereviewbot.ai - Catch bugs before your team does.

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

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@Asherlc
Asherlc enabled auto-merge (squash) July 18, 2026 05:12

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/metric-stream/clickhouse-sink.test.ts (1)

184-190: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the exported table name constant.

To maintain consistency and avoid hardcoding schema coordinates, use the METRIC_STREAM_DELETE_ACKNOWLEDGEMENT_TABLE constant instead of the raw string "ingest.metric_stream_delete_acknowledgement".

♻️ Proposed refactor
     expect(command).toHaveBeenNthCalledWith(2, {
-      query: expect.stringContaining("ingest.metric_stream_delete_acknowledgement"),
+      query: expect.stringContaining(METRIC_STREAM_DELETE_ACKNOWLEDGEMENT_TABLE),
       query_params: { event_id: deleteEvent.eventId },
     });

You may also need to update the import block from ./clickhouse-table.ts to include METRIC_STREAM_DELETE_ACKNOWLEDGEMENT_TABLE if it isn't already imported.

🤖 Prompt for 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.

In `@src/metric-stream/clickhouse-sink.test.ts` around lines 184 - 190, Update the
assertion for the second command in the test around firstCommandQuery to use the
exported METRIC_STREAM_DELETE_ACKNOWLEDGEMENT_TABLE constant instead of the
hardcoded ingest.metric_stream_delete_acknowledgement string, adding the
constant to the clickhouse-table import if needed.
🤖 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.

Outside diff comments:
In `@src/metric-stream/clickhouse-sink.test.ts`:
- Around line 184-190: Update the assertion for the second command in the test
around firstCommandQuery to use the exported
METRIC_STREAM_DELETE_ACKNOWLEDGEMENT_TABLE constant instead of the hardcoded
ingest.metric_stream_delete_acknowledgement string, adding the constant to the
clickhouse-table import if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c857ef93-9df1-409e-9466-d646f48edcb4

📥 Commits

Reviewing files that changed from the base of the PR and between 1d16e7b and 9d78ad8.

📒 Files selected for processing (3)
  • src/metric-stream/clickhouse-sink.integration.test.ts
  • src/metric-stream/clickhouse-sink.test.ts
  • src/metric-stream/clickhouse-sink.ts

@Asherlc
Asherlc merged commit cbacad2 into main Jul 18, 2026
109 checks passed
@Asherlc
Asherlc deleted the Asherlc/fix-garmin-ch-sync branch July 18, 2026 05:27
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