Концепции

Setup и teardown

Разовый setup перед нагрузкой и гарантированная очистка после — сайдкар-процессы, гейты готовности и аккуратное завершение в нативном YAML-движке

Обзор

Реальные нагрузочные тесты редко начинаются с первого запроса. Тестируемой системе может понадобиться mock-сервер, база с фикстурами или sidecar-прокси, запущенные до того, как подключится первый VU, — и кто-то должен всё это погасить после. Нативный YAML-движок умеет это из коробки: шаги before: выполняются один раз перед нагрузкой, шаги after: — один раз после, а actions std/child_process@v1 / std/kill_process@v1 управляют локальными OS-процессами на протяжении прогона.

Setup и teardown — часть open-source-движка: доступно на любом тарифе, в CLI и на платформенных машинах одинаково.

before: и after:

Обе секции живут в конфиге прогона рядом с vus / duration:

  • before: выполняется один раз, по порядку, до старта любого VU. Имя из outputs шага доступно каждому шагу теста в пространстве config — поднимите сервер, а шаги направьте на ${{ config.web.port }}. Если любой setup-шаг падает, прогон прерывается до запуска VU — сломанный setup иначе завалил бы каждую итерацию одинаково.
  • after: выполняется один раз после остановки нагрузки — при нормальном завершении, при упавшем прогоне, при упавшем before: и при Ctrl-C/SIGTERM одинаково. Падающий teardown-шаг логируется, но не прерывает остальные: очистка best-effort и предпринимается всегда.

Запуск процесса: std/child_process@v1

Порождает локальный OS-процесс и держит его живым весь прогон. Типичное место — блок before: в паре с after::

# config.yaml — раздаём фикстуры локально, нагружаем их, прибираем за собой
allow_process_actions: true

before:
  - name: web
    uses: std/child_process@v1
    with:
      command: python3
      args: ["-m", "http.server", "8080"]
      waitUntil:
        port_open: 8080
        timeout: 10s
      restart: on-failure
    outputs: web        # → ${{ config.web.port }}, ${{ config.web.pid }}

after:
  - name: stop web
    uses: std/kill_process@v1
    with: { name: web, signal: TERM }
  • Гейт готовности. waitUntil блокирует шаг, пока процесс не будет готов, — матч по захваченному выводу (stdout_contains, stderr_matches, …) или проба порта (port_open), с timeout (по умолчанию 30s) и on_timeout (fail или continue — залогировать и идти дальше). Процесс, завершившийся до готовности, валит шаг своим exit-кодом и хвостом stderr.
  • Политика рестарта. Супервизор применяет restart: never | on-failure | always до конца прогона (max_restarts, backoff_ms), логируя каждый рестарт.
  • Живые логи. stdout/stderr процесса стримится в лог прогона с префиксом {step}: — рядом с выводом нагрузки.

Остановка: std/kill_process@v1

Пара after: к child_process. Останавливайте по имени из реестра (имя шага или outputs) — предпочтительно, потому что поиск всегда находит текущий pid даже через рестарты, — или по сырому pid как best-effort запасной вариант для процессов вне реестра (только unix).

ПараметрПо умолчаниюОписание
signalTERMTERM, KILL, INT, HUP, QUIT, USR1, USR2
grace_ms5000Сколько ждать перед эскалацией до SIGKILL
treetrueСлать сигнал всей группе процессов, а не только лидеру

Остановка уже завершившегося процесса — no-op, а не ошибка. А всё, что ещё живо к концу прогона — нормальный финиш, упавший before: или Ctrl-C, — останавливается автоматически (SIGTERM с эскалацией до SIGKILL после grace-периода, вся группа процессов), так что забытый kill_process никогда не оставит висеть сервер.

Защитный гейт

Process-actions fail-closed: прогон должен явно разрешить их через allow_process_actions: true в конфиге. Список шагов из недоверенного источника не сможет тронуть процессы, пока вы это явно не разрешите — тот же гейт действует и на std/child_process@v1, и на std/kill_process@v1.

Дальше