query-db-collection (on-demand): a second live query on an identical stale subset reuses the observer and never refetches
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 68/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- typescript
- Ambito
- database
Direzione di ricerca
Start in packages/query-db-collection/src/query and trace loadSubset when it reuses an observer for an existing hashedQueryKey. Add the provided failing test under packages/query-db-collection/tests/, then compare reuse behavior with Query Core’s refetchOnMount rules. Done when the test confirms stale subsets refetch for a second identical live query and the false, 'always', and staleTime: null cases behave as described.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
- I've validated the bug against the latest version of DB packages
Describe the bug
With syncMode: "on-demand" and staleTime: 0, a second live query with the same predicate as an already-loaded subset never refetches. It reuses the existing observer, so rows the server added after the first load never appear, in either live query.
When state.observers already has the subset's hashedQueryKey, loadSubset bumps the refcount and returns on the observer's current success result without consulting isStale. Exact-request reuse is intended (#1657 / #1797; #1935: "Live queries that ask for the same subset share its Query"). The gap is that refetchOnMount (#1665) is only forwarded when an observer is created. A reused observer never mounts, so staleTime / refetchOnMount are skipped for the second consumer. Focus and reconnect refetches still work, so this only shows up while navigating in-session. #998 covered the empty-result case; a populated stale subset still short-circuits.
Versions: @tanstack/[email protected], @tanstack/[email protected], @tanstack/[email protected]. Also reproduced on 0.15.2 / 1.4.8 and on 0.8.7 / 1.2.12.
To Reproduce
- Create an on-demand query collection with
staleTime: 0. - Mount live query A with
where customerId = 'u1'.queryFnruns and the subset loads. - The server gains a new matching row.
- Mount live query B with the identical predicate.
- See that
queryFndoes not run again and that neither A nor B contains the new row.
Failing test (vitest, for packages/query-db-collection/tests/):
import { describe, expect, it, vi } from 'vitest'
import { QueryClient } from '@tanstack/query-core'
import {
compileSingleRowExpression,
createCollection,
createLiveQueryCollection,
eq,
toBooleanPredicate,
} from '@tanstack/db'
import { queryCollectionOptions } from '../src/query'
type Row = { id: string; customerId: string }
const flush = async () => {
for (let i = 0; i < 15; i++) await new Promise((r) => setTimeout(r, 0))
}
describe(`on-demand subset reuse`, () => {
it(`refetches a stale subset when a second live query acquires the same predicate`, async () => {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
})
const serverRows: Array<Row> = [{ id: `x1`, customerId: `u1` }]
const queryFn = vi.fn(async (ctx: any) => {
const where = ctx.meta?.loadSubsetOptions?.where
if (!where) return []
const predicate = compileSingleRowExpression(where)
return serverRows.filter((row) =>
toBooleanPredicate(predicate({ ...row })),
)
})
const collection = createCollection(
queryCollectionOptions<Row>({
id: `stale-subset-reuse`,
getKey: (row) => row.id,
queryKey: [`rows`],
queryClient,
syncMode: `on-demand`,
staleTime: 0,
queryFn,
}),
)
const window = () => {
const live = createLiveQueryCollection((q) =>
q.from({ row: collection }).where(({ row }) => eq(row.customerId, `u1`)),
)
live.subscribeChanges(() => {})
return live
}
const ids = (live: { values: () => Iterable<Row> }) =>
[...live.values()].map((row) => row.id).sort()
const a = window()
await flush()
expect(queryFn).toHaveBeenCalledTimes(1)
expect(ids(a)).toEqual([`x1`])
serverRows.push({ id: `x2`, customerId: `u1` })
const b = window()
await flush()
// Fails: queryFn stays at 1 call and both windows stay at [`x1`].
expect(queryFn).toHaveBeenCalledTimes(2)
expect(ids(a)).toEqual([`x1`, `x2`])
expect(ids(b)).toEqual([`x1`, `x2`])
})
})
Actual on 1.5.1: queryFn is called once, and both windows are ["x1"].
Expected behavior
Acquiring an existing subset applies the same freshness rule as mounting a new QueryObserver. It refetches when refetchOnMount allows it (true with a stale result, or 'always'), and does nothing for false or a fresh result. After step 4, queryFn has run twice and both live queries contain x1 and x2.
Additional context
Not platform-specific: it reproduces in plain Node with the test above. We hit it in a React Native app (Hermes), where two screens filter the same subset.
A local patch fixes it for us. When loadSubset reuses an existing observer, it evaluates the rule Query Core applies on mount: enabled, then refetchOnMount (a value or a function), against the current result's isStale / isFetching. If that calls for a refetch, it returns refetchAndWaitForApplication(observer, hashedQueryKey) and deduplicates concurrent acquisitions of the same key. With it, this test and the refetchOnMount: 'always' / false and staleTime: null variants behave as with a fresh observer. Happy to open a PR.
- Lingua principale
- TypeScript
- Stelle
- 4k
- Fork
- 277
- Merge medio
- 1g 11h
- PR unite (30g)
- 148
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di TanStack/db
-
Difficoltà 1/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
OfflineExecutor.dispose() never settles pendingTransactionPromisesForse di nuovo libera @Claire-ovo l’ha presa 11 giorni fa e non c’è nessuna pull request aperta. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
TanStack/db#2254 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Two @tanstack/db tests time out under CPU load because a cold module import counts against the 5 s default timeoutForse già presa @jp-knj l’ha presa 9 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 1 giorno
Issue simili
-
Navbar avatar and name stay stale after saving profile changesForse già presa @alexechoi l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
marmelab/atomic-crm#416 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
epam/ai-dial-react-file-manager#99 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
canonical/craft-actions#146 ·
I maintainer di solito rispondono entro 2 giorni
-
feature-request
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
bluesky-social/social-app#12343 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
lucide-icons/lucide#5165 ·
I maintainer di solito rispondono entro 1 giorno