Perfscale news #18. CLI Агенты, thresholds с остановкой и OTEL метрики

Приветствую, любители нагрузки! Не затягивая, переходим к прошлым выпускам

Прошлые выпуски

  • Новости 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-конфиги сливаются, а не затираются.

Три готовые роли:

  1. planner — собирает specs/.md: поток шагов, профиль нагрузки, данные и SLO;

  2. generator — пишет tests/.yaml через MCP write tools, линтит каждую запись и проверяет её коротким прогоном;

  3. 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 задачи:

  1. проверка на случайность. Для кадого запуска формируется свой seed (как мир в minecraft)

  2. воспроизводимость — мы точно знаем, что при одном и томже 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

0 0 голоса
Рейтинг новости
1
0
Подписаться
Уведомить о
0 комментариев