perf: cut idle CPU from leaked watchers and unconditional polling - #35
Merged
Merged
Conversation
The main process pegged ~53% CPU continuously while idle. Three causes: - main.js: each subagent live-tail watcher used fs.watchFile, which stat-polls the file once per second per watcher, forever. Teardown via fs.unwatchFile(path) was fragile and watchers could accumulate across a long app session. Switch to event-driven fs.watch + 300ms debounce, and store a per-watcher teardown() closure called from both stop-subagent-watch and the window-closed handler so nothing leaks. Falls back to a 10s poll only if fs.watch fails to attach. - public/app.js: pollActiveSessions ran every 3s unconditionally (IPC + full-sidebar querySelectorAll) even with zero running sessions. Poll adaptively: 3s while sessions run, 30s when idle. In-renderer session starts re-arm the fast cadence immediately; the 30s floor still catches externally-started sessions. The 30s timeago interval now no-ops when nothing is active. - schedule-runner.js: scanSchedules re-read 4KB of every project JSONL every 60s just to extract projectPath. Resolve it from the cache_meta SQLite cache (getAllFolderMeta) instead, falling back to the JSONL read only when a folder is genuinely uncached. Lint clean; existing 119-test suite passes unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The main process consumed ~53% CPU continuously while idle (observed after a 5h+ session), with renderer processes adding ~33%/~25%.
Root causes & fixes
1.
main.js—fs.watchFilestat-polling + watcher leak (main fix)Each subagent live-tail watcher used
fs.watchFile, which stat-polls the file once per second, per watcher, for the lifetime of the process. Teardown viafs.unwatchFile(path)was fragile and watchers could accumulate across a long session.fs.watch+ 300ms debounce (mirrors the existing projects watcher)teardown()closure called from bothstop-subagent-watchand the window-closed handler — watchers can no longer leakfs.watchfails to attach2.
public/app.js— unconditional 3s pollingpollActiveSessionsran every 3s (IPC + full-sidebarquerySelectorAll) even with zero running sessions.3.
schedule-runner.js— re-reading all JSONLs every 60sscanSchedulesread 4KB of every project JSONL every minute just to extractprojectPath.cache_metaSQLite cache (getAllFolderMeta), falling back to the JSONL read only when a folder is genuinely uncachedTesting
eslintclean (no new errors)node --testsuite: 119 pass / 0 fail, unchanged