API(enqueue/조회, asyncpg I/O 바운드) 부하 테스트로 멀티코어 활용·한계 측정. - loadtest/locustfile.py: enqueue(고유코드 write)+조회 가중 부하. - loadtest/bench_multicore.sh: PROCESS_COUNT 1→N 자동 비교(--processes 로 부하생성기도 멀티프로세스). - server_configs: PROCESS_COUNT / DB_POOL_SIZE / DB_MAX_OVERFLOW env override(코드·toml 수정 없이 튜닝). - loadtest/README.md: 발견 문서화. 발견(11코어·1500users): 기본 풀(10/20)로 워커 늘리면 실패 폭증(1w=0 → 4w=10336) — (pool+overflow)×2엔진×workers=240 > PG max_connections=100 커넥션 고갈(SQLAlchemy pool checkout 실패). 증명: 풀 8/4(96<100)로 PC=4 재실행 → RPS 1336→2705(2배), 실패 0. 코드는 멀티코어 활용 가능, 막는 건 풀 오버서브스크립션. 규칙: (pool+overflow)×2×process_count ≤ max_connections. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
70 lines
3.8 KiB
Bash
Executable File
70 lines
3.8 KiB
Bash
Executable File
#!/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;' (쌓인 부하 잡 제거)"
|