Files

12 KiB
Raw Permalink Blame History

Tsung — нагрузка EventHub

Профили

Файл Назначение
eventhub_ift_smoke.xml Короткий smoke на IFT после prepare
eventhub_ift.xml Mix: auth_browse 60% + create_flow 40%, фазы warmup/ramp/peak
eventhub_ift_stress.xml Hot-path stress: browse 70% / write 30%, ladder arrival, поиск верхней границы
eventhub_ift_stress_hold.xml Controlled hold: maxusers=100, use_controller_vm, ladder 0.2→0.1→0.05→0.035
eventhub_ift_stress_hold_hot.xml Hot ladder только 0.1→0.05→0.035, maxusers=100, use_controller_vm (stress-hold-hot)
eventhub_ift_stress_hold_beams.xml Опасно: use_controller_vm=false — slave beams раздувают concurrent (не для IFT без CONFIRM_DANGEROUS=1)
eventhub_ift_stress_bump.xml Запрещён по умолчанию (uncapped arrival; клинит WSL IFT)
eventhub_tsung.xml Legacy localhost (не для IFT)
eventhub_http.xml Legacy короткий localhost

Цель: HTTPS api.ift.eventhub.local (+ admin-api.ift.eventhub.local для seed/verify).

Правила безопасности IFT (обязательно)

IFT = Docker Swarm внутри WSL2 на Windows. Aggressive open-loop без лимитов клинит vmmem и рвёт SSH.

Правило Значение
Рекомендуемый stress eventhub_ift_stress_hold.xml (stress-hold)
maxusers 300 (hold default 100 после ×3/2022 — CPU/RAM saturation при 200)
thinktime 1 s на hot-path hold
Плановый interarrival 0.1 s (~10 VU/s); ниже — только с наблюдением
Uncapped / bump / maxusers≫300 запрещено на IFT без явного согласования
Рабочий потолок (2026-07-15, ×3, с WAF) ~10 VU/s hot-path; пересчитываем после Mnesia+loadtest-Traefik
Traefik loadtest prepare подменяет conf без WAF (LOADTEST_TRAEFIK=1); rate limit 2000/1s остаётся
Лестница реплик потолок сначала ×1, затем ×2, ×3 — отдельно (влияние репликации Mnesia)

