부하테스트와 Jmeter

페이지네이션 N + 1 문제 상황을 해결하고 나서 문득 실제 서비스에서 어느정도의 트래픽 부하를 감당할 수 있을지 궁금해졌다. 따라서, Apache Jmeter를 사용하여 실제 부하테스트를 진행해보고 성능 확인을 해보았다.

첫 번째 테스트는 개선 작업이 이루어진 메인 피드 요청에 대해서만 진행한다.

스크린샷 2024-02-14 오후 2.08.12.png

캐시 적용 전 TPS

캐시 적용 전 TPS

결과는 생각보다 참담했다… 메인 피드는 모든 사용자들이 한 번 이상씩은 반드시 사용하는 기능이고 가장 많은 요청이 발생하는 기능인데 평균 150/s의 처리율은 좋지 못한 결과였다. 10초 동안 초당 1000회의 요청을 보냈으나 모든 트래픽을 소화하는데 67초라는 시간이 걸렸다.

커서 기반 페이지네이션 리팩터링

Spring Boot에서는 @Cacheable 어노테이션을 사용하면 간단하게 캐싱을 구현할 수 있다. 캐싱에 앞서 페이지네이션 방식에 대해 재고해볼 필요가 있다. 현재 사용하고 있는 페이지네이션 방식은 pageNumber와 pageSize로 구현되어 있다. 이 방식은 캐싱에 매우 불리한 조건을 가진다. 새로운 데이터가 삽입되게 되면 캐시 데이터를 전부 업데이트 해주어야 한다.

<aside> 💡 새로운 데이터가 삽입되게 되면 같은 pageNumber라도 이전 요청과는 다른 데이터를 가지게 된다. 이렇게 될 경우 누락되는 데이터가 발생할 수 있다. 그래서 쓰기 작업이 발생할 때 마다 새롭게 캐시 데이터를 업데이트 해야한다.

</aside>

그래서 페이지네이션 방식을 커서 기반으로 변경하기로 결정했다. 간단히, 이전 페이지의 마지막 데이터 id(커서)를 받아 해당 값 이후 10개를 가져오는 방식이다. 이 방식은 새로운 데이터가 삽입되더라도 동일한 커서값 요청은 동일한 데이터를 반환해준다. 물론 이 방식에도 단점은 있다. 커서값이 pageSize 이하로 차이나는 요청은 중복되는 데이터가 캐시될 수 있다.


해결방안

리모트 DB

과도하게 낮은 TPS를 보이는 이유 중 하나는 로컬 DB를 사용하고 있기 때문이다. mysql 프로세스의 CPU 점유율이 과도하게 높은 것을 확인할 수 있었고 이 때문에 리소스 부족으로 트래픽 처리가 정상적으로 이루어질 수 없었다. 따라서, 로컬 DB를 리모트 DB로 마이그레이션 후 TPS 변화를 관찰해보자.

리모트 DB는 GCP의 CloudSQL을 사용하여 구축하였다.