Skip to content

fix(db): skip deletes for unsent keys in filterDuplicateInserts#1551

Open
v-anton wants to merge 2 commits into
TanStack:mainfrom
v-anton:fix/ghost-delete-sentkeys
Open

fix(db): skip deletes for unsent keys in filterDuplicateInserts#1551
v-anton wants to merge 2 commits into
TanStack:mainfrom
v-anton:fix/ghost-delete-sentkeys

Conversation

@v-anton
Copy link
Copy Markdown

@v-anton v-anton commented May 25, 2026

🎯 Changes

filterDuplicateInserts forwarded delete changes for keys never present in sentKeys. When SSE delivers deletes for items filtered out by the where clause, the spurious -1 multiplicity corrupts D2's TopKState window.

The fix checks the return value of sentKeys.delete() — if false, the key was never sent, so the delete is skipped.

✅ Checklist

  • I have tested this code locally with pnpm test:pr.
  • I have followed the steps in the Contributing guide.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes

    • Prevented spurious delete events from being emitted for items that were never previously sent, improving synchronization consistency and preventing downstream mismatches.
  • Tests

    • Added unit tests covering change-filtering behavior, including mixed insert/delete scenarios and “ghost delete” cases, to ensure deletes are only forwarded when appropriate and tracking state is updated correctly.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8a2cca51-9630-4d3d-ad22-04f4b62c5a5b

📥 Commits

Reviewing files that changed from the base of the PR and between 2774f87 and 2a36fbf.

📒 Files selected for processing (1)
  • packages/db/tests/collection-subscriber-duplicate-inserts.test.ts

📝 Walkthrough

Walkthrough

filterDuplicateInserts now skips delete changes for keys never recorded in sentKeys. Unit tests validate empty-input behavior, ghost-delete skipping, forwarding/removal of known deletes, and mixed insert/delete sequences. A changeset records the patch release.

Changes

Delete Guard Fix

Layer / File(s) Summary
Delete guard and unit tests
packages/db/src/query/live/utils.ts, packages/db/tests/collection-subscriber-duplicate-inserts.test.ts
filterDuplicateInserts adds a guard checking sentKeys before forwarding deletes; deletes for unknown keys are skipped. Tests import the function and validate empty input, ghost-delete skipping, forwarding/removal of known deletes, and mixed insert/delete sequences with assertions on sentKeys.
Release documentation
.changeset/fix-ghost-delete-sentkeys.md
Changeset file declares a patch version bump for @tanstack/db and documents the fix for skipping deletes in filterDuplicateInserts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • samwillis

Poem

🐰 I peeked at sentKeys in the night,
A ghostly delete met no prior sight.
I skip the phantom, keep counts true,
Hop, patch, and tidy—now D2's queued! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely describes the main change: skipping deletes for unsent keys in the filterDuplicateInserts function.
Description check ✅ Passed The pull request description follows the template structure, includes clear explanation of the problem and fix, and completes all required checklist items including changeset generation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/db/tests/collection-subscriber-duplicate-inserts.test.ts (1)

457-508: ⚡ Quick win

Add an explicit empty-batch/empty-set corner-case test in this block.

The new tests reproduce the bug well, but this added suite still misses an explicit changes = [] + sentKeys = new Set() case.

Proposed test addition
 describe(`filterDuplicateInserts`, () => {
+  it(`should return empty output for empty changes and empty sentKeys`, () => {
+    const sentKeys = new Set<string | number>()
+    const changes: Array<ChangeMessage<TestItem, string>> = []
+
+    const result = filterDuplicateInserts(changes, sentKeys)
+
+    expect(result).toEqual([])
+    expect(sentKeys.size).toBe(0)
+  })
+
   it(`should skip deletes for keys never sent to D2`, () => {
As per coding guidelines, `**/*.test.{ts,tsx,js}`: "Test corner cases including: empty arrays/sets, single-element collections, undefined vs null values, resolved promises, async race conditions, and limit/offset edge cases".
🤖 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 `@packages/db/tests/collection-subscriber-duplicate-inserts.test.ts` around
lines 457 - 508, Add a corner-case unit test for filterDuplicateInserts that
covers an empty changes array and an empty sentKeys set: create changes = [] and
sentKeys = new Set(), call filterDuplicateInserts(changes, sentKeys), and assert
the result is an empty array and sentKeys remains empty (or unchanged). Place
this test in the existing describe(`filterDuplicateInserts`) block alongside the
other it(...) cases so it validates the empty-batch/empty-set path for the
filterDuplicateInserts function.
🤖 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.

Nitpick comments:
In `@packages/db/tests/collection-subscriber-duplicate-inserts.test.ts`:
- Around line 457-508: Add a corner-case unit test for filterDuplicateInserts
that covers an empty changes array and an empty sentKeys set: create changes =
[] and sentKeys = new Set(), call filterDuplicateInserts(changes, sentKeys), and
assert the result is an empty array and sentKeys remains empty (or unchanged).
Place this test in the existing describe(`filterDuplicateInserts`) block
alongside the other it(...) cases so it validates the empty-batch/empty-set path
for the filterDuplicateInserts function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b5ed8e11-d4d5-4876-8783-a230926a8d39

📥 Commits

Reviewing files that changed from the base of the PR and between be656be and 2774f87.

📒 Files selected for processing (3)
  • .changeset/fix-ghost-delete-sentkeys.md
  • packages/db/src/query/live/utils.ts
  • packages/db/tests/collection-subscriber-duplicate-inserts.test.ts

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.

1 participant