ngrinder를 통한 부하테스트로 API 터지는 현상 해결
개요
먼저 프로젝트로 진행한 콘서트 전체조회 API에 대해 설명하자면, 해당 API는 프론트에서 메인 페이지에 데이터를 뿌려주기 위한 API로 전체 사용자가 콘서트에 대한 정보들을 확인할 수 있다.
이 API에 대한 테스트로 1000명의 사용자가 접속한다는 가정하에 ngrinder를 통해 부하테스트를 진행하였다.
문제점
ngrinder를 통해 1000명의 사용자가 각 한 번씩 API를 호출하였을 때의 시나리오를 구성하였다.
이렇게 설정을 하고 테스트를 진행하였는데 API가 터지는 현상을 확인하였다.
그래서 해당 테스트를 다시 한 번 더 진행해봤는데 이번에는 성공하는 것을 확인할 수 있었다.
위의 테스트가 처음 돌렸을 때의 경우인데, TPS 28.1, 평균 테스트 시간 7849.2, 에러율 15.2%로 나오고
아래의 테스트는 두 번째 돌렸을 때의 경우인데, TPS 248.7, 평균 테스트 시간 583.2, 에러율 0%로 나오고 있다.
두 번째 돌렸을 때는 캐시가 적용되었기 때문에 속도가 빨라져 정상적으로 성공한 것이다.
그래서 캐시에 저장된 데이터를 날리고 다시 Jmeter를 통해 부하테스트를 진행해보았다.
Jmeter에서도 똑같이 1000의 사용자를 가정하고 테스트를 진행해봤지만 성공하였다.
다시 처음으로 돌아와서 첫 번째 테스트에서 에러 로그를 확인해 봤다.
HikariCP Time Out으로 인해 데이터베이스와 연결하지 못해 테스트가 실패했다는 로그가 찍혀있었다.
첫 번째 테스트의 상세 결과를 확인해봤다.
TPS를 확인해보면 처음 시작할 때 TPS가 400으로 시작하면서 점차 줄어들면서 4초에 0이 된 것을 확인할 수 있다.
4초까지의 평균 TPS는 약 200정도가 될 것이고 이를 통해 성공한 테스트 개수가 약 800인 것을 확인할 수 있다.
4초 후에는 HikariCP에서 트랜잭션을 연결하지 못하기 때문에 테스트를 실패하고 에려율이 15.2%가 된 것임을 인지하게 되었다.
그래서 왜 HikariCP에서 Transaction Connection이 실패하는가에 대해 알아보고자 했다.
HikariCP로 Connection Pool 역할을 수행한다.
Connection Pool은 Pool 내에서 Connection들을 하여 HTTP 요청에 따라 응답을 제공하고 반환받아서 이를 재 사용한다.
HikariCP에서는 기본적으로 최대 10개까지 Connection을 다룰 수 있다.
위의 테스트에서 Ramp Up 설정을 하지 않고 테스트를 진행하였기 떄문에 1000명의 사용자 부하를 한번에 때려넣게 되어 있었다.
그래서 4초까지는 Connection을 주고 받는 것이 CP의 크기인 10개로 커버가 되다가 4초 이후에는 10개로는 감당하지 못해서 Connection이 터져버리게 된 것이다.
해결
가장 간단한 방법은 Pool Size를 늘려주는 것이다.
spring.datasource.hikari.maximum-pool-size=20
이 스크립트를 통해 Pool의 크기를 늘릴 수 있다.
하지만 Pool을 함부로 늘리는 것을 자원 낭비와 성능 저하를 불러올 수 있으므로 조심해야 한다.
우아한 형제들의 기술블로그에서 HikariCP에 대한 적절한 Pool Size를 구하는 법을 알려주고 있다.
나는 Pool Size를 늘리는 방법이 아닌 ngrinder에서 Ramp Up을 사용해주면서 부하를 점진적으로 줄 수 있도록 설정하였다.
왜냐하면 한 명의 사용자가 메인 페이지에 접속하면 캐시에 데이터를 저장되어 응답해주기 때문에 Pool Size를 늘릴 필요없이 괜찮을 것이라고 판단하였다.