Wrk Latency 부정확한 분포 분석

API7.ai

September 3, 2018

Technology

wrk는 Redis, NGINX, Node.js, LuaJIT와 같은 오픈 소스 프로젝트를 기반으로 구축된 훌륭한 HTTP 스트레스 테스트 도구입니다. 이 도구는 이벤트 기반, HTTP 파싱, 고성능 및 유연성의 강점을 활용하며, Lua 스크립트를 작성하여 테스트 요청을 생성할 수 있는 기능을 제공합니다.

wrk에는 테스트 케이스가 없고, 저자가 약 1년에 한 번씩 나타나 코드를 병합하지만, 이는 wrk를 성능 및 퍼즈 테스트 도구로 사용하는 데 방해가 되지 않습니다. 만약 여전히 멀티스레드 ab를 사용하고 있다면, wrk를 시도해 볼 가치가 충분히 있습니다.


다음은 wrk 결과에서 지연 분포의 통계 부분입니다.

    Latency Distribution
     50%    1.20ms
     75%  595.78ms
     90%  899.11ms
     99%    1.00s

이 예시는 요청의 50%가 1.2ms 내에 완료되고, 90%가 899ms 내에, 99%가 1초 내에 완료됨을 의미합니다.

우리가 wrk를 사용하여 자사 제품을 스트레스 테스트할 때, wrk의 지연 통계에서 대부분의 요청이 몇 밀리초 내에 완료되었지만, 소수의 요청이 100밀리초 이상의 지연을 보였습니다. OpenResty로 구축된 시스템에서 이러한 큰 지연은 과학적이지 않습니다.

이 문제의 최종 해결책은 매우 간단했지만, 구체적인 분석과 문제 위치 파악은 다소 복잡했고 며칠이 걸렸습니다. 최종 해결책은 중요하지 않으며, 문제를 해결하는 과정과 사고 방식이 흥미로운 부분입니다.


지연 문제를 마주했을 때, 우리의 첫 반응은 코드나 시스템 어딘가에 막힘이 있다는 것입니다. 시스템이 복잡하기 때문에 플레임 그래프를 제공합니다.

이런 종류의 문제를 분석하기 위한 준비된 systemtap 스크립트가 없어서 작성하는 데 시간이 걸렸습니다. 그러나 systemtap 스크립트를 여러 번 수정한 후에도 유의미한 지연을 포착하지 못했는데, 이는 wrk 결과와 명백히 불일치합니다. 스크립트가 완벽하지 않아 후킹되지 않은 일부 함수를 놓쳤을 가능성이 있다고 추측했습니다. 하지만 wrk 결과의 정확성에 대해서도 의문이 들었습니다.

우리는 방향을 돌려 wrk 통계가 잘못된 것인지, 아니면 실제로 서버 문제인지 확인하려고 했습니다. wrk가 위치한 서버에서 크래시 테스트의 모든 패킷을 덤프하고, 소요 시간별로 정렬했을 때, wrk의 지연 통계와 매우 다른 결과를 발견했습니다. 100밀리초를 초과하는 요청은 없었습니다. 위 테스트를 여러 번 반복했고 결과는 일관되었습니다.


이제 목표는 명확해졌습니다. wrk의 지연 통계에 관한 코드를 매끄럽게 만드는 것입니다. 가장 큰 걱정은 wrk의 내부 통계에 버그가 있을 가능성인데, 이는 수정하기 쉽지 않습니다. 결국 테스트 케이스가 없는 프로젝트이기 때문입니다.

우리는 wrk 통계 로직을 살펴보고 시작과 끝에 로그를 추가했으며, 지연에 관한 통계가 정확하다는 것을 확인하고 안도했습니다. 하지만 최종 결과를 출력하기 전에 통계 보정 코드가 있습니다.

    if (complete / cfg.connections > 0) {
        int64_t interval = runtime_us / (complete / cfg.connections);
        stats_correct(statistics.latency, interval);
    }

이 if 조건에 따르면, 압력 데이터가 생성될 때마다 보정이 이루어집니다. 관심이 있다면 stats_correct 함수의 코드를 살펴보세요. 단 10줄이지만, 여러 번 읽어도 이해하기 어려웠습니다.

코드 커밋 기록을 다시 확인해보니, 다음과 같은 내용이 있었지만 이해하기 어려웠습니다:

remove calibration & improve CO correction

한마디로, 커밋 기록이 조금 더 상세하거나 약어 없이, 또는 코드 주석을 추가했다면 많은 시간을 절약할 수 있었을 것입니다.

여기까지 문제를 확인했고, 제품 문제가 아니라는 것을 확인했습니다. 해결책은 위의 보정 코드를 주석 처리하는 것입니다. 하지만 wrk 저자가 의도적으로 추가한 이유가 있을 것이고, 그 이유를 이해하지 못하는 것은 항상 잠재적인 문제입니다. 자연스럽게 저자는 이슈를 열어 질문했고, 1년에 한 번 나타나는 wg가 15일 후에 답변을 했습니다. 위 커밋 정보의 약어 COCoordinated Omission을 의미하며, 이 문제에 관한 전용 글을 제공했습니다. 관심 있는 학생들은 이 키워드로 검색해보세요.

간단히 말해, Coordinated Omission은 스트레스 테스트를 할 때, 요청을 보내고 응답을 받는 시간만 계산하는 것으로는 충분하지 않다는 것입니다. 이는 서비스 시간을 의미하며, 많은 잠재적인 문제를 놓칠 수 있습니다. 테스트 요청의 대기 시간도 계산해야 사용자가 관심 있는 응답 시간으로 간주될 수 있습니다.

CO 문제를 제기한 Gil Tene는 wrk를 수정하여 CO 문제를 해결했습니다: https://github.com/giltene/wrk2, 이 프로젝트의 README에는 이에 대한 설명이 있으니 관심이 있다면 읽어보세요.


우리 제품의 경우, 코드에 막힘이 없으며, 스트레스 테스트를 할 때 CPU를 가득 채우고 있습니다. 막힘이 있더라도 플레임 그래프를 통해 샘플링하고 분석할 수 있습니다. 따라서 Coordinated Omission을 위해 wrk가 여기서 하는 단순하고 무식한 보정은 오히려 오해를 불러일으킬 수 있습니다.

Tags: