Files

186 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.ps1``ift-loadtest-restore.sh`
Прогон без п.1–4 не считается завершённым.
### Auth path (smoke / full)
1. `POST /v1/admin/login` (admin-api) — учётка из `ift-admin.csv`
2. `POST /v1/register``user.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.sh``ift-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
```bash
# из /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
```bash
# из корня 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)
```bash
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` или оркестратор:
```bash
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)
```powershell
# 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` приложения):
```bash
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`.