OpenClaw TUI 冷啟動慢?翻了一下這個 PR,cache 命中率從 24% 飛到 98%
在 GitHub 上看到一個蠻有意思的 PR,是在修 OpenClaw TUI 冷啟動時 plugin metadata 的 snapshot thrash 問題。
我之前其實有注意到冷啟動偶爾會卡一下,但一直以為是機器的問題,或是某個 skill 太肥了。沒想到根源是在 snapshot cache 的邏輯裡藏了好幾個 bug。
這個 PR 拆開來主要解決幾件事情。
Bug A 是 key mismatch
snapshot memo 的 lookup 跟 store 用的 key 不一樣。lookup 用 registryState 算,但 store 的時候用了 persisted index,裡面帶了 generatedAtMs 和 refreshReason 這種 volatile fields,所以每次算出來的 key 都不同,永遠 miss。這種 bug 最壞的地方是:不會報錯,只會讓你以為 cache 在運作,實際上完全沒用。命中率 24% 基本上等於沒有 cache。
修法很直接,store 跟 lookup 統一用同一份 registryState 算 key,volatile fields 不進 key。
Cache 結構從 single-slot 改成 8-entry LRU
這個我印象深刻,因為我在自己的 skill 開發裡也踩過類似的坑。single-slot cache 在 input 有兩種不同 shape 交替出現時,會一直互相覆蓋,等於每次都 miss。改成 LRU 之後就解決了。8 個 slot 應該夠用,冷啟動常見的 input 組合通常不超過這個數量。
eligibility 擴大 + watchedFiles 失效機制
這個改動我覺得設計得蠻細緻的。原本只接受特定 diagnostics 的 derived snapshot,大部分冷啟動 derive 直接被丟掉。現在改成只要是 non-empty derived snapshot 就接受,同時把 watchedFiles 擴到 manifest、index、source、setup、package.json 這幾個路徑。
cache hit 之前會 re-stat 這些檔案,有不一致就 evict,不一致就重建。這樣既提高命中率,又不會用到過期的 snapshot。
discovery 傳遞的改法
applyPluginAutoEnable 跟 bundled-channel 的判斷鏈條改成吃 snapshot 裡的 discovery,而不是每層重跑 discoverOpenClawPlugins。
我的理解是,discoverOpenClawPlugins 這個 function 本身就是觸發 thrash 的關鍵。以前每個葉節點都會重跑,現在統一在上層 resolve 一次,往下傳遞。PR 裡提到冷啟動從大約 685 次降到 10 次左右,這個幅度蠻大的。
workspaceDir 不一致的問題
這個 bug 比較隱性:isPluginProvidersLoadInFlight 跟 resolvePluginProviders 在處理 workspaceDir 時,沒有先 resolve env,導致 workspace-scoped provider 的 snapshot 漏掉。修法是先 resolve 好 env/workspaceDir,然後同時傳給 snapshot 跟 load base,確保兩邊看到同一個 workspace。
這個我目前在開發多個 workspace skill 的時候確實有遇到奇怪的 provider 消失問題,不知道是不是同一個 root cause。有機會可以驗證一下。
對我比較有直接影響的是 snapshot 傳遞那個改動。我現在有幾個 skill 在冷啟動時會做 provider resolution,如果以前每個 provider call chain 都在重跑 discovery,那我的啟動時間應該也有不少是這樣浪費掉的。
這種 PR 不是新功能,沒有什麼新的 UI,但實際上影響的是每次你開 TUI 的體驗。cache 命中率 24% → 98%,這個改善幅度在 cold start 場景裡是實打實的。
如果你有在開發 skill 或是自己有寫 provider,這個 PR 值得花時間看一下 key 的算法設計,還有 watchedFiles 怎麼跟 cache eviction 整合。這種模式在寫自己的 caching 邏輯時可以直接借用。
作者:Jesse