perf: stop the 5s active-sessions poll from blocking the event loop - #289
Open
vakovalskii wants to merge 1 commit into
Open
perf: stop the 5s active-sessions poll from blocking the event loop#289vakovalskii wants to merge 1 commit into
vakovalskii wants to merge 1 commit into
Conversation
getActiveSessions() backs GET /api/active, which the dashboard polls every 5s.
For Qwen agents it called findQwenSessionByPid(), which ran
execSync('lsof -a -p <pid> -Fn') — with a 2000ms timeout, once per pid — inline.
The server, the HTTP API and the browser-terminal WebSocket share one process
and one event loop, so that stalled the pty data pump: typing in the terminal
froze for up to two seconds per running agent, every five seconds. (7.15.0 moved
the other ps/lsof polls off the loop; this one was missed.)
lsof is kept — it is the only signal that can tell two sessions in the same
folder apart — but it now runs through _execFileAsync off the loop and callers
read a 30s per-pid cache, falling back to the existing cwd match until the first
refresh lands. Refreshes are de-duplicated per pid and the cache is bounded.
Also switches to argv form, so the pid is no longer interpolated into a shell
command string.
Note the measurement trap: on a machine with no running Qwen agent the hot loop
never executes, so timing this looks green either way. The added test asserts
the property structurally instead, and was verified to fail when execSync is
reintroduced.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Второй из трёх источников заморозки терминала, которые мы замерили. #287 (в RC #284) снял индекс поиска — этот про опрос активных сессий.
Проблема
getActiveSessions()обслуживаетGET /api/active, который дашборд опрашивает каждые 5 секунд. Для Qwen-агентов он звалfindQwenSessionByPid(), а тот выполнял— синхронно, на каждый pid, прямо на этом пути.
Сервер, HTTP API и WebSocket браузерного терминала живут в одном процессе и одном event loop. Значит ввод в терминале замирал до двух секунд на каждого запущенного агента, каждые пять секунд. В 7.15.0 остальные опросы
ps/lsofвынесли с цикла, а этот пропустили.Решение
lsofсохранён — это единственный сигнал, который отличает две сессии в одной папке (сопоставление поcwdтут бессильно). Убрана именно блокировка:_execFileAsyncвне цикла;cwd— тот же ответ, что дал быlsofдля одиночной сессии в папке;Про измерения — важная оговорка
Замерять это временем бесполезно: на машине без запущенного Qwen-агента горячий цикл не выполняется вообще, и любой замер выходит зелёным независимо от кода. Я сам на этом обжёгся раньше в этой же функции.
Поэтому тест проверяет свойство структурно, и я убедился, что он не пустой: временно вернул
execSync— тест упал (4 passed / 1 failed), откатил — снова 5/5.Проверка
node --test test/*.test.js→ 261 passed / 0 failed / 2 skippedtest/active-poll-nonblocking.test.js— 5 проверок: нет синхронных вызовов ни вfindQwenSessionByPid, ни вgetActiveSessions;lsofасинхронный и в argv-форме; дедупликация обновлений; ограниченность кешаЧто остаётся
Третий источник —
loadSessions()(648 мс с прогретым кешем, 11.9 с на холодную). Его правку намеренно не тащу сюда: она требует поэтапной пересборки в духе того, что #287 сделал для индекса поиска, и опирается на его помощники. Сделаю после того, как RC #284 вольётся, чтобы не разводить конфликты вdata.js.🤖 Generated with Claude Code