Производительность
deffector работает поверх инспектора ядра effector: он видит каждый шаг каждой транзакции. Это не бесплатно, поэтому агент аккуратно выбирает, когда писать.
Пауза, пока DevTools закрыты
По умолчанию (whileClosed: 'pause'):
- После загрузки агент пишет
graceMs— 10 секунд. Если открыть DevTools за это время, начало страницы будет в логе. - Если к этому моменту панель не подключилась, агент встаёт на паузу: отбрасывает записанное и перестаёт писать. Шаги продолжает считать только аварийная остановка.
- Когда панель подключается, запись продолжается с текущего состояния. Health пишет «История до открытия панели не записана».
- Когда панель отключается, агент ждёт 3 секунды (за это время перезапущенный service worker расширения успевает переподключиться) и снова встаёт на паузу.
Чтобы записать начало страницы, откройте DevTools и перезагрузите её. Чтобы писать всегда, задайте attachDeffector({ whileClosed: 'record' }).
На паузе хук ядра сразу возвращается, посчитав шаг для аварийной остановки: ничего не собирается, не анализируется и не отправляется. (Кнопка Остановить запись в панели — другое: панель подключена, транзакции не хранятся, но их изменения состояния складываются в базу, чтобы последующие переходы оставались верными.)
Сколько это стоит
Замеры в демо-лаборатории (demo/lab) на графах примерно из 1 000, 5 000 и 20 000 юнитов, где одно действие проходит через каждый юнит:
| Режим | Стоимость одного действия |
|---|---|
без агента (?noagent) | базовая |
| агент установлен, на паузе | примерно как без агента |
| запись, панель открыта | примерно в 3–7 раз дороже базовой |
Запись — это зарегистрировать каждый юнит, положить каждый шаг в транзакцию с лейном, родителем и значениями, найти двойные обновления, устаревшее чтение и остановленные filter, сохранить изменения сторов для перемещения во времени и отправить всё это в панель. В обычных приложениях действие — это несколько десятков шагов, и разница незаметна; на цепочке из 20 000 юнитов — заметна.
Измерить на своей машине:
npm run dev:lab # http://localhost:5178
PERF=perf.json npx playwright test e2e/perf.spec.ts # записывает числа в perf.jsonТест сравнивает ?noagent, агента на паузе (?grace=200) и запись, а ещё замеряет, сколько панель подключается и рисует граф.
Ограничения, которые держат память ровной
| Настройка | По умолчанию | Действие |
|---|---|---|
maxTransactions | 1000 | длина истории; более старые транзакции удаляются |
maxSteps | 5000 | шагов на транзакцию; остаток огромной транзакции не хранится |
| Не записывать | — | шумные транзакции хранят только изменения состояния и не отправляются |
В панели лог рисует только видимые строки, палитра ищет по последним 300 транзакциям, а Graph рисует не больше 120 юнитов, пока вы не попросите больше.
Аварийная остановка
Событие, которое вызывает само себя через sample или watcher, никогда не заканчивается: у цикла ядра effector нет ограничения, и вкладка зависает. deffector считает шаги в каждом синхронном проходе ядра; после hardStop (100 000) он обрывает распространение: каждый узел, выполненный сверх лимита, перестаёт передавать значения дочерним, ядро опустошает очередь и возвращает управление, а на следующей микрозадаче deffector возвращает связи на место. Вкладка жива, приложение продолжает работать, а:
- транзакция в логе помечается
остановлено; - Health показывает deffector остановил бесконечный цикл с самыми частыми юнитами и кнопкой Показать в Graph;
- в консоль пишется
[deffector] deffector остановил бесконечный цикл: …(на языке панели).
Состояние может быть частично обновлено циклом. Остановка работает и на паузе. hardStop: false отключает её, а большее число подойдёт, если приложение законно выполняет больше шагов за раз.
Отдельно циклы только через события находятся в статическом графе и попадают в Health ещё до того, как выполнятся.
Продакшен
Каждая точка входа через условие экспорта production становится пустышкой, а @deffector/agent/node вдобавок проверяет NODE_ENV. Ничего не подключается и не записывается. См. Установка.
Страницы без агента
Content script расширения работает на localhost и на разрешённых сайтах и открывает соединение с расширением только после того, как агент страницы подаст голос, поэтому страницы без effector ничего не теряют.