#!/usr/bin/env bash # # 멀티코어 스케일링 벤치 (대화형). # PROCESS_COUNT 를 1→N 으로 바꿔가며 API 를 띄우고, 각각 locust 헤드리스로 RPS/지연을 측정해 비교한다. # API 는 asyncio(스레드 1개)라 단일 프로세스=단일 코어 → workers 를 늘리면 코어만큼 스케일해야 정상. # # 주의: # - **워커는 반드시 OFF** (부하 중 실제 크롤=프록시/AI 비용). 잡은 PENDING 으로 쌓임 → 끝나면 TRUNCATE. # - 커넥션 한계: (pool_size+max_overflow)×2엔진×workers 가 PG max_connections 를 넘으면 거기서 막힌다. set -euo pipefail cd "$(dirname "$0")/.." # lps/ VENV=".venv"; PY="$VENV/bin/python"; LOCUST="$VENV/bin/locust" HOST="http://localhost:9600"; PORT=9600 command -v psql >/dev/null || true [[ -x "$LOCUST" ]] || { echo "[setup] locust 설치..."; "$PY" -m pip install -q locust; } CORES=$("$PY" -c "import os;print(os.cpu_count())") echo "── 멀티코어 부하 벤치 ── (CPU 코어: $CORES)" # 워커 실행 중이면 경고 if pgrep -f worker_main.py >/dev/null; then echo "[warn] 워커(worker_main.py)가 실행 중입니다 — 부하 중 실제 크롤 비용 발생. 중단 권장:" read -rp " 워커를 종료할까요? [Y/n]: " k; [[ "${k:-Y}" =~ ^[Nn] ]] || pkill -f worker_main.py || true fi read -rp "동시 유저 수(-u) [200]: " USERS; USERS="${USERS:-200}" read -rp "spawn rate(-r) [20]: " SPAWN; SPAWN="${SPAWN:-20}" read -rp "각 단계 지속(-t) [45s]: " DUR; DUR="${DUR:-45s}" read -rp "PROCESS_COUNT 목록(공백구분) [1 2 4]: " PCS; PCS="${PCS:-1 2 4}" # PG max_connections (참고) MAXC=$(psql -h 127.0.0.1 -U postgres -tAc "SHOW max_connections;" 2>/dev/null || echo "?") echo "PG max_connections=$MAXC · 풀 추정=(pool+overflow)×2×workers" echo "" RESULTS=() for PC in $PCS; do # 기존 API 종료 lsof -ti:"$PORT" 2>/dev/null | xargs kill 2>/dev/null || true; sleep 1 echo "▶ PROCESS_COUNT=$PC 로 API 기동..." PROCESS_COUNT="$PC" APP_ENV=local "$PY" web_main.py > "/tmp/lps_api_pc${PC}.log" 2>&1 & APIPID=$! # 기동 대기(헬스) for _ in $(seq 1 20); do curl -s "$HOST/healthz" >/dev/null 2>&1 && break; sleep 1; done # locust 헤드리스 (--processes 로 부하 생성기도 멀티프로세스 → locust 가 병목되지 않게) "$LOCUST" -f loadtest/locustfile.py --host "$HOST" --headless --processes "${LOCUST_PROCESSES:-4}" \ -u "$USERS" -r "$SPAWN" -t "$DUR" --csv "/tmp/lps_locust_pc${PC}" --only-summary >/dev/null 2>&1 || true kill "$APIPID" 2>/dev/null || true; sleep 1 # Aggregated 행 파싱: $10=Req/s $17=95% $3=count $4=fail read -r RPS P95 CNT FAIL < <(awk -F, '$2=="Aggregated"{print $10, $17, $3, $4}' "/tmp/lps_locust_pc${PC}_stats.csv" 2>/dev/null || echo "0 0 0 0") RESULTS+=("$PC|${RPS:-0}|${P95:-0}|${CNT:-0}|${FAIL:-0}") printf " → RPS=%.0f p95=%sms 요청=%s 실패=%s\n\n" "${RPS:-0}" "${P95:-0}" "${CNT:-0}" "${FAIL:-0}" done echo "════════ 결과 (동시유저 $USERS, $DUR) ════════" printf "%-8s %-10s %-10s %-10s %-8s %-10s\n" "workers" "RPS" "p95(ms)" "요청" "실패" "vs 1코어" BASE="" for r in "${RESULTS[@]}"; do IFS='|' read -r pc rps p95 cnt fail <<< "$r" [[ -z "$BASE" ]] && BASE="$rps" SCALE=$("$PY" -c "print(f'{(${rps:-0}/${BASE:-1}):.2f}x')" 2>/dev/null || echo "-") printf "%-8s %-10.0f %-10s %-10s %-8s %-10s\n" "$pc" "${rps:-0}" "${p95:-0}" "${cnt:-0}" "${fail:-0}" "$SCALE" done echo "" echo "해석: RPS 가 workers 에 비례해 오르면 멀티코어 활용 정상. 어느 지점부터 안 오르고 실패가 늘면" echo " 거기가 한계 — 보통 DB 커넥션(max_connections=$MAXC) 또는 write 경합. 풀/PG 튜닝 대상." echo "정리: psql -h 127.0.0.1 -U postgres -d lps_db -c 'TRUNCATE job;' (쌓인 부하 잡 제거)"