После каждого stress (обязательно — см. EventHubSpec/WORKFLOW.md §7):

  1. Summary: python3 test/tsung/summarize-tsung-stress.py ~/.tsung/log/<stamp>/tsung.log
  2. Сбор логов IFT → exchange ift-to-dev/loadtest-<stamp>/:
    • SSH ок: \\192.168.1.116\eventhub-cursor\scripts\collect-ift-loadtest-logs.ps1 (чистит remote dump stamp после download)
    • SSH hang: restart-wsl-and-collect-logs.ps1 или wsl --shutdown на HOME-PC → снова collect
  3. Разбор: Tsung vs прошлый прогон; архив (429 / Mnesia / archive / OOM); docker-stats; метрики бэка: GET /metrics (Prometheus text) + GET /v1/admin/nodes/metrics?from=&to= (admin JWT, история в Mnesia); compose Grafana/Prom — опционально (KEEP_OBSERVABILITY=1), при scale 0 mid-run там пусто
  4. Очистка leftover на IFT: cleanup-ift-loadtest-dumps.ps1 / EventHubDevOps/scripts/cleanup-ift-loadtest-dumps.sh
  5. Отчёт в issue (#30) / строка «Результат» ниже
  6. Restore: restore-ift.ps1ift-loadtest-restore.sh

Прогон без п.1–4 не считается завершённым.

Auth path (smoke / full)

  1. POST /v1/admin/login (admin-api) — учётка из ift-admin.csv
  2. POST /v1/registeruser.id
  3. GET /v1/admin/users/:id/verification-token — спец. admin-метод
  4. POST /v1/verify
  5. POST /v1/login → user JWT

Stress hot-path

Пользователи сидятся заранее (seed-ift-users.shift-users.csv). Сессия Tsung: login → browse/write. Без admin/register/verify на VU.

Критерий верхней границы (2A):

  • error rate > 1% (4xx/5xx + nomatch + match_stop относительно числа запросов), или
  • cumulative / late-phase mean request > 500 ms (hold ≥2 мин в фазах ladder).

Разбор: python3 test/tsung/summarize-tsung-stress.py [tsung.log]verdict=BOUNDARY_HIT|UNDER_THRESHOLD.

В error rate входят и transport-ошибки Tsung (error_connect_closed, error_timeout, error_abort_max_conn_retries).

Результат 2026-07-15 aggressive (20260715-1027/): BOUNDARY_HIT — mean ~735 ms, error ~88%, maxusers=5000, SSH wedge.

Controlled hold ×1 (20260715-1200/): фазы 0.2/0.1 GREEN; 0.05 OVER (timeout 290, ~17%); overall BOUNDARY_HIT (err ~2.9%).

Controlled hold ×2 (20260715-1336/, EVENTHUB_REPLICAS=2): фазы 0.2/0.1 GREEN (mean ≈37 ms); 0.05 OVER мягче (timeout 106, ~3%); overall UNDER_THRESHOLD (err ~0.94%, mean ~64 ms).

Controlled hold ×3 (20260715-1411/, EVENTHUB_REPLICAS=3, до фиксов): connect по фазам 0.2→0.035 GREEN (0.05 mean≈40 ms); overall BOUNDARY_HIT из‑за 3772× HTTP 429 — Traefik api-ratelimit (было 5000/1m≈83 rps).

Controlled hold ×1 (20260716-2143/, sha-6d8f02d, Traefik без WAF, maxusers=100): BOUNDARY_HIT по latency — 0 errors / 0×4xx/5xx; mean ~613 ms; late ~6.9 s; hard-stop use_controller_vm на 100 concurrent. Ранняя фаза 0.2 ~55 ms (GREEN). Образ с fix ram_copies + dump_log threshold.

Controlled hold ×3 maxusers=100 (20260715-2102/, sha-7fa24e831c16): UNDER_THRESHOLD — 0 errors, mean ~115 ms, late ~331 ms; только фазы 0.2/0.1 (~4 мин); фазы 0.05/0.035 не стартовали после hard-stop use_controller_vm. nodes/metrics: CPU peak ~60%, mem_available min ~335 MiB (без клина WSL).

Controlled hold ×3 maxusers=200 (20260715-2022/): BOUNDARY_HIT (err 1.74%, timeouts 175); CPU ~100%, mem ~11 MiB; клин SSH.

Controlled hold ×3 после фиксов (20260715-1845/, maxusers=300): 0× HTTP 4xx/5xx; mean ≈47 ms; 106× error_timeout; overall BOUNDARY_HIT (error_rate 1.018%); зависание ~194 VU + клин WSL.

Под hold также: Mnesia is overloaded: {dump_log, write_threshold|time_threshold}. Краткосрочно: MNESIA_DUMP_LOG_WRITE_THRESHOLD (IFT default 50000, EventHubBack#34). Долгосрочно: меньше sync-транзакций / hot-path вне Mnesia.

Restore fallback: оркестратор → WSL SSH, при fail Windows restore-ift.ps1. Restore после collect+анализа (см. gate выше); если SSH мёртв — wsl --shutdown → collect → restore.

Подготовка учётки admin

# из /opt/eventhub-ift/.env или локальной копии
cp test/tsung/ift-admin.csv.example test/tsung/ift-admin.csv
# одна строка без комментариев: email;password
printf '%s;%s\n' "$ADMIN_SUPER_EMAIL" "$ADMIN_SUPER_PASSWORD" > test/tsung/ift-admin.csv

ift-admin.csv в .gitignore.

Пул пользователей для stress

# из корня EventHubBack; нужны DNS/TLS до IFT и docker/.env.ift
COUNT=400 bash test/tsung/seed-ift-users.sh
# → test/tsung/ift-users.csv  (email;password), gitignore

Перед прогоном (хост IFT)

EVENTHUB_REPLICAS=1 bash /opt/eventhub-ift/devops/scripts/ift-loadtest-prepare.sh
# EVENTHUB_REPLICAS=N — больше нод

Оставляет traefik + eventhub×N.

Дех-машина (WSL)

  1. DNS: api.ift.eventhub.local и admin-api.ift.eventhub.local → IP IFT.
  2. sudo apt install tsung
  3. TLS: доверить CA стенда (EventHubDevOps/ift/traefik/certs). Проверка:
    curl -sf https://api.ift.eventhub.local/health
  4. Из корня EventHubBack или оркестратор:
tsung -f test/tsung/eventhub_ift_smoke.xml start
tsung -f test/tsung/eventhub_ift.xml start
tsung -f test/tsung/eventhub_ift_stress.xml start
# оркестратор (prepare + seed при необходимости + restore):
bash ~/.cursor/eventhub/run-ift-loadtest.sh smoke|full|stress|stress-bump|stress-hold|stress-hold-hot|stress-hold-beams
# replicas: EVENTHUB_REPLICAS=1|2|3 bash ~/.cursor/eventhub/run-ift-loadtest.sh stress-hold
# фазы 0.05/0.035: stress-hold-hot (без slave beams); stress-hold-beams — только с CONFIRM_DANGEROUS=1
# Traefik без WAF в prepare: LOADTEST_TRAEFIK=1 (default); штатный WAF: LOADTEST_TRAEFIK=0
# restore fallback: если WSL SSH не отвечает — Windows OpenSSH
#   powershell -File $env:USERPROFILE\.cursor\eventhub\restore-ift.ps1

Пересчёт потолка (лестница реплик)

Цель — отделить raw capacity одной ноды от стоимости репликации Mnesia.

  1. Задеплоить образ с рабочим dump_log_write_threshold (до mnesia:start).
  2. Для каждой ступени ×1 → ×2 → ×3 (отдельно, с полным gate после каждого прогона):
    • EVENTHUB_REPLICAS=N bash ~/.cursor/eventhub/run-ift-loadtest.sh stress-hold (зелёный baseline)
    • при UNDER на 0.2/0.1 — stress-hold-hot (0.1→0.05→0.035)
  3. В отчёте #30: replicas, verdict по фазам, CPU/mem, Mnesia dump_log, образ sha-*.
  4. Не смешивать ступени в одном прогоне; не использовать beams без CONFIRM_DANGEROUS=1.

Отчёт: ~/.tsung/log/<timestamp>/. Для stress — также ~/.tsung/ift-docker-stats-*.log.

После прогона (обязательный gate)

# 1) Tsung 2A (с дев WSL)
python3 test/tsung/summarize-tsung-stress.py ~/.tsung/log/<stamp>/tsung.log

# 2) Логи стенда на шару (+ auto-cleanup remote dump этого stamp)
powershell -NoProfile -File \\192.168.1.116\eventhub-cursor\scripts\collect-ift-loadtest-logs.ps1

# 3) Разбор архива + метрики бэка:
#    curl -sk https://api.ift.eventhub.local/metrics
#    GET /v1/admin/nodes/metrics?from=&to= (admin JWT) — история узлов
# 4) Leftover dumps на IFT (все stamps)
powershell -NoProfile -File $env:USERPROFILE\.cursor\eventhub\cleanup-ift-loadtest-dumps.ps1

# 5) Restore
powershell -NoProfile -File $env:USERPROFILE\.cursor\eventhub\restore-ift.ps1

Prepare с внешним Prometheus/Grafana (не путать с /metrics приложения):

KEEP_OBSERVABILITY=1 EVENTHUB_REPLICAS=3 bash /opt/eventhub-ift/devops/scripts/ift-loadtest-prepare.sh

Настройка интенсивности

В eventhub_ift.xml / eventhub_ift_stress*.xml блок <load>: interarrival и duration фаз.
thinktime в stress XML отключён (Tsung отклоняет min="0"; дробные max тоже) — максимальная плотность для поиска потолка.

Замечания

  • Все URL в IFT-сценариях абсолютные (https://api… / https://admin-api…): после запроса на admin-api относительный путь у Tsung остаётся на том же Host → 404.
  • В smoke/full каждый VU делает admin login — нагрузка и на admin-api; stress этого не делает.
  • Из hot path убраны: dummy refresh, reports/tickets, завышенные thinktime старого eventhub_tsung.xml.