Skip to content

Make provider deletion durable with generation fencing - #1677

Merged
Asherlc merged 9 commits into
mainfrom
Asherlc/evaluate-bullmq-fit
Jul 18, 2026
Merged

Asherlc merged 9 commits into
mainfrom
Asherlc/evaluate-bullmq-fit

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replaces provider-wide deletion with a PostgreSQL transactional outbox and checkpointed BullMQ redrive workflow, validates Redis payloads, and fails fast when required provider tables are missing.
  • Adds provider-generation fencing tokens, batched authoritative reads, ClickHouse projections, race-safe exact-ID tombstones with row-monotonic versions, and acknowledgement-gated analytics refresh.
  • Includes PostgreSQL and ClickHouse migrations, projection-safe cleanup and SQL lint corrections, plus unit and real-database tests for transactions, fencing, insertion races, and retry exhaustion.
  • Documents deployment, historical projection materialization, verification, and the production and validation incident context.

Replace unbounded provider-wide ClickHouse deletion with a generation-fenced transactional outbox and checkpointed BullMQ batches.

Includes PostgreSQL and ClickHouse migrations. Historical projection materialization remains operator-controlled, and pre-migration deletion requests must be resubmitted after deployment.
Copilot AI review requested due to automatic review settings July 18, 2026 17:29
@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.

@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

@codereviewbot-ai

codereviewbot-ai Bot commented Jul 18, 2026

Copy link
Copy Markdown

The PR looks solid and introduces a robust "generation" concept to handle provider data deletion and stale data insertion. The race condition handling in the ClickHouse sink is particularly well-thought-out.

I've noted two main points:

  1. Consistency: deleteProviderData (used for disconnecting providers) should also trigger the ClickHouse deletion outbox event, similar to requestProviderDataDeletion.
  2. Performance: writeMetricStreamRows now adds a DB roundtrip per batch. Since it's often called in loops, this could be optimized by caching the generation across batches.

Otherwise, the changes are correct and well-tested.


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

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.

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

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces generation-fenced provider-data deletion through PostgreSQL outbox records, BullMQ dispatch, checkpointed ClickHouse tombstoning, and acknowledgement handling. It also propagates generations through metric-stream ingestion, updates migrations and tests, and adds operational documentation.

Changes

Provider data deletion workflow

Layer / File(s) Summary
Persistence and generation contracts
src/db/*, drizzle/*, package.json
Adds generation and deletion-outbox tables, typed persistence helpers, lifecycle updates, and a public database export.
Generation-aware metric streaming
src/metric-stream/*, packages/server/src/repositories/*, packages/server/src/routers/*
Adds generation fields, database-backed generation enrichment, ClickHouse generation fencing, and stale-event tombstoning.
Outbox dispatch and workers
src/jobs/*, packages/server/src/routers/provider-detail.ts, packages/server/src/repositories/provider-detail-repository.ts
Replaces synchronous deletion with transactional outbox creation, BullMQ dispatch, checkpointed ClickHouse deletion, completion handling, redrive behavior, and shutdown wiring.
Tests and operational support
docs/*, analytics/*, src/providers/*, src/*test*, packages/server/src/**/*.test.ts
Updates SQL expectations, integration cleanup, provider mocks, workflow tests, runbook documentation, and incident baselines.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: area/server, area/db, area/providers, type/feature

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is clear and imperative, but it misses the required area prefix for this backend change. Prefix it with the relevant area, e.g. "[server] Make provider deletion durable with generation fencing", and keep it under 70 characters.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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 60a5ff9e are ready:

This comment updates automatically on each PR push.

@codereviewbot-ai

Copy link
Copy Markdown

[Review Note] File: packages/server/src/repositories/provider-detail-repository.ts Line: 765

