Andrey Epifancev 6a2439239d perf: модалки на малом и выращенном листе (grow)
perfkit.gen.grow доращивает FORM_2 AHR_UTILITY/AHR_SECURITY до 500 и
FORM_4 CAP до 300 строк; у форм-модалок варианты @big.

Refs #202

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 01:04:06 +04:00

89 lines
9.9 KiB
Markdown
Raw 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.

# Профилирование: perf/
## Стенд
Требования: Docker, python3.12, Node 24, npm.
```
perf/stand.sh all # up, migrate, bootstrap, web-build, api, web, smoke
perf/stand.sh down # остановить бэк, фронт и Postgres
```
Подкоманды: `up | migrate | bootstrap | api | web-build | web | smoke | reset | down | all`.
`reset` — чистая БД: останавливает бэк, сносит volume Postgres, поднимает заново (`up`, `migrate`, `bootstrap`, `api`); фронт не трогает.
- Postgres 16 (`pg_stat_statements`) на `localhost:55432`, `perf/perf/perf`.
- Бэк (`DEBUG=true`) на `http://localhost:8000`, логи и PID в `perf/.run/`.
- Фронт (prod-сборка, `rsbuild preview`) на `http://localhost:8080`; `/api` (включая WebSocket) проксируется на бэк.
- Профилирующая сборка (`PERF_PROFILING=1`, `react-dom/profiling`) лежит в `perf/.build/profiling`, обычная в `perf/.build/prod`.
Для запуска preview с профилирующей сборкой: `PERF_PROFILING=1 perf/stand.sh web` (после остановки обычного `web`).
- Админ: `admin` / `admin`.
- Переменные: `PERF_API`, `PERF_WEB`, `PERF_DB_DSN`.
- Зависимости Python ставятся в `perf/.venv` (`api/requirements-dev.txt` + `perf/requirements.txt`).
- npm берёт пакеты с `https://registry.npmjs.org` (`npm_config_registry`), `web/.npmrc` не нужен.
## Данные
```
perf/stand.sh reset # пустая БД (генератор на непустой БД откажется работать)
cd perf && .venv/bin/python -m perfkit.gen.run --scale 1 # ~20-40 мин, пишет results/manifest-s1.json
cd perf && .venv/bin/python -m perfkit.gen.verify --scale 1 # должен напечатать VERIFY OK
```
Генератор работает через HTTP-API под `admin` (WebSocket — только для программ и проектов FORM_4,
у них нет HTTP-ручки). Пользователи: `perf_rf`, `perf_rf_coauthor` (одна РФ, роль 3) и `perf_dfip`
(роль 2, все оргединицы), пароль `perf`; в манифесте они под ключами `rf`, `rf_coauthor`, `dfip`.
Числа плана и их обоснование — в `perf/perfkit/gen/plan.py`.
## Фронт: замеры
Каркас: `perf/e2e` (Playwright + CDP). Версии: `@playwright/test` 1.64.1, Chromium 156.0.8078.4 (headless shell, revision 1248).
```
cd perf/e2e && npm install && npx playwright install chromium # npm_config_registry=https://registry.npmjs.org
npx playwright test tests/harness.spec.ts # самопроверка каркаса
```
- `PERF_RUN_ID` — имя каталога прогона `perf/results/<run_id>/` (по умолчанию `YYYYMMDD-HHMM`).
- `PERF_SCALE` — масштаб манифеста `perf/results/manifest-s<scale>.json` (по умолчанию 1).
- `PERF_API`, `PERF_WEB` — адреса стенда (`http://localhost:8000`, `http://localhost:8080`).
- Агрегация: `cd perf && .venv/bin/python -m perfkit.aggregate results/<run_id>`.
- Библиотеки `perf/e2e/lib/`: `auth.loginAs`, `cdp.*` (throttle, trace, gc, heap), `probe` (long tasks > 50 мс, кадры rAF; тяжёлый кадр — интервал > 50 мс), `results.append`, `manifest.loadManifest`.
### Сценарии скролла
`perf/e2e/scenarios/scroll.spec.ts`, библиотека `lib/scroll.ts` (`scrollThrough`: колесо мыши шагами по 200 px вниз/вправо до конца и обратно, `complete` = `scrollTop + clientHeight >= scrollHeight - 2`). Самопроверка: `npx playwright test tests/scroll-lib.spec.ts`.
```
cd perf/e2e
PERF_RUN_ID=s1 PERF_SCALE=1 npx playwright test scenarios/scroll.spec.ts # вся матрица масштаба (долго!)
PERF_RUN_ID=t PERF_TARGETS='FORM_2/AHR/*' PERF_CPU_RATES=1 PERF_REPEATS=1 npx playwright test scenarios/scroll.spec.ts
cd .. && .venv/bin/python -m perfkit.aggregate results/s1
```
- Матрица: роль `rf` x листы с `owner=rf` и отчёты проектов x оси y,x x CPU 1 (без троттлинга, решение человека) x 3 повтора; `admin` x все листы x ось y x CPU x 1 повтор (только масштаб 1); `admin` и `dfip` x своды (svod1_MAIN/FORM_1..4, svod2_GO/RF, projects_summaries) x ось y x CPU x 3 повтора + одна смена фильтра на CPU 6 (target `.../filter`).
- target: `FORM_x/<sheet>/<direction|null>`, `project/<report_type>/<project_id>`, `svod/<name>`; к записи добавляется суффикс оси `@y` / `@x` (агрегатор группирует по target).
- Переменные: `PERF_TARGETS` (glob через запятую; сопоставляется с target без суффикса оси и с полным), `PERF_CPU_RATES` (по умолчанию `1`; `1,4,6` включает троттлинг вручную), `PERF_REPEATS` (число повторов вместо матричного), `PERF_ROLES` (`rf,admin,dfip`), `PERF_AXES` (`y,x`), `PERF_LOAD_TIMEOUT_MS` (ожидание данных, по умолчанию 15 мин; ответ свода 2 до 47 МБ), `PERF_MAX_STEPS`, `PERF_TEST_TIMEOUT_MS` (таймаут одного замера, по умолчанию 45 мин), `PERF_TEST_TIMEOUT_SVOD2_MS` (для свода 2, по умолчанию 3 ч: загрузка до 15 мин + скролл на CPU 6 порядка 40 мин).
- Каждый замер - новый browser context; CPU-троттлинг включается после загрузки данных (до скролла). Перед замером ждём >= 20 строк `tbody tr`; если не дождались - запись `incomplete: true` с `skip_reason`.
- Метрики: probe (long tasks, кадры), `axis` (0 = y, 1 = x), `rows_visible_before/after`, `json_bytes_sheet` (размер распакованного ответа загрузки данных из сетевого лога), `steps`. Записи с `skip_reason` (`no_overflow`, `rows<20`, `filter_no_effect`, `scroll_incomplete`...) всегда `incomplete:true`; в конце прогона они выводятся списком. Метрика `traced=1` - замер с включённым трейсом (повтор 0), Task 10 может исключать его из статистики. Trace первого повтора - в `perf/results/<run_id>/artifacts/` (30-70 МБ каждый, в git не попадают).
- Сайт свода не читает `#hash` из URL: вкладка (`FORM_1`, `GO`...) выбирается кликом по `role=tab`.
### Модалки
Перечень - `perf/e2e/modals.json` (id, page, source `file:line`, `open{selector, action, pre}`, priority, роль; `skip_reason` для недостижимых из UI). `lib/modal.ts` (`measureOpen`: `click_to_visible_ms`, `long_task_n`, `long_task_ms`, `main_thread_ms` = прирост CDP `Performance.TaskDuration` от клика до отрисовки), `lib/reactProfile.ts` (минимальный `__REACT_DEVTOOLS_GLOBAL_HOOK__`: renders и self_ms по компонентам, нужна профилирующая сборка). Самопроверка: `npx playwright test tests/modal-lib.spec.ts`.
```
cd perf/e2e
PERF_RUN_ID=m PERF_PRIORITY=1 PERF_REPEATS=2 npx playwright test scenarios/modals.spec.ts # priority 1 (по умолчанию 5 повторов; priority 2 - 3)
PERF_RUN_ID=m PERF_PRIORITY=2 npx playwright test scenarios/modals.spec.ts
../stand.sh web-profiling # профилирующая сборка на :8081
PERF_WEB=http://localhost:8081 PERF_PROFILING=1 PERF_RUN_ID=m PERF_MODALS='sheet_*,projects_create' npx playwright test scenarios/modals.spec.ts # scenario modal_renders
cd .. && .venv/bin/python -m perfkit.modals_inventory results/m # report-data/m/modals-inventory.md
```
- Переменные: `PERF_MODALS` (glob по id; `<id>@big` - вариант на самом большом листе), `PERF_PRIORITY` (`1`, `2`, `1,2`), `PERF_ROLES`, `PERF_REPEATS`, `PERF_CPU_RATES` (по умолчанию `1`).
- Каждый замер - новый browser context. Записи: `scenario: modal` (метрики `measureOpen`) и `modal_renders` (`metrics.renders_top` - топ-20 по self_ms, `commits`, `total_renders`).
- Оценка полной матрицы на CPU 1: priority 1 (17 целей x 5) около 6 мин, priority 2 (15 x 3) около 2 мин, profiling около 2 мин; всего 10-12 мин. С `PERF_CPU_RATES=1,4,6` priority 1 вырастет втрое.
Малый и большой лист. Форм-модалки (выбор ВСП/статьи, создание программы/проекта) существуют только на листах FORM_2 AHR_RENT/UTILITY/SECURITY (31 строка) и FORM_4 AHR/CAP/OPER (110 строк). Для зависимости от числа строк `perfkit.gen.grow` (`cd perf && .venv/bin/python -m perfkit.gen.grow --scale 1`, идемпотентно, ~4 мин, FORM_4 add_line O(n)) растит FORM_2 AHR_UTILITY/AHR_SECURITY до 500 и FORM_4 CAP до 300 строк и обновляет манифест. В `modals.json` у этих модалок `big_sheet`: пара записей `<id>` (малый лист: AHR_RENT, FORM_4/AHR) и `<id>@big` (UTILITY, FORM_4/CAP). Порядок для run-all: reset -> gen -> скролл -> соавтор -> grow -> модалки; малые замеры от grow не зависят, но скролл по UTILITY/SECURITY/CAP после grow несопоставим с до него.