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>
2.8 KiB
2.8 KiB
LPS 부하 테스트 (Locust)
대상 = API 서버(web_main). 워커(브라우저 크롤)는 프록시/브라우저에 처리량이 묶여 Locust 대상이 아니다.
API 는 요청을 받아 job 테이블에 적재만 하고 즉시 응답한다(asyncpg I/O 바운드, bcrypt 없음).
파일
locustfile.py— enqueue(POST /search, 고유코드 write) + 조회(jobs/stats/ops/readyz) 가중 부하bench_multicore.sh— PROCESS_COUNT 1→N 자동 비교(멀티코어 스케일링 측정, 대화형)
실행
# 웹 UI (:8089)
.venv/bin/locust -f loadtest/locustfile.py --host http://localhost:9600
# 헤드리스
.venv/bin/locust -f loadtest/locustfile.py --host http://localhost:9600 --headless --processes 4 -u 1500 -r 150 -t 1m
# 멀티코어 자동 벤치 (PROCESS_COUNT 1/2/4)
LOCUST_PROCESSES=4 ./loadtest/bench_multicore.sh
⚠️ 워커는 끄고 실행(부하 중 실제 크롤=프록시/AI 비용). 잡은 PENDING 으로 쌓임 → 끝나면 정리:
psql -h 127.0.0.1 -U postgres -d lps_db -c "TRUNCATE job;"부하 중 커넥션 관측:SELECT count(*) FROM pg_stat_activity WHERE datname='lps_db';
핵심 발견 (2026-07-09, 11코어 맥 · 1500 users · 20s)
1) 멀티코어는 되지만 DB 커넥션 풀이 발목을 잡는다.
API 는 asyncio(스레드 1개)라 단일 프로세스=단일 코어. uvicorn workers(=process_count)를 늘리면
코어만큼 스케일해야 하는데, 기본 풀(pool_size=10, max_overflow=20)에선 워커를 늘릴수록 오히려 실패 폭증:
| workers | RPS | 실패 | 원인 |
|---|---|---|---|
| 1 | 1,137 | 0 | 정상(단일 코어) |
| 2 | 1,390 | 2,997 | 커넥션 초과 시작 |
| 4 | 1,336 | 10,336 | 커넥션 고갈(SQLAlchemy pool checkout 실패) |
원인: (pool_size+max_overflow) × 2엔진(R/W) × workers 가 PG max_connections(기본 100)를 초과.
4워커면 (10+20)×2×4 = 240 > 100 → 워커 3~4가 커넥션 못 받아 요청 실패.
2) 풀을 올바르게 잡으면 멀티코어가 제대로 작동한다.
| PC=4 설정 | RPS | 실패 |
|---|---|---|
| 풀 10/20 (240 요구) | 1,336 | 10,336 |
| 풀 8/4 (96 요구) | 2,705 | 0 |
풀만 줄이니 RPS 2배 + 실패 0. 즉 코드는 멀티코어를 활용할 수 있고, 막는 건 풀 오버서브스크립션이다.
튜닝 규칙 (프로덕션)
(pool_size + max_overflow) × 2 × process_count ≤ PG max_connections
- 예) max_connections=100, process_count=4 → (pool+overflow) ≤ 12 → pool_size=8, max_overflow=4
- env 로 조절(코드 수정 없이):
DB_POOL_SIZE,DB_MAX_OVERFLOW,PROCESS_COUNT - 더 큰 처리량이 필요하면: PG
max_connections상향 또는 pgbouncer(커넥션 풀러) 도입 - 부하 한계(이 머신): 풀 정상화 시 4워커 ~2,700 RPS, p95 ~900ms, 실패 0