The deleteProviderData method (used during provider disconnection) should also trigger the ClickHouse data deletion by advancing the generation and writing to the outbox. Currently, it only deletes Postgres records, leaving stale data in ClickHouse.

  async deleteProviderData(providerId: string): Promise<void> {
    const eventId = randomUUID();
    await this.#db.transaction(async (transaction) => {
      await this.#deleteProviderTablesInTransaction(
        transaction,
        providerId,
        DISCONNECT_CHILD_TABLES,
      );
      await createProviderDataDeletionRequest(transaction, this.#userId, providerId, eventId);
    });
  }

Comment thread src/metric-stream/write-metric-stream.ts
Format the migration for the CI SQLFluff policy and build the outbox index concurrently.

Use synchronous ClickHouse mutations for integration cleanup because lightweight deletes reject tables with projections.
@codereviewbot-ai

codereviewbot-ai Bot commented Jul 18, 2026

Copy link
Copy Markdown

The introduction of the provider data generation and outbox pattern is a solid approach to handling asynchronous deletions in ClickHouse while maintaining consistency.

I've noted a few areas for improvement:

  1. Performance: Batching the generation lookups in writeMetricStreamRows to avoid sequential DB queries.
  2. Correctness/UX: Adding a generation filter to the ClickHouse read queries in ProviderDetailRepository to ensure deleted data is hidden immediately, even before the background tombstoning job completes.

The rest of the changes, including the SQL migrations and the outbox processor job, look well-implemented.


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

Comment thread src/metric-stream/write-metric-stream.ts
@codereviewbot-ai

Copy link
Copy Markdown

[Review Note] File: packages/server/src/repositories/provider-detail-repository.ts Line: 706

The ClickHouse query here does not filter by generation.

Since createDeterministicMetricStreamId now includes the generation in the hash, a new generation of data for the same external_id will have a different id. Without a generation filter, both the old (deleted) and new records will appear in the results until the asynchronous processProviderDataDeletionJob tombstones the old records.

To ensure immediate consistency in the UI after a deletion, consider filtering by the current generation. Since the current generation is stored in Postgres, you might need to fetch it first or join with the ingest.provider_data_generation table in ClickHouse (though the latter is also updated asynchronously).

WHERE user_id = {userId:UUID}
  AND provider_id = {providerId:String}
  AND generation >= {currentGeneration:UInt64}

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

@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

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/write-metric-stream.ts (1)

6-44: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

N+1 database queries when resolving provider generation.

writeMetricStreamRows resolves provider generations sequentially per missing key, which causes O(N) queries when receiving generic arrays spanning multiple providers (intra-batch). Additionally, because sync repositories call writeMetricStreamRows inside their batching loops, identical queries are repeated across every batch for the exact same provider (inter-batch). For high-frequency data streams (e.g., IMU syncs mapping hundreds of thousands of samples into chunks of 1000), this creates a massive database latency bottleneck in background workers.

  • src/metric-stream/write-metric-stream.ts#L6-L44: Update WriteMetricStreamRowsOptions to accept an optional generationCache?: Map<string, number>, thread it through addProviderDataGenerations, and refactor the generation lookup to fetch missing keys concurrently via Promise.all rather than using a sequential for...of loop.
  • packages/server/src/repositories/activity-recording-repository.ts#L157-L157: Initialize a const generationCache = new Map<string, number>(); immediately before the input.samples loop and pass it in the writeMetricStreamRows options.
  • packages/server/src/repositories/ble-heart-rate-sync-repository.ts#L104-L104: Initialize and pass a shared generationCache around the insertSampleBatch loop.
  • packages/server/src/repositories/health-kit-sync-repository.ts#L393-L393: Initialize and pass a shared generationCache around the processBodyMeasurements loop.
  • packages/server/src/repositories/health-kit-sync-repository.ts#L504-L504: Initialize and pass a shared generationCache around the processMetricStream loop.
  • packages/server/src/repositories/inertial-measurement-unit-sync-repository.ts#L94-L98: Initialize and pass a shared generationCache around the insertBatch loop.
  • packages/server/src/repositories/watch-altitude-sync-repository.ts#L81-L81: Initialize and pass a shared generationCache around the insertSampleBatch loop.
  • packages/server/src/repositories/whoop-ble-sync-repository.ts#L103-L103: Initialize and pass a shared generationCache around the insertRealtimeDataBatch loop.
⚡ Proposed fix for `src/metric-stream/write-metric-stream.ts`
 export interface WriteMetricStreamRowsOptions {
   database: Database;
   publisher: MetricStreamEventPublisher;
   rows: readonly MetricStreamRowInput[];
+  generationCache?: Map<string, number>;
 }
...
 export async function addProviderDataGenerations(
   database: Database,
   rows: readonly MetricStreamRowInput[],
+  cache?: Map<string, number>,
 ): Promise<MetricStreamRowInput[]> {
-  const generationsByProvider = new Map<string, number>();
-  for (const row of rows) {
-    const providerKey = `${row.userId}\0${row.providerId}`;
-    if (!generationsByProvider.has(providerKey)) {
-      generationsByProvider.set(
-        providerKey,
-        await getProviderDataGeneration(database, row.userId, row.providerId),
-      );
-    }
-  }
+  const generationsByProvider = cache ?? new Map<string, number>();
+
+  const missingKeys = new Set<string>();
+  for (const row of rows) {
+    const providerKey = `${row.userId}\0${row.providerId}`;
+    if (!generationsByProvider.has(providerKey)) {
+      missingKeys.add(providerKey);
+    }
+  }
+
+  if (missingKeys.size > 0) {
+    await Promise.all(
+      Array.from(missingKeys).map(async (key) => {
+        const [userId, providerId] = key.split("\0");
+        const generation = await getProviderDataGeneration(database, userId!, providerId!);
+        generationsByProvider.set(key, generation);
+      })
+    );
+  }
 
   return rows.map((row) => {
...
 export async function writeMetricStreamRows(
   options: WriteMetricStreamRowsOptions,
 ): Promise<WriteMetricStreamRowsResult> {
-  const rowsWithGeneration = await addProviderDataGenerations(options.database, options.rows);
+  const rowsWithGeneration = await addProviderDataGenerations(
+    options.database,
+    options.rows,
+    options.generationCache
+  );
🤖 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/write-metric-stream.ts` around lines 6 - 44, Eliminate
repeated provider-generation queries by adding an optional shared
generationCache to WriteMetricStreamRowsOptions and threading it through
addProviderDataGenerations; resolve uncached provider keys concurrently with
Promise.all, then populate and reuse the cache. In
activity-recording-repository.ts:157, ble-heart-rate-sync-repository.ts:104,
health-kit-sync-repository.ts:393 and :504,
inertial-measurement-unit-sync-repository.ts:94-98,
watch-altitude-sync-repository.ts:81, and whoop-ble-sync-repository.ts:103,
create one Map before the respective batching loop and pass it to each
writeMetricStreamRows or insert batch call.
🤖 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
`@packages/server/src/jobs/process-provider-data-deletion-job.integration.test.ts`:
- Around line 105-121: Parse the results of the generation and acknowledgement
ClickHouse queries with the existing Zod coercion pattern before asserting them.
Update the assertions around generationResult and acknowledgementResult so
aggregate values such as max(generation) and count() are converted from their
runtime representation to numbers, while preserving the expected values of 1.

In `@packages/server/src/repositories/provider-detail-repository.ts`:
- Around line 780-790: The transaction cleanup loop around transaction.execute
must not swallow undefined_table errors because the transaction remains aborted.
Require the listed tables to exist, or isolate each optional delete with a
savepoint that can be rolled back before continuing; preserve the outbox
insert's executability. Add a real-PostgreSQL integration test with a minimal
fixture covering the intended missing-table behavior.

In `@src/jobs/provider-data-deletion-outbox.ts`:
- Around line 18-23: Update the provider deletion outbox flow around
enqueueProviderDataDeletion and markProviderDataDeletionDispatched to handle
terminal queue failures after all attempts are exhausted. Reset the
corresponding request to pending or explicitly requeue it when the worker
reports a terminal failure, so it becomes pollable again instead of remaining
dispatched. Keep successful dispatch handling unchanged.

In `@src/jobs/worker.ts`:
- Around line 167-175: The providerDataDeletionWorker currently trusts the
TypeScript payload type without runtime validation. Define a Zod schema covering
the complete ProviderDataDeletionJobData payload, including checkpoint, parse
job.data at the worker boundary, and pass the parsed result to
processProviderDataDeletionJob while preserving the existing worker context and
callbacks.

---

Outside diff comments:
In `@src/metric-stream/write-metric-stream.ts`:
- Around line 6-44: Eliminate repeated provider-generation queries by adding an
optional shared generationCache to WriteMetricStreamRowsOptions and threading it
through addProviderDataGenerations; resolve uncached provider keys concurrently
with Promise.all, then populate and reuse the cache. In
activity-recording-repository.ts:157, ble-heart-rate-sync-repository.ts:104,
health-kit-sync-repository.ts:393 and :504,
inertial-measurement-unit-sync-repository.ts:94-98,
watch-altitude-sync-repository.ts:81, and whoop-ble-sync-repository.ts:103,
create one Map before the respective batching loop and pass it to each
writeMetricStreamRows or insert batch call.
🪄 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: d60699ae-4ddd-4d73-9b14-751272c70e3c

📥 Commits

Reviewing files that changed from the base of the PR and between cbacad2 and bc2c449.

📒 Files selected for processing (66)
  • analytics/models/read_models/activity_sensor_summary_rows.sql
  • docs/README.md
  • docs/production-incident-baseline.md
  • docs/provider-data-deletion-runbook.md
  • drizzle/0050_provider_data_deletion_outbox.sql
  • package.json
  • packages/server/src/jobs/process-provider-data-deletion-job.integration.test.ts
  • packages/server/src/repositories/activity-recording-repository.test.ts
  • packages/server/src/repositories/activity-recording-repository.ts
  • packages/server/src/repositories/ble-heart-rate-sync-repository.ts
  • packages/server/src/repositories/health-kit-sync-repository.test.ts
  • packages/server/src/repositories/health-kit-sync-repository.ts
  • packages/server/src/repositories/inertial-measurement-unit-sync-repository.ts
  • packages/server/src/repositories/provider-detail-repository.integration.test.ts
  • packages/server/src/repositories/provider-detail-repository.test.ts
  • packages/server/src/repositories/provider-detail-repository.ts
  • packages/server/src/repositories/watch-altitude-sync-repository.ts
  • packages/server/src/repositories/whoop-ble-sync-repository.ts
  • packages/server/src/routers/activity-recording.test.ts
  • packages/server/src/routers/ble-heart-rate-sync.test.ts
  • packages/server/src/routers/clickhouse-integration-test-helpers.ts
  • packages/server/src/routers/health-kit-sync-processors.test.ts
  • packages/server/src/routers/health-kit-sync-processors.ts
  • packages/server/src/routers/health-kit-sync.test.ts
  • packages/server/src/routers/inertial-measurement-unit-sync.test.ts
  • packages/server/src/routers/provider-detail.test.ts
  • packages/server/src/routers/provider-detail.ts
  • packages/server/src/routers/watch-altitude-sync.test.ts
  • packages/server/src/routers/whoop-ble-sync.test.ts
  • src/db/clickhouse-metric-stream-bootstrap.ts
  • src/db/clickhouse-migrations/0034_move_metric_stream_to_ingest.ts
  • src/db/clickhouse-migrations/0046_provider_data_generation.ts
  • src/db/clickhouse-migrations/registry.test.ts
  • src/db/clickhouse-migrations/registry.ts
  • src/db/clickhouse.ts
  • src/db/metric-stream-writer.test.ts
  • src/db/metric-stream-writer.ts
  • src/db/provider-data-deletion.test.ts
  • src/db/provider-data-deletion.ts
  • src/db/schema/events.ts
  • src/db/typed-sql.test.ts
  • src/db/typed-sql.ts
  • src/jobs/process-activity-delete-analytics-job.test.ts
  • src/jobs/process-activity-delete-analytics-job.ts
  • src/jobs/process-provider-data-deletion-job.test.ts
  • src/jobs/process-provider-data-deletion-job.ts
  • src/jobs/provider-data-deletion-outbox.test.ts
  • src/jobs/provider-data-deletion-outbox.ts
  • src/jobs/queues.test.ts
  • src/jobs/queues.ts
  • src/jobs/worker.test.ts
  • src/jobs/worker.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/write-metric-stream.test.ts
  • src/metric-stream/write-metric-stream.ts
  • src/providers/apple-health/db-insertion.integration.test.ts
  • src/providers/apple-health/db-insertion.test.ts
  • src/providers/fitbit/provider.test.ts
  • src/providers/garmin.test.ts
  • src/providers/oura.test.ts
  • src/providers/ride-with-gps-ext.test.ts
  • src/providers/strava.test.ts

Comment thread packages/server/src/jobs/process-provider-data-deletion-job.integration.test.ts Outdated
Comment thread packages/server/src/repositories/provider-detail-repository.ts Outdated
Comment thread src/jobs/provider-data-deletion-outbox.ts
Comment thread src/jobs/worker.ts
Batch fencing-token reads, validate Redis payloads, fail fast on missing tables, and redrive terminal BullMQ failures. Derive ClickHouse tombstone versions from stored rows.
@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.

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

Copy link
Copy Markdown
Owner Author

Review disposition for the outside-diff cross-batch generation-cache suggestion in this review:

  • I did not add a shared cross-batch cache because generation is a fencing token that can advance between batches; reusing a cached value could publish new rows with a stale generation after deletion begins.
  • 3a26e7479 instead batches every unique user/provider scope into one fresh authoritative query per write batch.
  • Unit and real PostgreSQL integration tests cover the batched lookup, while the existing ClickHouse post-insert fence covers the in-flight race.

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

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

Keep provider and sync unit fixtures faithful to the batched generation lookup without weakening production fencing.
@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.

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

@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 `@docs/production-incident-baseline.md`:
- Around line 13856-13860: Update the validation bullet in the production
incident baseline to cite an authoritative successful CI run or primary
test/mutation report for the stated test totals, integration results, deletion
tests, dry-run count, and 100% mutation score. Retain the claims only when each
is verifiable from the linked evidence, and follow the documentation citation
requirement for content under docs/.

In `@packages/server/src/repositories/health-kit-sync-repository.test.ts`:
- Around line 14-22: Move resolveProviderDataGenerationsForTest and its
provider-data-deletion mock setup out of shared src/test/test-helpers.ts imports
and into colocated test-helper files for
packages/server/src/repositories/health-kit-sync-repository.test.ts#L14-L22,
packages/server/src/routers/watch-altitude-sync.test.ts#L3-L11,
packages/server/src/routers/whoop-ble-sync.test.ts#L3-L11,
src/providers/amazfit-zepp.test.ts#L21-L26,
src/providers/apple-health/db-insertion.test.ts#L24-L29,
src/providers/apple-health/import.test.ts#L7-L12,
src/providers/eight-sleep.test.ts#L15-L20,
src/providers/fitbit/provider.test.ts#L21-L26, and
src/providers/garmin.test.ts#L19-L24. Update each test to use its directory’s
local test-helpers.ts while preserving the existing getProviderDataGenerations
mock behavior.

In `@src/jobs/worker.ts`:
- Around line 170-188: Wrap providerDataDeletionJobDataSchema.parse in the
worker processor and convert validation failures into BullMQ’s imported
UnrecoverableError so malformed jobs are not retried or redriven. Update the
providerDataDeletionWorker failed handler to accept the original error
parameter, preserve retry handling, and call captureException for terminal or
unrecoverable failures, including the original job error rather than only retry
errors.

In `@src/metric-stream/clickhouse-sink.integration.test.ts`:
- Around line 107-108: Update the row expectation in the ClickHouse integration
test to assert latest_version as the string "1", matching the declared result
type and ClickHouse UInt64 client behavior; leave is_deleted unchanged.
🪄 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: 908c2b13-ae49-4a5f-97eb-2c245b4fe0bb

📥 Commits

Reviewing files that changed from the base of the PR and between bc2c449 and e2ee260.

📒 Files selected for processing (44)
  • analytics/models/read_models/read_model_microbatch.sql.test.ts
  • cspell.json
  • docs/production-incident-baseline.md
  • drizzle/0050_provider_data_deletion_outbox.sql
  • packages/server/src/jobs/process-provider-data-deletion-job.integration.test.ts
  • packages/server/src/repositories/activity-recording-repository.test.ts
  • packages/server/src/repositories/health-kit-sync-repository.test.ts
  • packages/server/src/repositories/heart-rate-repository.integration.test.ts
  • packages/server/src/repositories/provider-detail-repository.integration.test.ts
  • packages/server/src/repositories/provider-detail-repository.test.ts
  • packages/server/src/repositories/provider-detail-repository.ts
  • packages/server/src/routers/activity-recording.test.ts
  • packages/server/src/routers/ble-heart-rate-sync.test.ts
  • packages/server/src/routers/health-kit-sync-processors.test.ts
  • packages/server/src/routers/health-kit-sync.test.ts
  • packages/server/src/routers/inertial-measurement-unit-sync.test.ts
  • packages/server/src/routers/watch-altitude-sync.test.ts
  • packages/server/src/routers/whoop-ble-sync.test.ts
  • src/db/metric-stream-writer.test.ts
  • src/db/provider-data-deletion.integration.test.ts
  • src/db/provider-data-deletion.test.ts
  • src/db/provider-data-deletion.ts
  • src/jobs/queues.test.ts
  • src/jobs/queues.ts
  • src/jobs/worker.test.ts
  • src/jobs/worker.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/write-metric-stream.test.ts
  • src/metric-stream/write-metric-stream.ts
  • src/providers/amazfit-zepp.test.ts
  • src/providers/apple-health/db-insertion.test.ts
  • src/providers/apple-health/import.test.ts
  • src/providers/eight-sleep.test.ts
  • src/providers/fitbit/provider.test.ts
  • src/providers/garmin.test.ts
  • src/providers/oura.test.ts
  • src/providers/peloton.test.ts
  • src/providers/ride-with-gps-ext.test.ts
  • src/providers/strava.test.ts
  • src/providers/whoop.test.ts
  • src/providers/withings.test.ts
  • src/test/test-helpers.ts
💤 Files with no reviewable changes (1)
  • src/metric-stream/clickhouse-sink.test.ts

Comment thread docs/production-incident-baseline.md Outdated
Comment thread packages/server/src/repositories/health-kit-sync-repository.test.ts
Comment thread src/jobs/worker.ts
Comment thread src/metric-stream/clickhouse-sink.integration.test.ts
Exercise interval dispatch, overlap fencing, error recovery, and shutdown so the lifecycle is mutation-covered.
@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.

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

Classify malformed BullMQ jobs as unrecoverable, keep generation mocks colocated, and align ClickHouse test types with observed results.

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

Exercise null, callable, and malformed record result shapes so the typed SQL guard is mutation-complete.
@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.

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

Exercise persistence edge cases and cached queue creation, reuse, connection wiring, and shutdown for the deletion workflow.
@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.

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