Connection is not available, (HikariCP)

고승원·2024년 1월 25일
0

Spring

목록 보기
13/14

hikariCP에서 커넥션 연결을 할 수 없는 에러가 발생할 때 어떻게 해결하는지 정리해 본 글입니다.
SpringBoot, HikariCP, Postgresql을 사용 중입니다.

에러 내용

  • HikariPool-1 - Connection is not available, request timed out after
  • Could not open JDBC Connection for transaction; nested exception is java.sql.SQLTransientConnectionException

현재 애플리케이션 다이어그램

에러가 발생한 간략한 아키텍처는 다음과 같습니다.

  1. DB에 상품, 주문 관련 데이터가 있습니다.
  2. 연동 서버는 주기적으로 각 제휴사로부터 상품, 주문 관련 정보를 업데이트 또는 수집 합니다.

문제 상황

  1. 평소엔 문제없이 제휴사로부터 상품, 주문 관련 정보를 업데이트 또는 수집 합니다.

  2. 간헐적으로 연동 서버와 DB 사이에 Connection is not available 에러가 다수 발생

    따라서, 상품 수정 또는 주문 수집에 누락이 발생합니다.



해결 방법

결론부터 말하면, HikariCP 설정에 문제가 있었습니다.
PoolSize, maxLifetime 설정이 너무 작게 되어 있어 늘렸습니다.



가설 1 - DB 커넥션 부족

10개가 넘는 연동 제휴사 서버가 하나의 DB만 바라보니 DB의 Connection 수가 부족하다고 생각했습니다.

바로 제 생각에 따른 근거를 찾기 위해 로깅을 하기 시작했습니다.

Postgresql 커넥션 수

먼저 Postgresql의 max_connections를 확인했습니다.

max_connections는 postgresql.conf에 정의되어 있으며, SHOW max_connections 쿼리를 통해 조회가 가능합니다.

다음으로 사용되고 있는 Connection 개수를 확인했습니다. 모니터링은 PgAdmin을 사용했습니다.

Session과 Connection은 동일한 뜻이므로 현재 max_connections의 25%도 안 쓰고 있음을 알 수 있습니다.



가설 2 - CPU, Memory 부족

다음으로 DB가 실행 중인 서버의 리소스가 부족해서 커넥션을 못 열어주고 있다고 생각했습니다.

별도의 모니터링 툴이 없고, agent를 붙였다가 문제가 생길까 봐 간단한 스크립트로 확인했습니다.

LOG_FILE=/monitoring.log

while true
	do
	TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
	
	CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{printf("%.1f%%\\n", 100 - $8)}')
	MEM_USEAGE=$(free -h | awk '/^Mem/ {print ($3/$2)*100}')%
	
	echo "$TIMESTAMP -  CPU 사용률 :  $CPU_USAGE% 메모리 사용률 : $MEM_USEAGE%"  >> "$LOG_FILE"
	
	sleep 60

done

한 번씩 CPU가 튀지만, 커넥션을 맺지 못할 정도는 아니라고 생각합니다.



가설 3 - HikariCP 설정 문제

DB의 문제가 아니면 애플리케이션의 문제라고 생각해서 찾아보게 되었습니다.

HikariCP의 HouseKeeper를 사용해 로깅을 해봤습니다.

yml 파일에서 로그 레벨만 변경하면 됩니다.

logging:
  level:
    com.zaxxer.hikari: TRACE
    com.zaxxer.hikari.HikariConfig: DEBUG

로그를 확인해 본 결과 maximumPoolSize가 40인데도, 커넥션이 없어서 wating이 일어나고 있었습니다.



결론

maxLifetime

poolName: Hikari
   connectionTimeout: 30000
   maximumPoolSize: 40
   maxLifetime: 60000

maxLifetime이 너무 짧아 커넥션 재사용률이 매우 떨어지고 있었기 때문입니다.

커넥션을 한두 번 쓰고 버리니, 재사용률이 떨어지고, 필요할 때 커넥션을 사용할 수 없었던 것입니다.

maxLifetime을 default인 30분으로 변경해 주고, 다시 모니터링 했습니다.

아직 끝이 아니다

maxLifetime을 변경한 뒤 Connection 수를 안정적으로 유지하고 있습니다.

하지만, 여전히 Connection is not available 에러가 발생하고 있습니다.

원인은 HikariCP 데드락인데, maximumPoolSize이 부족해 조정이 필요해 보입니다.

PoolSize 계산 공식은 하단에 HikariCP 공식 문서에 기재되어 있으니 참고하여서 수정하면 됩니다!

저는 55로 늘렸습니다.



MinimumIdle

보통 커넥션을 사용하지 않고 있다면, maxLifetime이 지난 뒤 커넥션이 반환됩니다. 이때 커넥션이 부족해 Connection is not available 에러가 발생합니다. 따라서 최소한으로 유지할 커넥션 수를 따로 지정했습니다. (default는 maxLifeTime과 동일합니다.)



마무리

커넥션을 가져오지 못하는 문제에 대해, 초기에는 하드웨어 성능이 부족한 것이라고 생각하고 있었습니다. 그러나 리소스를 충분히 활용하지 못하고 있는 것으로 나타났습니다.

이번 기회에 HikariCP 내부 코드를 살펴보고, 문제를 정의하고 해결하기 위해 여러 곳을 찾아보면서 즐거움을 느꼈습니다.

pgAdmin을 사용하지 않고 쿼리 스크립트를 직접 작성하여 커넥션 수를 확인하고, Housekeeper의 존재를 모르고 MBean을 생성하여 로깅까지 시도했었지만, 결과적으로 더 효율적인 도구를 알게 되어 기분이 좋습니다. (바퀴를 다시 발명하지 말자)

그리고, 다음엔 CPU가 튀는 이유도 찾아봐야겠습니다.



참고


HikariCP 공식문서
https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
https://techblog.woowahan.com/2664/
https://techblog.woowahan.com/2663/
https://www.pgadmin.org/download/

profile
봄은 영어로 스프링

0개의 댓글