События

Серии событий

Нагрузочная проверка обработчика: количество, скорость, потолки и обратная связь по фактической скорости.

На этой странице · 7

Серия — это N доставок одного события с заданной скоростью. Она отвечает на вопрос, который на одиночных «Тестах» не проверить: выдержит ли обработчик реальный поток и что случится, когда он перестанет успевать.

apistend trigger ONCRMDEALUPDATE --count 500 --rate 100 --watch

Тот же движок обслуживает сценарии на экране «Вебхуки»: у сценария просто заполнен идентификатор сценария, а параметры те же — событие, сколько отправить, с какой скоростью и какую долю сбоев имитировать.

Параметры

Флаги серии

ФлагЧто задаёт
--count <n>сколько событий отправить
--rate <n>скорость, событий в секунду
--error-rate <n>доля событий с имитацией сбоя отправки, проценты
--watchпоказывать прогресс до конца серии

Задать можно и одно значение из двух: --count без --rate отправит всё как можно ровнее за секунду, --rate без --count — одну секунду работы на этой скорости.

--error-rate не портит ответ приложения, а не отправляет событие вовсе: доставка создаётся, видна в журнале и сразу получает исход «сбой отправки». Так проверяется ветка обработки ошибок и лестница повторов сервиса.

Потолки

Серия обрезается по потолкам сервера, и об обрезке говорят вслух:

$ apistend trigger ONCRMDEALUPDATE --count 60 --rate 900
> Серия ONCRMDEALUPDATE: 60 событий по 500 соб/с ≈ 1 с
> Получатель: /bitrix/deal
! скорость ограничена до 500 соб/с

Значения потолков задаются переменными окружения стенда — BURST_MAX_RATE, BURST_MAX_COUNT и BURST_MAX_CONCURRENT — и видны в ответе GET /api/bursts, так что смотреть их надо у своего стенда, а не в этом тексте. В .env.example они выставлены в 500 событий в секунду, 100 000 событий на серию и 3 одновременные серии на песочницу.

Отказы разведены по причинам, потому что чинятся по-разному:

ОтветЧто значит
NO_WEBHOOK (404)нет подписки на это событие
WEBHOOK_PAUSED (409)вебхук на паузе — включите его перед запуском
NO_AGENT (409)получатель локальный, а apistend listen не запущен
TOO_MANY_BURSTS (429)достигнут потолок одновременных серий песочницы
BAD_PARAMS (400)количество или скорость не положительное число

Серия на локальный получатель без агента не запускается намеренно: иначе она насыпала бы тысячи строк в очередь и ничего не проверила.

Прогресс и фактическая скорость

С --watch CLI опрашивает прогресс дважды в секунду и перерисовывает одну строку:

> Серия ONCRMDEALUPDATE: 300 событий по 100 соб/с ≈ 3 с
> Получатель: /bitrix/deal

   50 %  150/300  99 соб/с из 100
> Серия завершена: отправлено 300 из 300 · успешно 300 · ошибок 0
  300 из 300 событий · 98 соб/с фактически

Показывается фактическая скорость, а не заказанная. Считается она по фазе отправки: после последнего события серия ещё ждёт ответы, и если бы это ожидание попало в знаменатель, скорость оказалась бы заниженной.

«Приложение не успевает»

Сервер не наливает в сокет всё, что положено по расписанию. Он следит за числом доставок, отданных агенту и оставшихся без ответа. Потолок — удвоенная заказанная скорость, но не меньше 20 доставок. Когда потолок достигнут, такт пропускается, а строка прогресса это показывает:

   75 %  375/500  21 соб/с из 50  приложение не успевает
   ...
> Серия завершена: отправлено 500 из 500 · успешно 500 · ошибок 0
  500 из 500 событий · 20 соб/с фактически · приложение не успевало 15.2 с

Это и есть главный результат нагрузочной проверки: заказано 50 событий в секунду, приложение вытянуло 20. Без притормаживания замер показывал бы скорость записи в сокет, а не скорость обработки.

Секунды в итоге — это пропущенные такты, умноженные на длину такта (сто миллисекунд), то есть сколько времени серия простояла в ожидании вашего приложения.

Локальная параллельность тоже потолок

Агент по умолчанию делает не больше 16 одновременных вызовов приложения (--concurrency). Если обработчик отвечает за секунду, выше 16 событий в секунду поток не поднимется, сколько ни заказывай, — и серия честно об этом скажет.

Итог серии

Когда отправка кончилась, серия переходит в фазу ожидания ответов: ждёт таймаут сервиса плюс две секунды запаса и только потом подводит итог. Без этой фазы в сводке недосчитывалось бы успешных доставок — приложение просто не успевало ответить на последние.

Итоговая строка собирается из фактов: сколько отправлено из заказанного, какая получилась скорость, сколько доставок осталось без ответа в срок и сколько времени приложение не успевало. Успешными считаются доставки в состоянии succeeded, неуспешными — failed, no_response и dropped.

Остановка вручную (кнопка «Стоп» на экране «Вебхуки») ставит в итог пометку «Остановлено вручную» и не ждёт таймаут: подвисшие доставки всё равно подберёт уборщик планировщика.

Состояние interrupted

Расписание серий живёт в памяти процесса API — своего таймера у каждой серии нет, такт общий на все. Перезапуск сервера это состояние теряет, и притворяться, что серия продолжается, стенд не может. Поэтому при старте все серии, оставшиеся в состоянии running, честно помечаются interrupted с пометкой «Прервана перезапуском сервера», а сценарии, которые их запускали, возвращаются в ready.

Итоговых чисел у такой серии нет: сколько успело уйти, видно по журналу доставок её событий. Прерванная серия не возобновляется — запустите её заново.

Подводные камни

  • Событие серии — то же, что и одиночное. Порядковый номер меняет идентификаторы в теле, но структура одна; уникальные данные для каждой доставки серия не выдумывает.
  • Серия не запускается поверх идущего сценария. Второй запуск того же сценария отвечает 409: две серии с одним идентификатором писали бы прогресс вдвоём, а полоса на экране показывала бы одну.
  • Удаление вебхука посреди серии останавливает её с пометкой «Вебхук удалён во время серии», а его доставки уходят вместе с ним каскадом.
  • Bitrix24 в серии с --error-rate не повторяет ничего. Все имитированные сбои сразу терминальны — у сервиса нет повторов.