Andrey Epifancev 627ea82321 perf: замеры открытия модалок
Перечень модалок (modals.json), measureOpen с main_thread_ms, профиль
перерисовок React (reactProfile), сценарий modals.spec.ts, подкоманда
stand.sh web-profiling (:8081), генератор modals-inventory.md.

Refs #202

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

112 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Стенд профилирования: up | migrate | bootstrap | api | web-build | web | web-profiling | smoke | reset | down | all
set -euo pipefail
SELF="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
cd "$(dirname "$0")/.."
ROOT="$(pwd)"
PERF="$ROOT/perf"
RUN="$PERF/.run"
VENV="$PERF/.venv"
mkdir -p "$RUN"
export PERF_API="${PERF_API:-http://localhost:8000}"
export PERF_WEB="${PERF_WEB:-http://localhost:8080}"
export PERF_DB_DSN="${PERF_DB_DSN:-postgresql://perf:perf@localhost:55432/perf}"
export DATABASE_URL="postgresql+asyncpg://perf:perf@localhost:55432/perf"
export DEBUG=true
export npm_config_registry="${npm_config_registry:-https://registry.npmjs.org}"
venv() {
if [ ! -x "$VENV/bin/python" ]; then
python3.12 -m venv "$VENV"
"$VENV/bin/pip" install -q -r "$ROOT/api/requirements-dev.txt" -r "$PERF/requirements.txt"
fi
}
start_bg() { # name, workdir, cmd...
local name="$1" dir="$2"; shift 2
if [ -f "$RUN/$name.pid" ] && kill -0 "$(cat "$RUN/$name.pid")" 2>/dev/null; then
echo "$name уже запущен (pid $(cat "$RUN/$name.pid"))"; return
fi
# внешний >/dev/null: фоновая подоболочка не должна держать stdout вызывающего (иначе `| tail` виснет)
(cd "$dir" && nohup "$@" >"$RUN/$name.log" 2>&1 </dev/null & echo $! >"$RUN/$name.pid") >/dev/null 2>&1
}
wait_http() { # url, tries
for _ in $(seq 1 "${2:-60}"); do
curl -fsS -o /dev/null "$1" 2>/dev/null && return 0
sleep 1
done
echo "не дождались $1" >&2; return 1
}
cmd="${1:-}"
case "$cmd" in
up)
docker compose -f "$PERF/docker-compose.yml" up -d pg
for _ in $(seq 1 60); do
docker exec perf-pg pg_isready -U perf -d perf >/dev/null 2>&1 && break
sleep 1
done
docker exec perf-pg pg_isready -U perf -d perf
;;
migrate)
venv
(cd "$ROOT/api" && "$VENV/bin/alembic" upgrade head)
;;
bootstrap)
venv
"$VENV/bin/python" "$PERF/stand/bootstrap.py"
;;
api)
venv
start_bg api "$ROOT/api" "$VENV/bin/python" run_server.py
wait_http "$PERF_API/healthz" 60
;;
web-build)
(cd "$ROOT/web" && npm ci && npx rsbuild build -c ../perf/web/rsbuild.perf.config.ts \
&& PERF_PROFILING=1 npx rsbuild build -c ../perf/web/rsbuild.perf.config.ts)
;;
web)
start_bg web "$ROOT/web" npx rsbuild preview -c ../perf/web/rsbuild.perf.config.ts
wait_http "$PERF_WEB/" 60
;;
web-profiling)
# профилирующая сборка (react-dom/profiling, без минификации) на :8081, prod на :8080 не трогаем
[ -f "$PERF/.build/profiling/index.html" ] || (cd "$ROOT/web" && PERF_PROFILING=1 npx rsbuild build -c ../perf/web/rsbuild.perf.config.ts)
PERF_PROFILING=1 PERF_WEB_PORT=8081 start_bg web-profiling "$ROOT/web" npx rsbuild preview -c ../perf/web/rsbuild.perf.config.ts
wait_http "http://localhost:8081/" 60
;;
smoke)
venv
"$VENV/bin/python" "$PERF/stand/smoke.py"
;;
reset)
# Чистая БД: останавливаем api (держит соединения), сносим volume Postgres, поднимаем заново
# и снова запускаем api. Фронт (web) не трогаем: БД он не использует.
if [ -f "$RUN/api.pid" ]; then
pkill -P "$(cat "$RUN/api.pid")" 2>/dev/null || true
kill "$(cat "$RUN/api.pid")" 2>/dev/null || true
rm -f "$RUN/api.pid"
fi
docker compose -f "$PERF/docker-compose.yml" down -v
"$SELF" up; "$SELF" migrate; "$SELF" bootstrap; "$SELF" api
;;
down)
for n in web web-profiling api; do
if [ -f "$RUN/$n.pid" ]; then
pkill -P "$(cat "$RUN/$n.pid")" 2>/dev/null || true
kill "$(cat "$RUN/$n.pid")" 2>/dev/null || true
rm -f "$RUN/$n.pid"
fi
done
docker compose -f "$PERF/docker-compose.yml" down
;;
all)
"$SELF" up; "$SELF" migrate; "$SELF" bootstrap; "$SELF" web-build; "$SELF" api; "$SELF" web; "$SELF" smoke
;;
*)
echo "usage: $0 up|migrate|bootstrap|api|web-build|web|web-profiling|smoke|reset|down|all" >&2; exit 2
;;
esac