docs: clarify query cancellation semantics#1694
Conversation
📝 WalkthroughWalkthroughAdds documentation for abortable Query Collection requests, cleanup and unloading semantics, automatic mutation refetching, and preload behavior for eager and on-demand collections. ChangesQuery collection lifecycle documentation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
More templates
@tanstack/angular-db
@tanstack/browser-db-sqlite-persistence
@tanstack/capacitor-db-sqlite-persistence
@tanstack/cloudflare-durable-objects-db-sqlite-persistence
@tanstack/db
@tanstack/db-ivm
@tanstack/db-sqlite-persistence-core
@tanstack/electric-db-collection
@tanstack/electron-db-sqlite-persistence
@tanstack/expo-db-sqlite-persistence
@tanstack/node-db-sqlite-persistence
@tanstack/offline-transactions
@tanstack/powersync-db-collection
@tanstack/query-db-collection
@tanstack/react-db
@tanstack/react-native-db-sqlite-persistence
@tanstack/rxdb-db-collection
@tanstack/solid-db
@tanstack/svelte-db
@tanstack/tauri-db-sqlite-persistence
@tanstack/trailbase-db-collection
@tanstack/vue-db
commit: |
|
Size Change: 0 B Total Size: 125 kB ℹ️ View Unchanged
|
There was a problem hiding this comment.
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 `@docs/collections/query-collection.md`:
- Around line 288-293: Update the on-demand subset unloading documentation to
distinguish remaining-observer behavior from ignored-signal behavior: explain
that another observer keeps the query active until the final observer is
removed, while an unobserved request whose signal is ignored may complete and
remain inactive until gcTime.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: eb988b21-7a06-4381-bf4c-1a0e011f9e8b
📒 Files selected for processing (1)
docs/collections/query-collection.md
| On-demand subset unloading does not explicitly call | ||
| `queryClient.cancelQueries()`. It removes the subset's Query observer. If this | ||
| was the final observer and the query function consumed `ctx.signal`, TanStack | ||
| Query aborts the request. If the signal was ignored, or another observer still | ||
| uses the same exact Query key, the request may finish and remain cached until | ||
| `gcTime`. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Files matching query-collection.md:\n'
fd -a 'query-collection\.md$' . || true
file="$(fd 'query-collection\.md$' . | head -n1)"
if [ -n "${file:-}" ]; then
printf '\nLine count:\n'
wc -l "$file"
printf '\nRelevant section:\n'
sed -n '270,310p' "$file" | cat -n
fi
printf '\nNearby query cancellation docs mentions:\n'
rg -n "gcTime|cancelQueries|Query observer|observer|remaining observer|active" docs || true
# Inspect package metadata for TanStack Query version/doc source if relevant
printf '\nLock/package TanStack Query references:\n'
rg -n "`@tanstack/react-query`|`@tanstack/query-core`|tanstack/query|query-cancellation|gcTime" package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null || trueRepository: TanStack/db
Length of output: 39251
🌐 Web query:
TanStack Query 5 observer lifecycle gcTime when query becomes inactive documentation
💡 Result:
In TanStack Query v5, a query becomes inactive when it no longer has any active observers (e.g., all components using a hook like useQuery have unmounted) [1][2][3]. Once a query transitions to the inactive state, TanStack Query begins a garbage collection countdown based on the query's gcTime configuration [2][3]. Key aspects of this lifecycle include: 1. Inactive State: A query is considered inactive when it is present in the cache but is not currently being used by any observers [1][3]. Data persists in the cache in this state, allowing for instant reuse if the query becomes active again before the garbage collection timeout expires [2][3]. 2. gcTime (Garbage Collection Time): Formerly known as cacheTime in earlier versions, gcTime defines the duration (in milliseconds) that inactive query data remains in memory before being garbage collected and deleted [4][5][6]. 3. Default Behavior: The default gcTime is 5 minutes (5 * 60 * 1000 milliseconds) [1][6]. During Server-Side Rendering (SSR), this defaults to Infinity to prevent premature data removal [6]. 4. Cleanup Process: If no new observers mount for that query key before the gcTime expires, the query is removed from the cache entirely [2][6]. If a new observer mounts while the query is still inactive (i.e., before the timer expires), the garbage collection process is cancelled, and the query returns to an active state [2]. If multiple instances of a query exist with different gcTime settings, the library uses the longest duration to ensure data availability [6]. You can set gcTime to Infinity to disable garbage collection for a specific query [6].
Citations:
- 1: https://tanstack.com/query/v5/docs/framework/react/guides/important-defaults
- 2: https://tanstack.com/query/v5/docs/framework/react/guides/caching
- 3: What is an "inactive" query? query#1913
- 4: https://github.com/TanStack/query/blob/feb1efd8/docs/framework/react/guides/migrating-to-v5.md
- 5: https://tanstack-query.mintlify.app/migration/migrating-to-v5
- 6: https://tanstack.com/query/v5/docs/framework/react/reference/useQuery
Separate active-observer behavior from gcTime.
A case with another observer on the same Query key is different from ignored-signal behavior. With remaining observers, the query stays active until the last observer is removed; with an ignored signal, the unobserved request can finish and only later reach gcTime while the data is inactive.
Suggested wording
-On-demand subset unloading does not explicitly call
-`queryClient.cancelQueries()`. It removes the subset's Query observer. If
-this was the final observer and the query function consumed `ctx.signal`,
-TanStack Query aborts the request. If the signal was ignored, or another
-observer still uses the same exact Query key, the request may finish and remain
-cached until `gcTime`.
+On-demand subset unloading does not explicitly call
+`queryClient.cancelQueries()`. It removes the subset's Query observer. If
+this was the final observer and the query function consumed `ctx.signal`,
+TanStack Query aborts the request. If the signal was ignored, the request may
+finish and remain cached until the query becomes inactive and reaches `gcTime`.
+If another observer still uses the same exact Query key, the query remains
+active until that observer is also removed.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| On-demand subset unloading does not explicitly call | |
| `queryClient.cancelQueries()`. It removes the subset's Query observer. If this | |
| was the final observer and the query function consumed `ctx.signal`, TanStack | |
| Query aborts the request. If the signal was ignored, or another observer still | |
| uses the same exact Query key, the request may finish and remain cached until | |
| `gcTime`. | |
| On-demand subset unloading does not explicitly call | |
| `queryClient.cancelQueries()`. It removes the subset's Query observer. If this | |
| was the final observer and the query function consumed `ctx.signal`, TanStack | |
| Query aborts the request. If the signal was ignored, the request may finish | |
| and remain cached until the query becomes inactive and reaches `gcTime`. If | |
| another observer still uses the same exact Query key, the query remains | |
| active until that observer is also removed. |
🧰 Tools
🪛 LanguageTool
[style] ~292-~292: This phrase is redundant. Consider writing “same”.
Context: ...red, or another observer still uses the same exact Query key, the request may finish and r...
(SAME_EXACT)
🤖 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 `@docs/collections/query-collection.md` around lines 288 - 293, Update the
on-demand subset unloading documentation to distinguish remaining-observer
behavior from ignored-signal behavior: explain that another observer keeps the
query active until the final observer is removed, while an unobserved request
whose signal is ignored may complete and remain inactive until gcTime.
Source: MCP tools
|
Size Change: 0 B Total Size: 4.22 kB ℹ️ View Unchanged
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/db/skills/meta-framework/SKILL.md (1)
76-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMake the source collection’s on-demand mode explicit.
This example reuses
todoCollectionwithout showing or stating that it was created withsyncMode: "on-demand", so readers may not understand why directpreload()is a no-op. Add the configuration or a concise comment linking to it.🤖 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/skills/meta-framework/SKILL.md` around lines 76 - 104, Update the “On-demand Query Collection preload” example to explicitly show that todoCollection is created with syncMode: "on-demand", or add a concise comment linking that configuration to the preload behavior. Keep the existing activeTodos, loader, and useLiveQuery flow unchanged.
🤖 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/skills/meta-framework/SKILL.md`:
- Around line 76-104: Update the “On-demand Query Collection preload” example to
explicitly show that todoCollection is created with syncMode: "on-demand", or
add a concise comment linking that configuration to the preload behavior. Keep
the existing activeTodos, loader, and useLiveQuery flow unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5530cb53-c421-47cf-a873-6793535dddc4
📒 Files selected for processing (4)
packages/db/skills/db-core/collection-setup/SKILL.mdpackages/db/skills/db-core/collection-setup/references/query-adapter.mdpackages/db/skills/db-core/mutations-optimistic/SKILL.mdpackages/db/skills/meta-framework/SKILL.md
This documents how Query Collections interact with TanStack Query cancellation, observer removal, and cache cleanup. It also aligns the repository skills with the corrected lifecycle, mutation-refetch, and preload behavior.
Root cause
The earlier wording treated Query Collection's lack of an explicit
cancelQueries()call during subset unload as proof that the request would continue. Query Core can still cancel the request when removing the subset's observer removes the final observer and the query function consumed its abort signal.The cleanup wording also treated Query cache entries as collection-owned. Explicit cleanup only visits keys the collection still tracks, and exact Query keys are shared by all consumers of the same
QueryClient.Approach
QueryFunctionContext.signalto an abortable client.cancelQueries(), while noting when Query Core will still abort the request.ctx.signaland distinguish eager collection preload from on-demand live-query preload.Key invariants
ctx.signal.gcTime.QueryClientand exact Query key, not to one collection.Non-goals
Trade-offs
The documentation describes the current tracking model instead of changing cleanup to retain unloaded key metadata. Changing that lifecycle policy would require a separate design and implementation review.
Verification
Run for this branch:
pnpm vitest run tests/query.test.ts -t 'should call refetch based on handler return value|should transition to ready immediately in on-demand mode without loading data|query cancellation and subset cleanup lifecycle' --pool-options.threads.maxThreads=2 --typecheck.enabled=false pnpm prettier --check docs/collections/query-collection.md packages/db/skills/db-core/collection-setup/SKILL.md packages/db/skills/db-core/collection-setup/references/query-adapter.md packages/db/skills/db-core/mutations-optimistic/SKILL.md packages/db/skills/meta-framework/SKILL.md git diff --checkThe focused suite passed 8 tests.
The original PR also listed these broader repository checks:
pnpm test:docs pnpm lint pnpm test:sherif pnpm build pnpm --filter @tanstack/query-db-collection testFiles changed
docs/collections/query-collection.md— adds and corrects Query Collection cancellation and cleanup guidance.packages/db/skills/db-core/collection-setup/SKILL.md— forwards request signals and routes detailed cancellation guidance.packages/db/skills/db-core/collection-setup/references/query-adapter.md— documents cancellation, tracked-key cleanup, shared cache entries, and automatic mutation refetch.packages/db/skills/db-core/mutations-optimistic/SKILL.md— removes contradictory manual-refetch advice.packages/db/skills/meta-framework/SKILL.md— distinguishes eager source preload from on-demand live-query preload.Closes #350
Related design work: #1657 and #1573
Summary by CodeRabbit
AbortSignalprovided to query functions, including forwarding the signal tofetch.liveQuerypreload flow.