docs: update project context and README with 20x2 gradual load test
This commit is contained in:
@@ -249,3 +249,23 @@ curl -k -X PUT "https://stend.4mont.ru/api/admin/users/2/acl" \
|
|||||||
- Нет отдельной UI-админки (есть admin API).
|
- Нет отдельной UI-админки (есть admin API).
|
||||||
- TTL неактивности основан на heartbeat runtime-страницы.
|
- TTL неактивности основан на heartbeat runtime-страницы.
|
||||||
- Для production стоит добавить Alembic-миграции, rate limiting и централизованный логинг.
|
- Для production стоит добавить Alembic-миграции, rate limiting и централизованный логинг.
|
||||||
|
|
||||||
|
## 8. Нагрузочное тестирование (актуально на 2026-04-24)
|
||||||
|
|
||||||
|
В проекте подтвержден рабочий плавный профиль нагрузки без резкого burst:
|
||||||
|
- +1 пользователь в минуту;
|
||||||
|
- целевой online: 20 пользователей;
|
||||||
|
- каждый запускает 2 WEB-сервиса (termidesk и vmmanager).
|
||||||
|
|
||||||
|
Результат прогона 20x2:
|
||||||
|
- логин и открытие сервисов: 100% успешных проверок;
|
||||||
|
- HTTP 5xx в тесте: 0;
|
||||||
|
- достигнуто 40 ACTIVE WEB-сессий (20 + 20).
|
||||||
|
|
||||||
|
Важно для повторения теста:
|
||||||
|
1. Перед прогоном временно увеличьте idle timeout (SESSION_IDLE_SECONDS) и пересоздайте WEB-пул, чтобы слоты получили новый IDLE_TIMEOUT.
|
||||||
|
2. Параллельно собирайте метрики хоста (uptime/free/df/docker stats) в файл.
|
||||||
|
3. После прогона проверяйте согласованность: ACTIVE-сессии в БД vs реально существующие portal-webpool-* контейнеры.
|
||||||
|
|
||||||
|
Замеченный риск:
|
||||||
|
- при агрессивном авторасширении/сжатии WEB-пула возможны ACTIVE-сессии, привязанные к уже удаленным слотам.
|
||||||
|
|||||||
@@ -355,3 +355,138 @@ git push https://ruslan%40ipcom.su:utOgbZ09ruslan@git.ruslan.xyz/ruslan/Stend_mo
|
|||||||
4. Операционная польза:
|
4. Операционная польза:
|
||||||
- Быстрее диагностируются причины `504`/обрывов/закрытий;
|
- Быстрее диагностируются причины `504`/обрывов/закрытий;
|
||||||
- Проще фильтровать инциденты по `req_id` и `session_id` в `docker compose logs api`.
|
- Проще фильтровать инциденты по `req_id` и `session_id` в `docker compose logs api`.
|
||||||
|
|
||||||
|
## 19) Обновления (2026-04-23, лимиты + нагрузка)
|
||||||
|
|
||||||
|
1. Исправление гонки лимитов активных сервисов:
|
||||||
|
- Зафиксирован кейс, когда при параллельных открытиях сервисов одним пользователем лимит мог временно обходиться (наблюдалось до 8 активных сервисов).
|
||||||
|
- Причина: проверка лимита выполнялась вне критической секции.
|
||||||
|
- Исправление: в `go_service` добавлена пользовательская advisory-lock секция `allocator_lock(db, 92000 + user.id)`, внутри которой выполняются:
|
||||||
|
- проверка существующей сессии по сервису,
|
||||||
|
- проверка/ротация по лимиту,
|
||||||
|
- создание новой сессии.
|
||||||
|
- Результат: операции открытия сервисов для одного пользователя сериализованы, лимит применяется стабильно.
|
||||||
|
|
||||||
|
2. Нагрузочное тестирование (k6):
|
||||||
|
- Добавлен скрипт `scripts/load/portal_k6.js`:
|
||||||
|
- логин,
|
||||||
|
- открытие сервиса `/go/<slug>`,
|
||||||
|
- heartbeat `/api/sessions/{id}/touch`,
|
||||||
|
- закрытие `/api/sessions/{id}/close`.
|
||||||
|
- Добавлены профили: `smoke`, `load`, `stress`.
|
||||||
|
- Добавлены пользовательские метрики: `open_success`, `open_rejected`, `limit_redirects`, `touch_rejected`, `flow_errors`.
|
||||||
|
- Добавлена инструкция запуска: `docs/LOAD_TESTING.md`.
|
||||||
|
|
||||||
|
3. Git фиксация:
|
||||||
|
- Commit: `1438dee`
|
||||||
|
- Message: `feat: improve session limit handling and add k6 load testing`
|
||||||
|
|
||||||
|
## 16) Обновления (2026-04-24, вынос maintenance в отдельный контейнер)
|
||||||
|
|
||||||
|
1. Выделен отдельный сервис maintenance:
|
||||||
|
- Добавлен контейнер `maintenance` в `docker-compose.yml`.
|
||||||
|
- Команда контейнера: `python maintenance_runner.py`.
|
||||||
|
- Назначение: единственный фоновый процесс обслуживания пулов и cleanup просроченных сессий.
|
||||||
|
|
||||||
|
2. Поведение API на старте изменено:
|
||||||
|
- Для `api` установлен флаг `ENABLE_STARTUP_MAINTENANCE=0`.
|
||||||
|
- API-воркеры больше не запускают maintenance-потоки при startup.
|
||||||
|
- В логах API при старте ожидаемое сообщение: `startup_maintenance_disabled`.
|
||||||
|
|
||||||
|
3. Что делает maintenance-контейнер:
|
||||||
|
- bootstrap схемы БД (под schema-lock),
|
||||||
|
- `ensure_universal_pool()` и `ensure_web_pool()`,
|
||||||
|
- поддержка warm-pool (когда WEB pool отключен),
|
||||||
|
- cleanup протухших сессий (через существующий `cleanup_loop`).
|
||||||
|
|
||||||
|
4. Блокировка лидера maintenance:
|
||||||
|
- Используется file-lock `/tmp/portal-maintenance.lock`.
|
||||||
|
- Контейнер maintenance удерживает lock и работает как singleton.
|
||||||
|
|
||||||
|
5. Операционные команды:
|
||||||
|
- Перезапуск API + maintenance:
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build api maintenance
|
||||||
|
```
|
||||||
|
- Проверка:
|
||||||
|
```bash
|
||||||
|
docker compose ps api maintenance
|
||||||
|
docker compose logs -f api maintenance
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Текущее целевое состояние после обновления:
|
||||||
|
- `api` отвечает за пользовательские HTTP-запросы.
|
||||||
|
- `maintenance` отвечает за фоновые задачи и состояние пулов.
|
||||||
|
- Traefik продолжает маршрутизацию как и раньше.
|
||||||
|
|
||||||
|
## 17) Нагрузочный прогон (2026-04-24, 100 пользователей x 2 сервиса)
|
||||||
|
|
||||||
|
Сценарий:
|
||||||
|
- 100 тестовых пользователей `loadu001..loadu100` (пароль `LoadTest!2026`),
|
||||||
|
- каждому выдан доступ к 2 WEB-сервисам: `termidesk`, `vmmanager`,
|
||||||
|
- тест: каждый пользователь логинится и запускает оба сервиса последовательно.
|
||||||
|
|
||||||
|
Инструмент и артефакты:
|
||||||
|
- k6 через Docker: `grafana/k6`,
|
||||||
|
- скрипт: `/root/Stend_mont/scripts/load/k6_100_users_2_services.js`,
|
||||||
|
- вывод прогона: `/tmp/k6_100x2.out`.
|
||||||
|
|
||||||
|
Итог прогона:
|
||||||
|
- `iterations`: 100 (по одной на VU),
|
||||||
|
- `checks_succeeded`: 41.61% (124/298),
|
||||||
|
- `http_req_failed`: 41.13% (174/423),
|
||||||
|
- `open termidesk -> 303`: 14% (14/99),
|
||||||
|
- `open vmmanager -> 303`: 11% (11/99),
|
||||||
|
- p95 `http_req_duration`: ~9.07s,
|
||||||
|
- основная причина ошибок по API-логам: `web_pool_lock_timeout` -> HTTP 503 на `/go/<slug>`.
|
||||||
|
|
||||||
|
Вывод:
|
||||||
|
- при burst-нагрузке 100x2 текущий WEB-пул и таймауты распределения не выдерживают,
|
||||||
|
- требуется увеличение емкости/параметров пула и повторный прогон.
|
||||||
|
|
||||||
|
## 18) Нагрузочный прогон (2026-04-24, плавный 20 пользователей x 2 сервиса)
|
||||||
|
|
||||||
|
Цель:
|
||||||
|
- проверить поведение без резкого пика;
|
||||||
|
- имитировать постепенное подключение: +1 пользователь в минуту;
|
||||||
|
- довести до 20 online, каждый запускает 2 WEB-сервиса (termidesk, vmmanager).
|
||||||
|
|
||||||
|
Подготовка:
|
||||||
|
- временно увеличен idle timeout для теста:
|
||||||
|
- .env: SESSION_IDLE_SECONDS=7200;
|
||||||
|
- WEB runtime слоты пересозданы, чтобы получили IDLE_TIMEOUT=7200.
|
||||||
|
- API и maintenance пересозданы с новыми env.
|
||||||
|
|
||||||
|
Профиль нагрузки:
|
||||||
|
- k6 сценарий ramping-vus:
|
||||||
|
- 20m до 20 VU,
|
||||||
|
- 5m удержание 20 VU,
|
||||||
|
- 1m спад до 0.
|
||||||
|
- каждый VU: логин + /go/termidesk + /go/vmmanager, затем удержание.
|
||||||
|
|
||||||
|
Фактический результат:
|
||||||
|
- k6 checks: 60/60 (100%);
|
||||||
|
- custom metrics:
|
||||||
|
- login_ok: 20/20;
|
||||||
|
- open_service_a_ok: 20/20;
|
||||||
|
- open_service_b_ok: 20/20;
|
||||||
|
- HTTP errors: 0/80;
|
||||||
|
- в БД после прогона: 40 ACTIVE WEB-сессий (20 termidesk + 20 vmmanager).
|
||||||
|
|
||||||
|
Наблюдения по инфраструктуре:
|
||||||
|
- во время роста зафиксировано авторасширение WEB-пула до слотов 0..40;
|
||||||
|
- позже часть старших слотов была удалена, но в БД остались ACTIVE-сессии на слотах 20..39.
|
||||||
|
|
||||||
|
Ресурсы сервера (по /tmp/server_stats_20x2.log):
|
||||||
|
- max load average (1m): 17.35;
|
||||||
|
- max used RAM: 9135 MB (из ~64 GB);
|
||||||
|
- max disk usage /: 96%;
|
||||||
|
- max CPU:
|
||||||
|
- stend_mont-api-1: 3.39%,
|
||||||
|
- stend_mont-traefik-1: 60.87%,
|
||||||
|
- stend_mont-db-1: 7.71%,
|
||||||
|
- single portal-webpool-*: до 250.44%.
|
||||||
|
|
||||||
|
Вывод:
|
||||||
|
- плавный сценарий 20x2 проходит стабильно по HTTP/логике запуска;
|
||||||
|
- обнаружен риск целостности состояния: ACTIVE-сессии могут ссылаться на слоты, контейнеры которых уже scale-down/удалены.
|
||||||
|
|||||||
Reference in New Issue
Block a user