query icon indicating copy to clipboard operation
query copied to clipboard

fix(query-core): prevent duplicate abort event listeners in infinite queries

Open kamja44 opened this issue 2 months ago β€’ 2 comments

🎯 Changes

Fixed a memory leak in infiniteQueryBehavior.ts where abort event listeners were being registered multiple times when the signal property was accessed repeatedly during pagination. Root Cause: The addSignalProperty getter was registering a new abort event listener on every signal access without checking if a listener was already attached. This pattern differed from query.ts, which correctly handles this scenario. Solution:

  • Added listenerAttached flag to prevent duplicate listener registration (line 25, 32-33)
  • Added { once: true } option for automatic listener cleanup (line 39)
  • Added test case to verify no duplicate listeners when signal is accessed multiple times

Files Changed:

  • packages/query-core/src/infiniteQueryBehavior.ts - Fixed duplicate listener bug
  • packages/query-core/src/tests/infiniteQueryBehavior.test.tsx - Added regression test

Impact:

  • Prevents memory leaks in infinite queries with many pages
  • No API changes, fully backward compatible
  • Test coverage: 100% statements, 100% branches for infiniteQueryBehavior.ts

βœ… Checklist

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

πŸš€ Release Impact

  • [O] 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

    • Fixed a memory leak in infinite queries by ensuring abort listeners are not registered more than once, improving stability and memory usage during paginated loads.
  • Tests

    • Added tests confirming abort listeners are attached only once even when the abort signal is accessed repeatedly during query execution.
  • Chores

    • Added a changeset entry to publish the patch.

✏️ Tip: You can customize this high-level summary in your review settings.

kamja44 avatar Dec 09 '25 02:12 kamja44

πŸ¦‹ Changeset detected

Latest commit: 26cc89332244e9ca66c012b4e5b6ae52c2d9a13c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
@tanstack/query-core Patch
@tanstack/angular-query-experimental Patch
@tanstack/query-async-storage-persister Patch
@tanstack/query-broadcast-client-experimental Patch
@tanstack/query-persist-client-core Patch
@tanstack/query-sync-storage-persister Patch
@tanstack/react-query Patch
@tanstack/solid-query Patch
@tanstack/svelte-query Patch
@tanstack/vue-query Patch
@tanstack/angular-query-persist-client Patch
@tanstack/react-query-persist-client Patch
@tanstack/solid-query-persist-client Patch
@tanstack/svelte-query-persist-client Patch
@tanstack/react-query-devtools Patch
@tanstack/react-query-next-experimental Patch
@tanstack/solid-query-devtools Patch
@tanstack/svelte-query-devtools Patch
@tanstack/vue-query-devtools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

changeset-bot[bot] avatar Dec 09 '25 02:12 changeset-bot[bot]

Walkthrough

Patches @tanstack/query-core to prevent duplicate abort event listeners in infinite queries by ensuring the listener is attached at most once, and adds tests and a changeset documenting the fix.

Changes

Cohort / File(s) Summary
Changeset
/.changeset/calm-goats-punch.md
Adds a patch changeset entry documenting the memory-leak fix for infinite query abort listener handling and a patch version bump.
Tests
packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx
Adds a test verifying that AbortSignal.prototype.addEventListener is not called multiple times when context.signal is accessed repeatedly; test asserts one listener per page across multiple pages. The diff contains a duplicated test block.
Implementation
packages/query-core/src/infiniteQueryBehavior.ts
Adds a listenerAttached flag and guards listener registration so the abort listener on context.signal is attached at most once (uses { once: true }); abort still marks cancellation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Focus areas:
    • Verify correct lifecycle/reset of listenerAttached across pages/fetches and across multiple queryFn invocations.
    • Ensure no edge cases where reattachment is required (e.g., new signals across retries/pages).
    • Remove duplicated test block if unintended and confirm test coverage matches intended behavior.

Suggested reviewers

  • TkDodo

Poem

🐰 I hopped where signals used to grow,
Two ears, then threeβ€”oh no, no, no!
One little flag, a single bind,
Now aborts stop leaking, peace of mind. πŸ₯•

Pre-merge checks and finishing touches

βœ… Passed checks (3 passed)
Check name Status Explanation
Title check βœ… Passed The title clearly and specifically summarizes the main change: fixing duplicate abort event listeners in infinite queries, which directly matches the core issue addressed in the changeset.
Description check βœ… Passed The description covers all required template sections: detailed explanation of changes and root cause, completed checklist items, and release impact. All key information is provided with good context.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • [ ] πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

πŸ“œ Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between edb2bcc30ae126d29d7035790dad4d4a4dbd4657 and 26cc89332244e9ca66c012b4e5b6ae52c2d9a13c.

πŸ“’ Files selected for processing (1)
  • packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
πŸ“š Learning: 2025-11-22T09:06:05.219Z
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.

Applied to files:

  • packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx
πŸ”‡ Additional comments (1)
packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx (1)

493-544: LGTM! Test correctly validates the memory leak fix.

The test properly addresses the previous review concern by installing the spy on AbortSignal.prototype.addEventListener before the observer is created, ensuring all listener registrations are captured. The assertion correctly validates that despite accessing context.signal three times per page across three pages (9 total accesses), only 3 abort listeners are registeredβ€”one per page as intended by the fix.

The test is well-structured with clear comments explaining both the expected behavior with the fix and what would happen without it.


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.

coderabbitai[bot] avatar Dec 09 '25 02:12 coderabbitai[bot]