Приветствую, любители нагрузки! Не затягивая, переходим к прошлым выпускам
Прошлые выпуски
-
Новости Perfscale № 1
-
Perfscale news #2: Fix Protocol, Magic metrics и MCP
-
perfscale news #3 Websocket, Inference, NPM
-
Perfscale news #4. GRPC, Fixed Triggers, Child Process
-
Perfscale news #5. SQL, Thresholds, неработающие Linked Accounts
-
Perfscale news #6. GraphQL, поддержка Import, и метрики
-
Perfscale news #7. Fix Import, SOAP, and more HTTP metrics
-
Perfscale news #8. WS Benchmark, Jmeter open sourced & fixed linked accounts
-
Perfscale news #9. GPU, LLM, Docker images
-
Perfscale news #10. Shared variables, Pub/Sub load testing & live metrics
-
Perscale news #11. Fine‑grained token, SQID, stdout log streaming
-
Perfscale news #12. WASM‑библиотеки, SDK для авторов и «живые» логи
-
Perfscale news #13. Выжигание WASM модулей, http3 и первый postmortem
-
Perfscale news #14. Фикс fix, api reference и поддержка webrtc
-
Perfscale news #15. Локальная документация, обновленный bench, и метрик сервер
-
Perfscale news #16. SDK Report API, оптимизации под задачи и опциональные pro модули
-
Perfscale news #17. Свои шаги на WASM (RFC 009), Node.js‑шаги, Faker и нагрузка с нескольких IP и типизированный инпут
Это восемнадцатый выпуск новостей Perfscale — открытого инструмента нагрузочного тестирования, который умеет в k6, locust и JMeter, а также имеет собственный движок. Сегодня разбираем: streaming-репортеры в Prometheus и OTLP, coding агенты, досрочную остановку по threshold и диагностику failed-запросов.
Perfscale OSS
live метрики в системе наблюдения
Раньше живой поток метрик шёл только в платформу или локальный perfscale serve. Теперь тот же поток можно отправить напрямую в Prometheus Remote Write и OTLP/gRPC collector — без обёртки над k6 и без отдельного экспортёра:
report: reporters: - use: std/report/prometheus-rw@v1 during_run: true interval_ms: 5000 labels: { env: staging } max_series: 10000 with: url: http://vm:8428/api/v1/write histogram: classic # или native batch_size: 500 max_pending: 24 retry: { max_attempts: 5, backoff: "1s..30s" } final_timeout: 30s - use: std/report/otel@v1 during_run: true with: endpoint: http://otel-collector:4317
Prometheus по умолчанию получает классические bucket{le}, sum и _count; histogram: native включает нативные гистограммы. OTLP экспортирует экспоненциальные гистограммы с кумулятивной темпоральностью. Клиентские quantile-серии не нужны: бэкенд сам считает корректные квантили по бакетам, в том числе между воркерами:
histogram_quantile(0.95, sum by (le) (rate(http_req_duration_bucket[1m])))
Главное здесь не сам факт экспорта, а то, как он устроен под нагрузкой:
-
у каждого репортера своя очередь и доставка — мёртвый VictoriaMetrics не остановит OTLP, а медленный OTLP не остановит VU;
-
батчи запечатываются по
batch_sizeили по тикуinterval_ms; -
max_pendingограничивает backlog. Переполнение выбрасывает самый старый снапшот и пишет это вperfscale_report_dropped_snapshots_total{reporter="…"}; следующий кумулятивный снапшот снова несёт полные значения; -
сетевые ошибки, 429 и 5xx ретраятся с экспоненциальным backoff и учётом
Retry-After, а другие 4xx считаются poison batch и не зацикливаются; -
max_seriesобрезает кардинальность детерминированно, со счётчикомperfscale_report_dropped_series_total; -
на завершении остаётся синхронный final flush терминального кумулятивного снапшота, ограниченный
final_timeout. Его ошибка пишется в лог, но не меняет exit code нагрузочного теста.
Labels складываются в порядке step > reporter > run. run_id и worker всегда ставит движок, поэтому распределённый прогон раскладывается по генераторам без ручной разметки.
init-agents и init-skills
В прошлых выпусках у нас появился MCP-сервер, который умеет линтить каждую запись. Теперь агента можно ещё и инициализировать в репозитории одной командой — с готовыми инструкциями, навыками и безопасной моделью запусков.
Первая команда ставит три embedded skills:
perfscale init-skills # ./.claude/skills/perfscale init-skills --loop agents # ./.agents/skills/perfscale init-skills -g # ~/.claude/skills/ — для всех проектов
Внутри:
-
perfscale-cli— YAML test/config,lint/run/schemaи справочники по действиям, профилям, порогам, метрикам, репортерам и протоколам; -
perfscale-library-sdk— WASM-шаги,defineMetric, расширения конфигурации; -
perfscale-migration-k6— миграция и соответствие метрик k6 ↔ perfscale.
Повторный запуск просто перезаписывает файлы, поэтому после обновления бинаря не остаётся старой инструкции прошлогоднего формата.
Вторая команда генерирует самих агентов:
perfscale init-agents --loop claude --promptsperfscale init-agents -c staging.config.yaml --loop codex
Поддерживаются claude (по умолчанию), codex, copilot, opencode, vscode и vscode-legacy. Для каждого loop создаются свои agent-файлы и MCP-конфиг с npx -y @perfscale/mcp, включая Windows-вариант cmd /c. Существующие MCP-конфиги сливаются, а не затираются.
Три готовые роли:
-
planner — собирает
specs/.md: поток шагов, профиль нагрузки, данные и SLO; -
generator — пишет
tests/.yamlчерез MCP write tools, линтит каждую запись и проверяет её коротким прогоном; -
healer — воспроизводит ошибку, чинит тест и перезапускает. Если сломан уже сам сервис, помечает тест
skip:с причиной, а не переписывает реальность под зелёный статус.
Отдельная история — безопасность. Generator и healer по умолчанию делают только 1 VU и короткий прогон. Полная нагрузка разрешена либо по явному запросу пользователя, либо через prompt threshold, который сначала печатает форму и просит подтверждение. Healer при этом не имеет права ослаблять thresholds: — подкрутить SLO до проходного не считается починкой.
threshold заменяет Playwright’овский coverage: ступенчато поднимает нагрузку до заданной и отвечает HOLD / NOT HOLD по каждому порогу, с наблюдаемыми значениями и местом первого слома.
abortOnFail: порог теперь может остановить прогон досрочно
Thresholds в Perfscale традиционно выполнялись в after: и выносили вердикт по итогам всего прогона. Это правильно для отчёта, но не всегда для CI: если сервис уже валится по 500-м 50 минут к ряду, то зачем гонять ещё полчаса?
Теперь на gate можно повесить abort_on_fail: true:
after: - name: slo gate use: std/thresholds@v1 with: http_req_failed: ["rate<0.05"] http_req_duration: ["p95<500"] severity: fail abort_on_fail: true http_debug: error
Что происходит:
-
раз в 250 мс чистый monitor вычисляет gate по уже собранным метрикам;
-
первое нарушение
severity: failостанавливает нагрузку — fixed, ramping и arrival режимы работают одинаково; -
остановка происходит между шагами, текущий запрос завершается, как и при Ctrl-C;
-
after:teardown, репортеры и итоговая сводка выполняются как обычно, а финальныйafter:остаётся единственной записанной оценкой порога; -
если метрики ещё не появились, monitor ждёт данные, а не падает с
unknown metric; -
обычное завершение по длительности или Ctrl-C не приводит к ложной строке об abort.
http_debug: error: тело ошибки есть, успешного шума нет
Флаг --quiet у нас уже умел прятать успешные request/check lines. Новый режим идёт дальше — это аналог предложения из grafana/k6#6618: failed requests и failed checks получают полную диагностику, включая payload (спасибо пользователю из чата QA LOAD @radomir_zvezdan https://t.me/qa_load/96300/175218).
Каждая неуспешная операция выдаёт одну замаскированную строку http_debug: {…}:
{ "vu": 1, "iteration": 17, "step": "checkout", "action": "std/http@v1", "request": { "method": "POST", "url": "https://staging.example.com/orders", "body": { "sku": "item-17", "token": "***" } }, "response": { "status": 500, "body": "upstream exploded" }}
Для failed check добавляются сами проверки и failed_checks. Для успешных запросов и checks не создаётся ни JSON, ни буфер для будущего вывода. Диагностика строится только в ветке ошибки и проходит через существующее маскирование секретов — ${{ env.* }} в логе превращается в ***.
http_debug действует только вместе с abort_on_fail: true на after: std/thresholds@v1. Другие значения и сочетания отклоняются на lint и до старта VU. И важная деталь реализации: именно эти diagnostic lines идут через гарантированный log path, а не через lossy per-request канал — специально включённая диагностика не должна исчезать в самый интересный момент.
@std/pick теперь умеет в ремаппинг переменных
Одно важное дополнение, которые в прошлом выпуске забыл подчеркнуть: это ремаппинг переменных. Например представьте, у вас есть тест: «получить список пользователей и дальше работать с одним из них». В k6 для этого пишут JSON.parse и Math.random. В perfscale хватает одного шага std/pick@v1:
steps: - name: list users use: std/http@v1 with: url: http://cluster.example.svc:8080/users outputs: users check: status: 200 - name: pick a user use: std/pick@v1 with: from: "${{ users.body }}" # тело ответа парсится как JSON на лету outputs: user # случайный элемент массива - name: get the user use: std/http@v1 with: url: "http://cluster.example.svc:8080/users/${{ user.id }}" check: status: 200
Без index элемент выбирается равномерно случайно. При заданном seed: в конфиге выбор детерминирован, и упавший прогон воспроизводится один в один. Мы через seed закрываем 2 задачи:
-
проверка на случайность. Для кадого запуска формируется свой seed (как мир в minecraft)
-
воспроизводимость — мы точно знаем, что при одном и томже seed мы всегда получим тотже элемент массива.
- name: pick my user use: std/pick@v1 with: from: "${{ users.body }}" index: "${{ __vu__ }}" # VU нумеруются с 1 index_offset: -1 # → (VU-1) % длина списка outputs: user
Кстати, std/pick довольно мощный — он изначально работал с массивами, но потом мы добавили туда аналог jq, для работы JSON. А потом поняли, что он же по сути создает новую перменную, поэтому в std/pick мы смекнули, что надо уметь работать не только с массивами и объектами, но и со строками, и уже объявленными переменными.
Pub/Sub: flush: none
Асинхронная публикация умеет быть очень быстрой — и очень молчаливой (поскольку она асинхронна). Теперь драйвер может вернуть PubSubOutcome.errors, и движок сложит это значение с синхронным признаком ошибки в pubsub_errors.
Практический эффект: за один exchange счётчик может быть больше 1. Несколько ошибок доставки не превращаются в одну условную «ошибку шага» и не исчезают из-за flush: none. При этом асинхронные ошибки остаются метрикой, а не обязательным падением шага.
В Pro-драйверах semantics приведены к одному виду:
-
Kafka —
BaseProducerдля enqueue-only, финальный flush с ограничениемmessage_timeout_msна exchange-scoped завершении, delivery errors из callback попадают вpubsub_errors; -
Redis — неатомарный pipeline, drain ожидающих ответов перед закрытием exchange-соединения, ошибочные replies тоже считаются;
-
MQTT — QoS 1/2 без ожидания ack при
flush: none, ограниченный ack drain и корректный disconnect, недобор попадает в ошибки; -
scope: vuсохраняет тёплые соединения и не делает drain на каждом обмене.
Закрытие perfscaled#6 именно об этом: Kafka, Redis и MQTT теперь уважают общий flush вместо того, чтобы всегда ждать подтверждение.
Perfscaled 0.7.0: доставка метрик и сетевые настройки агента
Агент получил ту же streaming-семантику доставки с батчингом и ограниченными очередями.
А ещё — perfscaled умеет задавать настройки сети прямо из UI.
net.ipv4.ip_local_port_range = 1024 65535net.ipv4.tcp_tw_reuse = 1
На Linux эти настройки включены по умолчанию и применяются только после успешного heartbeat. Перед изменением агент сохраняет исходные значения в /etc/perfscale/network-tuning.json с правами 0600. На странице машины видны pending, applied, disabled, error и unsupported; для конкретной машины tuning можно отключить, и сохранённые значения будут восстановлены. Не-Linux получает честный unsupported, а не фальшивое «применено».
Попробовать
Обновиться проще всего через npm:
npm install -g @perfscale/exeperfscale --version
Или через GitHub Releases v0.31.0 — там Linux, macOS и Windows
Дальше можно начать с малого:
# Установить навыки агента в текущий репозиторийperfscale init-skills# Сгенерировать planner/generator/healer и promptsperfscale init-agents --loop claude --prompts
А если сервису стало плохо — уже не обязательно ждать конца длительности: ставьте abort_on_fail: true, смотрите на конкретные failed-запросы и разбирайтесь с реальными payload’ами, а не с догадками по p99.
Не болейте, пусть ваши очереди репортеров будут короткими, thresholds — справедливыми, а p99 — низким. До следующего выпуска!
Ссылки
-
Релиз: Perfscale v0.31.0
-
abortOnFailиhttp_debug: PR #31 -
init-agents/init-skills: issue #15 и PR #32 -
Документация: Perfscale/docs#5
-
Агент: perfscaled v0.7.0
-
GitHub: github.com/Perfscale/perfscale
-
Платформа: perfscale.ru и perfscale.su
Источник: habr.com