Время ответа состоит из ожидания зависимостей, работы в других потоках и очереди к основному event loop. Только последнюю часть показывает event-loop lag.
Измерение в процессе
monitorEventLoopDelay из perf_hooks собирает гистограмму задержек между тиками цикла.
import { monitorEventLoopDelay } from 'node:perf_hooks';
const lag = monitorEventLoopDelay({ resolution: 20 });
lag.enable();
const snapshot = {
meanMs: lag.mean,
maxMs: lag.max,
p99Ms: lag.percentile(99),
};
lag.disable();
Снимок делают за интервал нагрузки, а не за один запрос. Среднее может выглядеть спокойно, пока редкие длинные паузы уже заметны клиентам, поэтому смотрите процентили и максимум.
Что означает рост
Lag растет, когда основной поток долго занят синхронным кодом, сборкой мусора, большой сериализацией или очередью микрозадач. Медленная база сама по себе обычно увеличивает время ответа, но не lag: пока Node ждет, он может обрабатывать другие события.
Поэтому сначала сопоставляют lag с HTTP-латентностью и CPU, потом ищут источник. Профиль CPU показывает горячую функцию, heap statistics - цену сборки мусора, а структура запросов - размер JSON и число одновременных задач.
Измерять после изменения
Worker считается оправданным, если lag основного процесса падает при приемлемой стоимости передачи данных. Увеличение UV_THREADPOOL_SIZE на такой lag не поможет: оно влияет только на нативный пул.
Пороговое значение зависит от SLO и нагрузки. Важна не красивая цифра в одном прогоне, а деградация под повторяемой нагрузкой и сравнение до и после.
Коротко
- Event-loop lag - задержка обработки событий основным потоком.
monitorEventLoopDelayдает среднее, максимум и процентили.- Медленная зависимость и блокировка event loop - разные причины задержки.
- Профиль, память и структура запросов помогают найти источник.
- Изменение оценивают под одинаковой нагрузкой до и после.
Что почитать дальше
- Event loop и libuv - что именно стоит в очереди.
- Память и утечки ресурсов - когда lag дает сборка мусора.