LabHub

부하 테스트 · 테스트 설계와 워밍업 · 이론

부하 생성기가 백프레셔에 협조한다

LabHub 에서 이어서 보기

한 줄 요약

부하 테스트 결과와 프로덕션 지표가 다르면 대개 부하 테스트 쪽을 의심해야 한다. 실제 사용자는 이전 요청이 느리다고 다음 요청을 미뤄 주지 않기 때문이다.

왜 이게 필요했나

부하 생성기를 초당 1,000건으로 설정했는데 응답 하나가 2초를 잡아먹었다고 하자. 동기식(closed) 생성기는 그 2초 동안 보냈어야 할 2,000건을 보내지 않는다. 시스템이 가장 느렸던 구간의 샘플이 통째로 사라지고, 부하 생성기는 자신이 만들어 낸 백프레셔에 '협조'해 버린다. Gil Tene 이 coordinated omission 이라고 부른 문제다.

증상은 셋이다. 부하를 올려도 p99 가 거의 움직이지 않는다. 보고된 처리량이 설정한 목표치보다 낮은데 지연 분포는 목표치에서 측정한 것처럼 생겼다. 그리고 프로덕션이 부하 테스트보다 꼬리가 훨씬 나쁘다.

모든 실행에 적용할 수용 검사는 한 줄이다. 보고된 실제 처리량이 설정한 목표 처리량과 일치하는가. 일치하지 않으면 그 실행의 지연 분포는 신뢰할 수 없다.

어떻게 동작하나

이것이 closed 모델과 open 모델의 차이다. closed 는 동시성을 고정한다(항상 N개가 처리 중). open 은 도착률을 고정한다(응답 여부와 무관하게 초당 λ건 도착). 실제 사용자는 open 에 가깝다.

리틀의 법칙 L = λ × W 는 두 모델을 잇는다. 시스템 안의 평균 요청 수는 도착률 곱하기 평균 체류 시간이다. RPS 1,000, 지연 100ms 면 동시에 100개가 처리 중이다. 반대로 동시성을 10으로 고정한 closed 실행에서 RPS × 평균 지연이 10 근처가 아니라면, 부하 생성기가 목표 부하를 실제로 내지 못했다는 뜻이다.

커넥션 풀 사이징에도 그대로 쓴다. 인스턴스 하나가 초당 500 요청, 평균 80ms 면 500 × 0.08 = 40 이고, 꼬리 지연을 감안해 1.5배 여유를 주면 권장 풀 크기는 60 이다. 여기서 멈추면 안 된다. 함대 단위로 다시 검산해야 한다. 인스턴스 40대 × 풀 60 = 최대 2,400 연결인데 Postgres max_connections 가 200 이면 배포 직후 접속 폭주로 장애가 난다.

현장에서 만나는 모습

나쁜 벤치마크에는 공통점이 있다. 워밍업이 없어 JIT 가 가열되지 않았고, CPU 주파수를 고정하지 않았고, OS 노이즈를 고려하지 않았고, 한 번만 측정했고, 이상치를 처리하지 않았다.

좋은 절차는 단순하다. 워밍업 10회, 본 측정 100회 이상, 2σ 밖 제거, p50·p95·p99 를 함께 보고, 환경 기록. 콜드 스타트 실측치도 알아 두면 워밍업 길이를 정하는 데 도움이 된다. Python/Node 150~500ms, Go/Rust 50~100ms, JVM 1000~3000ms, GraalVM Native 50~100ms.

테스트 종류도 목적에 따라 나눈다. 부하(예상 트래픽에서 정상 동작), 스트레스(한계점을 찾기 위해 점진 증가), 스파이크(급증 반응), 내구성 Soak(장시간으로 메모리 누수 탐지).

다음 실습에서 할 것

동시성 1·2·5·10·20 사다리를 계획하고 스크립트로 자동 실행합니다. 결과를 표로 만들어 처리량이 더 이상 늘지 않는 무릎 지점을 규칙에 따라 찾고, 리틀의 법칙으로 실행 자체가 유효했는지 검증한 뒤, 도착률을 고정한 open 실행을 따로 돌려 목표 대비 실제 처리량을 대조합니다.