Orca 稳态 Worktree 重扫优化:基于 Git-admin 指纹门控的免子进程缓存刷新 Orca 稳态 Worktree 重扫优化基于 Git-admin 指纹门控的免子进程缓存刷新【免费下载链接】orcaOrca is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop, mobile and remote runtime.项目地址: https://gitcode.com/GitHub_Trending/orca48/orca导读本文深入剖析 Orca 在OrcaRuntimeService.listRepoWorktreesForResolution主进程 worktree 解析缓存上采用的一项稳态优化方案通过读取仓库 Git 管理目录admin 目录的廉价文件系统指纹在不拉起git worktree list子进程的前提下证明工作树状态没有变化从而在稳态下把周期性的全仓库扫描扇出替换为一批 stat/readdir/readFile。你将理解该问题的根因并非某个请求主动清掉缓存、指纹的构成与判定逻辑、代码落地细节、一致性边界与实测效果并能基于 docs/reference/worktree-scan-fingerprint.md 与仓库源码复现整条决策链。问题背景稳态下的 Git 子进程风暴设计文档记录了一个来自生产环境的 trace10 个已注册仓库、3 小时 27 分钟的窗口内共产生4,272 次git worktree调用、8,663 个 Git 子进程总数。这些调用以全舰队一轮的形式出现周期约30.5 秒一次——即每个仓库每个WORKTREE_SCAN_CACHE_TTL_MS窗口执行一轮扫描。最初的报告假设是某个终端/状态/编排请求使 30 秒缓存失效但根因分析推翻了这一假设而这直接决定了修复方向。到底是谁过期了缓存真正的问题出在三级缓存的不同 TTL 与高频轮询者叠加常量定义见 src/main/runtime/orca-runtime-postlude.tsresolvedWorktreeCache整个舰队快照TTL 仅为1 秒RESOLVED_WORKTREE_CACHE_TTL_MS 1000。任何轮询频率高于 1 Hz 的调用方都会强制重算快照computeResolvedWorktrees会向每一个注册仓库扇出逐仓库调用listRepoWorktreesForResolution每个仓库的调用由worktreeScanCache支撑其 TTL 为30 秒WORKTREE_SCAN_CACHE_TTL_MS 30_000定义见 src/main/runtime/runtime-worktree-scan-cache.ts。一旦过期下一次轮询就不得不拉起子进程。因此没有任何请求主动过期30 秒缓存——它只按墙钟时间自然过期过期后第一个到达的轮询者就要承担整轮全仓库git worktree list扇出。那些高频调用方无 selector 的listTerminals、showTerminal、getWorktreePs、listManagedWorktrees、resolveWorktreeSelector、编排权威刷新等只决定由谁来付账而不决定付账频率。稳态子进程流量恒为repos / 30 s与轮询频率无关——这正是观测到的每 30.5 秒一轮。30 秒 TTL 存在的唯一理由Orca 内部的变更面其实已经是事件驱动的create、remove、rename、folder-rename、sparse 编辑、仓库 add/update/remove、SSH 重连、混合版本远程失效等都会调用invalidateWorktreeScanCacheForRepo/invalidateResolvedWorktreeCache约40 个调用点。于是 30 秒 TTL 存在的唯一理由只剩一个发现 Orca 之外发生的工作树变更——git worktree add/remove/move/prune、在另一个 worktree 里执行git checkout、直接用rm -rf删掉 worktree 目录等。这本质上是文件系统问题而文件系统完全可以在不拉起子进程的情况下回答它。这就是整篇设计的出发点。目标与非目标目标完整继承自 docs/reference/worktree-scan-fingerprint.md消除稳态下周期性的全仓库git worktree list扇出保持外部创建/删除/移动/prune/lock-unlock/重新 checkout 的工作树约30 秒的发现延迟不变保留一个有界的对账bounded reconciliation让廉价探针观察不到的变更仍能收敛对 SSH 仓库、WSL 路由仓库、文件夹工作区、bare 仓库、以及探针无法解析 Git admin 布局的主机行为零改动故障开放fail open探针任何错误都必须与现状等价——即真正执行扫描。非目标不修改RESOLVED_WORKTREE_CACHE_TTL_MS或全舰队快照的结构不动 src/main/ipc/worktrees.ts 中面向渲染进程的worktrees:list/worktrees:listAllIPC 扫描缓存独立 5 秒缓存由registerWorktreeChangeInvalidator失效并非 trace 中的轮询源不把resolveWorktreeSelector限定到单一仓库见被否决的备选方案不为每个仓库新增文件系统 watcher不做任何 wire/RPC/持久化 schema 变更。指纹设计用文件系统状态回答有没有变核心思路为本地仓库引入一个廉价、免子进程的 Git worktree admin 指纹在 TTL 已过期的扫描路径上先读取并比对指纹指纹未变就延续缓存、跳过子进程。指纹的输入集合入口函数readRepoWorktreeAdminFingerprint(repoPath)位于 src/main/runtime/repo-worktree-admin-fingerprint.ts。它先免子进程解析仓库的 Git common 目录读取.git若是gitdir:文件则跟随之再解析其commondir若.git不存在则将repoPath视为 bare gitdir随后记录下表输入输入能捕获的外部变更commonDir/worktrees下排序后的条目名worktree add、worktree remove、worktree prunerepoPath是否存在主 checkout 被删除commonDir/packed-refs的 mtime size松散 ref 被 pack 走期间移动的 tipcommonDir/reftable的 mtime sizereftable 后端下移动的 tip每个 checkout 的HEAD内容分支切换、detachdetached 的 oid 就写在 HEAD 里每个 checkout 中 HEAD 所指向 ref 的内容普通git commit、reset、fetch移动 tip每个条目gitdir的内容worktree move、worktree repair每个条目locked是否存在worktree lock/unlockgitdir指向路径是否存在用rm -rf删除 worktree 目录翻转prunable状态每个 checkout覆盖主 worktree 与每个 linked worktree。读取 HEAD 指向的 ref tip 是让普通 commit 可见的关键commit 重写refs/heads/branch而不动HEAD却会改变git worktree list --porcelain输出的 oid。symref 目标只在它是refs/下的相对路径时才被跟随——防止手工编辑过的HEAD把探针引到 ref store 之外源码中由isSafeRefName保证见 src/main/runtime/repo-worktree-admin-fingerprint.ts。指纹的每个输入都是对已经很热的 inode做的一次stat、readdir或小readFile并且指纹只依赖仓库路径——不注入任何历史扫描结果。每个仓库的并发探针上限为8LINKED_WORKTREE_PROBE_CONCURRENCY镜像SPARSE_CHECKOUT_DETECTION_CONCURRENCY。一个 10 仓库 × 每仓库 10 个 worktree 的舰队每个 30 秒窗口只花费几百次文件系统调用而不再是 10 次进程 spawn——进程表抖动正是报告中的症状。从实现看指纹是一个NUL 分隔的字符串FIELD_SEPARATOR \u0000因为 NUL 既不会出现在路径也不会出现在 Git ref 中字段边界天然无歧义缺失字段以-占位。任何读取失败都会使整体返回null调用方把null视为无法证明未变化。缓存判定逻辑listRepoWorktreesForResolution在TTL 已过期路径上增加一个分支文档中的判定树原文如下cached entry exists, same generation runtimeKey, TTL expired └─ probe eligible? (no connectionId, no wslDistro, fingerprint recorded) ├─ no → real scan (todays behaviour) └─ yes → read fingerprint now ├─ null or different → real scan ├─ equal, last real scan 5 min → extend TTL, no subprocess └─ equal, last real scan ≥ 5 min → real scan (bounded reconcile)WORKTREE_SCAN_ADMIN_RECONCILE_INTERVAL_MS 5 * 60_0005 分钟与既有WORKTREE_SCAN_AGENT_SCRATCH_TTL_MS先例保持一致见 src/main/runtime/orca-runtime-postlude.ts一次扫描结果若为失败ok: false该缓存条目永不被延续——瞬时 Git 故障仍会按 30 秒 TTL 重试。代码落地走读从常量到缓存接线探针资格判定什么仓库可以走指纹refreshRepoWorktreeScan见 src/main/runtime/orca-runtime-refresh-repo-worktree-scan.ts中fingerprintCapable需要同时满足三个条件无 SSH connection id注意这里用getRepoSshConnectionId解析执行宿主而非读裸字段——仅带executionHostId: ssh:*的行同样在远端跑 Git给它做本地指纹会去 stat 客户端路径该仓库的扫描 TTL小于对账间隔agent-scratch 仓库的 TTL 已达 5 分钟、等于对账间隔读指纹纯属浪费直接被排除无wslDistroWSL 路由仓库同样在别处执行 Git。SSH / WSL 仓库、文件夹工作区等原本就走真实扫描的路径完全不变。指纹在扫描前发起为什么顺序重要探针在扫描之前发起startRepoWorktreeAdminFingerprintProbe。原因是时序上的正确性存储在扫描结果上的指纹必须在扫描运行前捕获。如果变更发生在扫描执行期间存储的指纹就会结构性过期——下一次探针比对会发现差异并重扫而若在扫描之后捕获那次变更会被永久掩盖直到 5 分钟对账截止。指纹结果是与缓存条目绑定的真正走扫描的路径不会等待探针冷读不能背上文件系统延迟探针结果通过adminFingerprintProbe的 promise 在扫描完成后异步回填见 src/main/runtime/orca-runtime-list-known-resolved-worktrees-for-explicit-target.ts 的缓存条目写入逻辑。单飞探针与超时回退为避免病态挂载把 libuv 的全部 fs 线程钉死withTimeout放弃探针并不会取消它而readdir/stat不接受 AbortSignal实现用一个worktreeAdminFingerprintProbes集合保证每个仓库同时只有一个探针在途见 src/main/runtime/orca-runtime-refresh-repo-worktree-scan.ts。探针等待还受超时约束WORKTREE_SCAN_ADMIN_FINGERPRINT_TIMEOUT_MS RESOLVED_WORKTREE_REPO_TIMEOUT_MS - WORKTREE_SCAN_FALLBACK_ALLOWANCE_MS。这里刻意为回退预留预算而非花在探针上——探针超时后调用方仍要在同一预算内跑git worktree list所以回退路径需要自己的余量WORKTREE_SCAN_FALLBACK_ALLOWANCE_MS 1500超时返回null即既有的无法证明未变化哨兵于是真实扫描运行。由于探针读取的是回退扫描读取集合的子集探针慢到无法在预算内完成时扫描本身也不会更快等待反而更优。缓存条目与失效语义缓存键为${repo.id}\0${getRepoExecutionHostId(repo)}的scanScopeKey条目携带generation、runtimeKey、result、expiresAt、adminFingerprint、scannedAt六个字段在途扫描也按同一 scope key 去重invalidateWorktreeScanCacheForRepo删除条目指纹一并删除并 bump generation见 src/main/runtime/orca-runtime-refresh-repo-worktree-scan.ts。因此与既有失效机制的交互是零改动的指纹只会在TTL 本要刷新条目的场景下延长该条目而所有事件驱动的失效路径仍强制下次读取走真实扫描。以notifyBranchRenamed为例它调用invalidateResolvedWorktreeCache()invalidateWorktreeScanCacheForRepo(repoId)并通知渲染进程让分支改名即刻浮出水面见 src/main/runtime/orca-runtime-refresh-repo-worktree-scan.ts。Git 版本兼容性探针读取的每条路径都是 Git 磁盘布局中远早于 2.25 基线就存在的内容Orca 的 Git 版本基线见 docs/reference/git-compatibility.md.git目录或gitdir:文件、commondir、worktrees/name/{HEAD,gitdir,locked}、packed-refs、松散refs/。reftable在 Git 2.45 才出现在旧版 Git 上它只是 stat 为缺失而缺失是一个稳定值因此无害。本改动不引入任何新 Git 命令——它只是跳过了一条既有命令。Agent-scratch 仓库已有 5 分钟扫描 TTL与对账间隔相等因此指纹门对它们永不触发、行为完全不变。新鲜度预算两种有界退化下表对比改动前后各类变更的可见延迟引自原文档变更改动前改动后Orca 发起的 create/remove/rename/sparse/仓库编辑即时事件即时事件SSH 重连 / provider generation bump即时事件即时事件外部worktree add/remove/move/prune/lock≤ 30 s≤ 30 s任意 worktree 内外部git checkout/commit/reset≤ 30 s≤ 30 s外部rm -rf worktree≤ 30 s≤ 30 s外部 sparse-checkout 模式编辑≤ 30 s≤ 5 min同一 mtime 刻度内、文件大小不变时移动 packed/reftable tip≤ 30 s≤ 5 minSSH / WSL 仓库、文件夹工作区不变不变两种退化都被对账间隔约束且都属于 Orca 自身不会发起的变更类型sparse 模式编辑对指纹不可见packed-refs/reftable 中的 tip 只能拿到 mtimesize 粗粒度戳。常量注释与 src/main/runtime/orca-runtime-postlude.ts 中的说明完全对应。主线程成本探针为何是净优化扫描与探针都是异步的都不会朴素地跑在主线程但二者在主线程上的开销并不相等。文档在 macOS 上以 1 ms 间隔采样事件循环延迟、对含 20 个 linked worktree 的仓库各跑 30 次测得wall per call主线程阻塞/次最坏单次阻塞git worktree list18.66 ms2.69 ms3.02 ms指纹探针1.66 ms0.01 ms0.04 ms原因在源码层很清晰fs/promises会把调用派发到 libuv 线程池探针约 99% 的延迟都在线程外而uv_spawn、fd/管道建立、stdout 收集与解码是真实同步的主进程工作。10 个仓库同时刷新时改动前每轮约27 ms的事件循环停顿改动后约0.1 ms——这减少的是主线程压力而不是增加它因此把任何一侧搬到 worker 线程都无济于事。残余风险UNC 路径一个注册在 UNC 路径\\wsl$\...但由本地 Windows Git runtime 执行的仓库仍会被探针命中——因为从 Orca 的视角它并非 WSL 路由。该场景下探针是正确的、且严格比它替换的子进程更廉价但其文件系统调用会像既有 sparse-checkout 探针一样跨过 9p 边界。实测效果与测试证据src/main/runtime/worktree-scan-admin-fingerprint-gate.test.ts 复现了报告的稳态场景——10 个空闲本地仓库、一个以 1 Hz 轮询的调用方、模拟 30 分钟——并统计git worktree list调用次数每 30 分钟git worktree list每小时仅 TTL改动前6001,200指纹门改动后6012090% 的削减剩余部分是有界对账。真正有外部活动的仓库仍按 30 秒节奏重扫因为指纹会翻转。外推到原始 trace 的形态10 仓库、3 小时 27 分钟4,272 次git worktree调用将降到约427 次。被否决的备选方案含设计取舍理由把WORKTREE_SCAN_CACHE_TTL_MS提到 5 分钟。一行改动、子进程削减相同但会把每一种外部变更延迟都劣化到 5 分钟包括最常见的我在终端里跑了git worktree add。指纹以同样的削减换来了不退化。对commonDir/worktrees做每仓库fs.watch。延迟更低但每个仓库都要新增常驻 watcher 句柄、继承递归 watch 的平台差异、还须自备休眠/rearm 机制。既有 watcher 基础设施是针对工作区文件的扩展到 Git admin 目录是更大、风险更差的改动却只为同样的稳态收益。把resolveWorktreeSelector限定到所属仓库。原始简报曾要求该方案。listTerminals已通过buildResolvedWorktreeFromIdlistKnownResolvedWorktreesForExplicitTarget为显式 worktree id 避开了扇出把同一思路扩展到resolveWorktreeSelector意味着拆分 runtime 中扇入最高的方法38 个调用点并为仓库子集重建 lineage 投影——实质性回归风险。指纹门落地后针对单仓库调用的剩余扇出成本只是一批 stat 而非子进程收益已不再匹配风险故作为 follow-up 而非本改动。测试计划指纹与门控各自分层验证指纹本身的正确性在 src/main/runtime/repo-worktree-admin-fingerprint.test.ts 中用真实临时目录和真实git二进制验证mock 文件系统只会复述假设无变化时多次读取结果稳定worktree add、worktree remove、worktree move、worktree lock、linked worktree 内checkout、主 worktreecheckout、任意一侧的 commit、rm -rfworktree 目录后指纹均变化git pack-refs把松散 ref pack 走后仍能追踪被移动的 tiplinked worktree 路径与主仓库路径产生相同指纹bare 仓库经由自身 gitdir 解析、仍能追踪worktree add非 Git 目录与缺失路径返回null。缓存接线层面src/main/runtime/worktree-scan-admin-fingerprint-gate.test.ts 额外固定了门控语义指纹未变越过 30 秒 TTL 仍抑制重扫、并能重新武装指纹变化在 30 秒 TTL 上重扫5 分钟对账在指纹未变时仍强制一次重扫notifyBranchRenamed事件失效仍强制立即重扫null指纹探针失败回退到扫描SSH 仓库从不咨询探针失败的扫描永不被延续并发调用方共享一个探针与一次扫描1 Hz / 10 仓库工作负载的上述测量。补充一点从源码可见的工程细节该指纹测试套件同时被列入 pr.yml 的 Windows 边界步骤见 src/main/runtime/worktree-scan-admin-fingerprint-gate.test.ts 注释以确保门控在 Windows 上的仓库路径处理保持诚实。小结这套方案把30 秒重扫一次是否真的需要跑 Git这一判断从猜看墙钟升级为查证看文件系统事件驱动的失效路径继续提供毫秒级即时性指纹探针覆盖 Orca 之外的变更发现并保留 30 秒延迟契约5 分钟有界对账兜住指纹盲区。设计文档的关键输入表、判定树、新鲜度预算、主线程测量与实测削减数据都可以在本仓库对应源码与测试文件中逐一找到落点构成一条从问题 trace → 根因 → 设计 → 实现 → 验证的完整闭环。【免费下载链接】orcaOrca is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop, mobile and remote runtime.项目地址: https://gitcode.com/GitHub_Trending/orca48/orca创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考