😊 성능 테스트 가이드 시리즈😊 / 클릭 시 이동
1. 성능 테스트_성능 목표 잡기
2. 성능 테스트_HikariCP의 연결 최대 풀 설정
3. 성능 테스트_Caffeine 캐시 설정 및 적용
4. 성능 테스트_인덱싱과 트랜잭션 관리 최적화
5. 성능 테스트_하드웨어 리소스 업그레이드
문제 원인 파악
기본적으로 테스트를 진행할 때는 에러가 발생하면 안 되는데, 첫 번째 테스트를 진행하던 중에 에러가 발생했다.
딱 특정 지점에만 에러가 발생하는 것을 보고 해당 부분의 log를 분석하였다.
2024-09-16T04:22:39.013+09:00 WARN 24884 --- [now_here] [.0-8080-exec-72] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: null
2024-09-16T04:22:39.013+09:00 WARN 24884 --- [now_here] [.0-8080-exec-35] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: null
2024-09-16T04:22:39.014+09:00 ERROR 24884 --- [now_here] [.0-8080-exec-72] o.h.engine.jdbc.spi.SqlExceptionHelper : HikariPool-1 - Connection is not available, request timed out after 30001ms (total=10, active=10, idle=0, waiting=88)
2024-09-16T04:22:39.014+09:00 ERROR 24884 --- [now_here] [.0-8080-exec-35] o.h.engine.jdbc.spi.SqlExceptionHelper : HikariPool-1 - Connection is not available, request timed out after 30002ms (total=10, active=10, idle=0, waiting=88)
2024-09-16T04:22:39.015+09:00 ERROR 24884 --- [now_here] [.0-8080-exec-35] c.n.n.d.m.service.MemberAuthService : get Member By Token Error =Could not open JPA EntityManager for transaction
2024-09-16T04:22:39.016+09:00 ERROR 24884 --- [now_here] [.0-8080-exec-35] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
java.lang.NullPointerException: Cannot invoke "com.now_here5.now_here.global.security.dto.AuthenticatedMemberDto.getRoleNamesDto()" because "memberDto" is null
이 에러는 애플리케이션이 데이터베이스의 연결을 얻지 못해 발생한 것이다.
HikaraiCP 연결 풀이 고갈되어서 그런데, 로그를 좀 더 분석해 보면
HikariPool-1 - Connection is not available, request timed out after 30001ms (total=10, active=10, idle=0, waiting=88)
total=10: 최대 10개의 연결이 설정
active=10: 현재 모든 연결이 사용 중
idle=0: 유휴 상태의 연결이 없음
waiting=88: 88개의 스레드가 연결을 대기
이로 인해 연결을 얻지 못한 요청들이 30초 후에 타임아웃
이와 같은 이유로
java.lang.NullPointerException: Cannot invoke "com.now_here5.now_here.global.security.dto.AuthenticatedMemberDto.getRoleNamesDto()" because "memberDto" is null
해당 에러를 통해 알 수 있듯이 memberDto 객체가 생성되지 않아 NullPointerException이 발생하는 것이었다.
성능 개선
성능 개선을 위한 내용을 작성할 때 설명을 위해서
HikariCP의 JDBC 커네션 풀의 설정 파라미터와 모니터링 툴의 지표에 대해 설명하겠다.
HikariCP의 JDBC 커네션 풀의 설정 파라미터
- maximum-pool-size
- 최대 풀 크기를 설정하는 파라미터로 HikariCP가 동시에 유지할 수 있는 최대 데이터베이스 연결 수를 지정
- 늘릴 때 장점
- 동시 처리 가능한 요청 수가 증가해 타임아웃 발생 가능성이 줄어듦
- 단점
- 데이터베이스 서버 과부하 우려
- 늘릴 때 장점
- 최대 풀 크기를 설정하는 파라미터로 HikariCP가 동시에 유지할 수 있는 최대 데이터베이스 연결 수를 지정
- minimum-idle
- 최소 idle 연결 수를 설정하는 파라미터
- 기본값이 10개이므로 새로운 요청이 들어올 때 빠르게 연결을 제공하도록 함
- 늘릴 때 단점
- 불필요한 idle 연결 수가 많아지면 자원 낭비가 발생
- 최소 idle 연결 수를 설정하는 파라미터
- connection-timeout
- 연결 타임아웃을 설정하는 파라미터, 데이터베이스 연결을 위해 API 서버가 기다릴 최대 시간을 밀리초 단위로 설명
- 풀 고갈 상황에서 요청들이 빠르게 실패하고 예외 처리를 적절하게 대응 가능
- 너무 짧으면 일시적인 트래픽 급증 시 불필요한 타임아웃 발생
- 너무 길면 사용자 만족도 감소
- idle-timeout
- idle 상태의 타임아웃을 설정하는 상태
- 위와 맥락을 같이함
- idle 상태의 타임아웃을 설정하는 상태
- max-lifetime
- 연결의 풀에서 유지될 수 있는 최대 수명을 설정
- 기본적으로 30분으로 설정해 오래된 연결이 자동으로 폐기되고 새로 생성되게함
- 너무 짧은 경우 연결을 재성성하게 되어 성능에 영향
- 연결의 풀에서 유지될 수 있는 최대 수명을 설정
pgAdmin4 Dashboard
- Database Sessions (데이터베이스 세션):
- 파란색(Total)은 전체 세션 수, 주황색(Active)은 활성 세션, 녹색(Idle)은 유휴 상태의 세션
- 처음에는 총 세션 수가 약간 변동하다가, 이후 안정적으로 유지
- 활성 세션과 유휴 세션은 전체 세션과 비슷하며 이는 전체적인 세션 관리가 원활하게 이루어지고 있음을 나타냄
- Transactions per second (초당 트랜잭션):
- 초당 트랜잭션 수가 처음에는 상당히 많다가 급격히 줄어들고, 이후 거의 유지
- 트랜잭션이 처음에는 활발하게 일어났으나 이후 줄어드는 패턴을 볼 수 있습니다.
- 이는 특정 작업이 완료되면서 트랜잭션 수가 감소
- Tuples in (데이터 삽입):
- 튜플 삽입(Inserts), 업데이트(Updates), 삭제(Deletes)에 대한 정보
- Tuples out (데이터 출력):
- Fetched(가져오기)와 Returned(반환)된 튜플
- 초기에는 데이터 가져오기와 반환 작업이 활발하게 일어나다가 급격히 줄어듬
- 이는 처음에 데이터베이스로부터 데이터를 많이 조회했다가 작업이 완료되면서 감소
- Block I/O (블록 입출력):
- 데이터베이스 블록 입출력에서 Read(읽기)와 Hit(히트)에 대한 정보
- 읽기 작업이 초기에는 상당히 활발하다가 감소하는 경향
- 이는 초반에 데이터 읽기 작업이 집중적으로 이루어졌다는 것을 의미
모니터링 툴에서 갑자기 지표들이 감소한 이유는 에러를 우선적으로 처리하고 다음 테스트를 진행하기 위해
테스트를 중지했기 때문이다.
위에서 분석한 문제 원인과 해결 방안을 통해서 내린 결론은
HikariCP의 최대 풀 크기가 10으로 고정되어 있어 스레드가 대기를 하다가 타임아웃이 발생해 에러가 발생한 것이다.
그래서 이를 20으로 조정하고 2번째 테스트를 진행했다.
결과는 다음 글에..