Progressive collection live queries remain suspended after their requested subset finishes loading
维护者通常 1 天内回复
已经有一个关联 PR 被合并了。
- #2115 来自 @KyleAMathews —— 已合并
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 25/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 停滞
- 技术栈
- node.js, typescript
调研方向
Start by reading the progressive on-demand sync and live query readiness paths in the @tanstack/db package, then run the standalone reproduction from the issue with Node.js 24. Trace how subset completion and outstanding query subsets affect readiness and preload(). Done means the requested subsets make the query ready and resolve preload() while source full sync is pending, with unrelated outstanding subsets still awaited; an open linked pull request already addresses this issue.
由索引模型根据 Issue 内容生成。
描述
- I've validated the bug against the latest version of DB packages (
@tanstack/[email protected], tested 2026-10-12).
Describe the bug
A filtered root live query remains unready after its requested on-demand subset has completed and the matching row is already present in query.toArray. query.preload() remains pending until the source collection calls markReady().
We encountered this with Electric progressive collections and useLiveSuspenseQuery: a detail page's loading fallback waited for the entire source collection to finish syncing, instead of rendering after its requested subset was available. The reproduction below isolates the readiness behavior in @tanstack/db; it does not require React or an Electric server.
To reproduce
Use Node.js 24 and an otherwise empty directory:
yarn init -y
yarn add @tanstack/[email protected] [email protected]
# Save the following code as repro.mjs:
node repro.mjs
The custom sync adapter models the relevant progressive-sync lifecycle: it publishes the requested subset and resolves loadSubset, while deliberately withholding the source's full-sync markReady() until after the first observation. Electric exposes progressive mode to DB as an on-demand source.
import assert from 'node:assert/strict';
import {setTimeout as delay} from 'node:timers/promises';
import {createCollection, eq, liveQueryCollectionOptions} from '@tanstack/db';
// Model Electric progressive sync: the requested subset can finish before
// background full sync calls markReady(). No Electric server is needed.
const subsetRequested = Promise.withResolvers();
const subsetFinished = Promise.withResolvers();
let publish;
let finishFullSync;
const source = createCollection({
getKey: (row) => row.id,
syncMode: 'on-demand',
gcTime: 0,
sync: {
sync: ({begin, write, commit, markReady}) => {
finishFullSync = markReady;
publish = async () => {
begin();
write({type: 'insert', value: {id: 1, uid: 'target'}});
await commit();
};
return {
loadSubset: () => {
subsetRequested.resolve();
return subsetFinished.promise;
},
cleanup: () => {},
};
},
},
});
const query = createCollection(liveQueryCollectionOptions({
query: (q) => q.from({item: source})
.where(({item}) => eq(item.uid, 'target'))
.findOne(),
gcTime: 0,
}));
let preloadResolved = false;
const preload = query.preload().then(() => { preloadResolved = true; });
try {
await subsetRequested.promise;
await publish();
subsetFinished.resolve();
// Allow asynchronous query processing to settle. Full sync is deliberately
// held until after we capture this state; no network timing is involved.
await delay(100);
const beforeFullSync = {
requestedRows: query.toArray.map(({id, uid}) => ({id, uid})),
sourceReady: source.isReady(),
queryReady: query.isReady(),
preloadResolved,
};
console.log('After requested subset completes:', beforeFullSync);
finishFullSync();
await preload;
console.log('After full sync completes:', {queryReady: query.isReady(), preloadResolved});
assert.deepEqual(beforeFullSync.requestedRows, [{id: 1, uid: 'target'}]);
assert.equal(beforeFullSync.sourceReady, false);
assert.equal(beforeFullSync.queryReady, true,
'Query should be ready after its requested subset completes, before full sync');
assert.equal(beforeFullSync.preloadResolved, true);
} finally {
await query.cleanup();
await source.cleanup();
}
Actual behavior
Reproduced with @tanstack/db 0.12.3 and 0.13.0:
After requested subset completes: {
requestedRows: [ { id: 1, uid: 'target' } ],
sourceReady: false,
queryReady: false,
preloadResolved: false
}
After full sync completes: { queryReady: true, preloadResolved: true }
AssertionError: Query should be ready after its requested subset completes, before full sync
Expected behavior
After the query's requested subset has completed, the query should become ready and preload() should resolve even while the progressive source's full background sync is still running. It should still wait for any outstanding subsets required by the query; the presence of a row alone is not enough to indicate readiness.
Environment
- macOS, Node.js 24.21.0, Yarn 4.5.0.
- Standalone reproduction:
@tanstack/db0.12.3 and 0.13.0. - Original React/Electric application:
@tanstack/db0.12.3,@tanstack/react-db0.5.7,@tanstack/electric-db-collection0.5.8, Microsoft Edge.
- 主要语言
- TypeScript
- 星标
- 3.9k
- 派生
- 272
- 平均合并
- 1 天 1 小时
- 30 天内合并 PR
- 235
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
TanStack/db 的其他 Issue
-
难度 3/5 半天 新手友好度 62/100
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 8/100
维护者通常 1 天内回复
-
难度 3/5 半天 新手友好度 58/100
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 5/100
维护者通常 1 天内回复
-
难度 4/5 3-5 天 新手友好度 25/100
维护者通常 1 天内回复
相似的 Issue
-
Create/import wallet form shows raw "password must be at least 1 characters" instead of the translated message可能已有人在做 @CapThunder19 今天认领。 未关闭bug
难度 2/5 1-3 小时 新手友好度 82/100
joinmarket-webui/jam#1538 ·
维护者通常 1 天内回复
-
t0ggles
难度 2/5 1-3 小时 新手友好度 70/100
nolimits4web/swiper#8240 · 1 条评论 ·
-
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 72/100
modelcontextprotocol/inspector#2723 ·
维护者通常 1 天内回复
-
status:approved type:bug
难度 1/5 1 小时以内 新手友好度 70/100
维护者通常 1 天内回复