Repository navigation
perf(cli): Cache persisted session catalogs - #8892
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
E2E Test ReportEnvironment: macOS arm64, Node.js v22.22.3, npm 10.9.8, global qwen 0.21.8 for the baseline, source daemon for the implementation, and isolated temporary runtime/workspace directories. No user session directory was read or modified. The empty-runtime baseline sent five concurrent list variants to global qwen; every request returned HTTP 200 in approximately 1–8 ms.
After the 1,000-session run, daemon status reported event-loop mean 21.2 ms, p99 24.1 ms, and max 33.8 ms. Response envelopes retained only the existing Deterministic verification passed for the cache state machine (9 tests), focused REST/session-list coverage (80 tests), ACP list and mutation coverage (27 tests), LiveTask coverage (14 tests), and selected multi-workspace/runtime-isolation coverage (7 tests). The five concurrent query variants performed one complete catalog load and one sidecar pass; warm hits performed neither, while live and organization changes remained immediately visible. Prettier, ESLint, and |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
[codex] Thanks — keeping the 2s TTL design. The E2E report above already records 300- and 1,000-session cold-concurrent and warm p50/p95 results, plus deterministic catalog/sidecar load counts; while that is not a same-dataset base-build A/B, it measures the burst path this PR targets. The TTL intentionally covers sequential reload bursts beyond exact promise overlap, and the listed internal maintenance removal paths deliberately rely on the documented two-second bound under the approved consistency boundary. No code change is needed for this review. |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Review follow-up completed on
Verification: 25 cache/LiveTask tests passed; 3 targeted server mutation/telemetry tests passed; 101 multi-workspace tests passed; changed-file ESLint passed. The combined server + multi-workspace run had 1 unrelated flaky registry-source assertion among 1,016 tests, which passed immediately in isolation. Full build remains locally blocked by pre-existing Ink type/export mismatches in selection UI files; the prior PR SHA was fully green and the new test-only SHA has fresh CI running. |
yiliang114
left a comment
There was a problem hiding this comment.
LGTM. The cache core is correct and unusually well-tested: single-flight waiters receive failures (never cached, inFlight cleared on both outcomes), TTL is non-sliding from scan completion with an independent read-path age guard, generation counters keep invalidated/superseded loads from installing or deleting newer values, keys derive from the same runtime.sessionRuntimeBaseDir + workspaceCwd on read and invalidate sides so cross-workspace collision is impossible in every current call path, reads are pinned via resolved-runtime ALS with SessionService capturing the root at construction, and memory is bounded (50k-summary cap, oversized served-not-installed, 2s lifetime). Invalidation runs after the mutation settles and before responses are sent, including ACP over-the-wire archive/unarchive/delete; the 12 bot suggestions are all dispositioned (7 fixed with tests, 2 deferred, 3 rejected with verified rationale). CI green on head.
Two P2 hardening items for follow-up, not blockers: (1) runtimeBaseDir is optional on the read options (session-list.ts:88) — all three current daemon callers pass it, but a future caller omitting it silently reads the process-default root and its invalidations would never match; make it required or assert presence. (2) Pre-existing: the ACP _qwen/sessions/{delete,archive,unarchive} handlers build SessionService without the runtime root (dispatch.ts:4337/4362/4386) unlike removeOrphanSession and the REST equivalents — now that reads are pinned to sessionRuntimeBaseDir, a secondary-workspace dispatcher with a different ambient root mutates a different root than the catalog being listed; one-line-each fix. P3s (internal maintenance deletes TTL-only, span-attribute overwrite on multi-lookup, no over-the-wire close/update_metadata test, external writers TTL-covered) are all documented, bounded tradeoffs. Ship it.
|
@qwen-code /triage |
1 similar comment
|
@qwen-code /triage |
What this PR does
This PR adds a process-local, two-second single-flight cache for the persisted session catalogs used by organized and metadata-filtered daemon session lists. Catalogs are isolated by resolved runtime root, exact workspace identity, and archive state; persisted JSONL summaries and worktree sidecars are cached, while live bridge state, organization metadata, filtering, sorting, and pagination remain request-local.
Explicit REST and ACP metadata, close, delete, archive, and unarchive operations invalidate the affected catalog states before their responses are sent. The implementation also pins each read to the selected workspace runtime, adds cache status telemetry for scans, waiters, hits, and failures, and keeps default numeric pagination and the session-info disk scan on their existing paths.
Why it's needed
Organized, pinned, source-filtered, and LiveTask session lists currently repeat the same full JSONL and worktree-sidecar scan when several requests arrive together. On workspaces with hundreds or thousands of sessions, those duplicate synchronous filesystem reads increase route latency and event-loop lag without producing different persisted data.
Sharing only the short-lived persisted catalog removes redundant work while preserving current live and organization freshness, runtime isolation, cursor behavior, truncation semantics, and public REST/ACP response shapes.
Reviewer Test Plan
How to verify
Start a daemon with an isolated runtime directory and a workspace containing persisted sessions. Send five concurrent organized-all, organized-pinned, parent/source-filtered, and LiveTask-style requests for the same active workspace; confirm that one request performs the catalog scan, the other requests join it, and all responses retain the existing wire shape.
Repeat the requests within two seconds and confirm that no JSONL or worktree sidecar is read again. Change live client/prompt/title state and organization pin/group state between requests and confirm that the next response reflects those changes without waiting for cache expiry.
Exercise metadata update, close, delete, archive, and unarchive through REST and ACP, then list the affected active and archived catalogs before the two-second TTL expires. Confirm that each mutation exposes the new state immediately, failed loaders do not serve stale data, and identical workspace paths under different runtime roots never share results.
Confirm that default numeric cursor pagination still calls storage with the requested page size and that session-info remains an independent disk scan.
Evidence (Before & After)
N/A — this is a daemon performance and consistency change with no TUI or Web Shell UI change.
Tested on
Environment (optional)
macOS arm64, Node.js v22.22.3, npm 10.9.8, source daemon plus global qwen 0.21.8 baseline, temporary
QWEN_RUNTIME_DIRand temporary workspaces only.Risk & Scope
Linked Issues
N/A
中文说明
此 PR 的作用
此 PR 为 organized 和 metadata 过滤型 daemon session list 使用的持久化会话目录增加进程内、两秒 TTL 的 single-flight 缓存。目录按解析后的 runtime root、精确 workspace identity 和 archive state 隔离;持久化 JSONL 摘要与 worktree sidecar 会被缓存,而 live bridge 状态、organization metadata、过滤、排序和分页仍在每次请求中独立计算。
显式 REST 与 ACP metadata、close、delete、archive 和 unarchive 操作会在响应发送前失效受影响的目录状态。实现还将每次读取固定到选中的 workspace runtime,增加 scan、waiter、hit 和 failure 的缓存状态 telemetry,并让默认数字游标分页与 session-info 磁盘扫描继续使用现有路径。
为什么需要此改动
organized、pinned、source-filtered 和 LiveTask session list 在多个请求同时到达时,目前会重复执行相同的完整 JSONL 与 worktree sidecar 扫描。在包含数百或数千个 session 的 workspace 中,这些重复的同步文件系统读取会增加路由延迟和 event-loop lag,却不会产生不同的持久化数据。
仅共享短生命周期的持久化目录可以消除冗余工作,同时保持现有 live 与 organization 新鲜度、runtime 隔离、cursor 行为、truncation 语义以及公开 REST/ACP response shape 不变。
Reviewer 测试计划
验证方法
使用隔离的 runtime directory 启动 daemon,并准备一个包含持久化 session 的 workspace。针对同一个 active workspace 并发发送五个 organized-all、organized-pinned、parent/source-filtered 和 LiveTask 风格请求;确认只有一个请求执行目录扫描,其余请求加入该扫描,并且所有响应保持现有 wire shape。
在两秒内重复这些请求,确认不会再次读取 JSONL 或 worktree sidecar。在两次请求之间修改 live client/prompt/title 状态以及 organization pin/group 状态,确认下一个响应无需等待缓存过期即可反映这些变化。
通过 REST 和 ACP 执行 metadata update、close、delete、archive 和 unarchive,然后在两秒 TTL 到期前列出受影响的 active 与 archived 目录。确认每个 mutation 都立即暴露新状态,失败的 loader 不会提供 stale data,并且不同 runtime root 下相同 workspace path 永远不会共享结果。
确认默认数字游标分页仍使用请求的 page size 调用 storage,并且 session-info 仍是独立的磁盘扫描。
证据(改动前后)
N/A——这是 daemon 性能与一致性改动,不包含 TUI 或 Web Shell UI 变化。
测试平台
环境(可选)
macOS arm64、Node.js v22.22.3、npm 10.9.8、源码 daemon 与全局 qwen 0.21.8 基线;仅使用临时
QWEN_RUNTIME_DIR和临时 workspace。风险与范围
关联 Issue
N/A