HikariCP ReConnection 원리 살펴보기

Solomon Maeng
7 min readNov 28, 2023

--

최근 회사에서 DB 점검 혹은 장애로 인해 루비로 만들어진 프로젝트의 서버를 재시작하여 ReConnection하는 작업을 진행했다.

루비로 만들어진 것들은 개발자가 수동으로 ReConnection을 해주어야 했지만, 자바로 만들어진 애플리케이션들은 서버 재시작 없이도 ReConnection이 되었다.

도대체 어떻게 ReConnection이 자동으로 이루어지는지가 너무 궁금해서 늦은 밤에 글을 쓰게 되었다.

SpringBoot 2.0을 기점으로 Tomcat DBCP -> Hikari CP로 바뀌었다. 그래서 SpringBoot + Hikari CP + MySQL Driver 코드를 보며 ReConnection이 어떻게 이루어지는지 살펴보자.

우선 처음으로 살펴봤던건 SpringBoot에서 제공하는 AutoConfiguration이었다.

org.springframework.boot.autoconfigure.AutoConfiguration.imports에서 DataSouce 설정을 해주는 AutoConfiguration을 살펴보았다.

JDBC의 DataSource를 설정하는 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration은 아래와 같다.

애플리케이션 개발하면서 RDBMS 연동할 때 Connection Pool을 무조건 사용하기 때문에 PooledDataSourceConfiguration에 정의된 Hikari.class를 살펴보았다.

여기서 DataSource를 생성해주는 것 같다. 그럼 HikariDataSource.class도 한번 들어가보자.

volatile로 선언된 HikariPool 클래스의 pool 인스턴스 변수가 눈에 들어온다.

HikariPool 클래스도 살펴보자.

다른 건 눈에 띄지 않는데, ConcurrentBag이라는 컬렉션에 PoolEntry를 담고 있는걸 보아선 connectionBag로 선언된 인스턴스 변수가 ConnectionPool을 관리하는 컬렉션이 아닐까 싶다.

getConnection 메서드에서 Connection을 가져오는것을 눈으로 확인할 수 있었다.

자세히 살펴보니 중간에 있는 if 문이 눈에 띈다.

if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && isConnectionDead(poolEntry.connection))) {
closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
timeout = hardTimeout - elapsedMillis(startTime);
}

isConnectionDead(poolEntry.connection) 메서드가 내가 찾고 있던 코드였다.

인스턴스 변수인 isUseJdbc4Validation이 true 라면 Connection 객체의 isValid()를 호출하여 Connection Validation을 실행하고, false 라면 config(HikariConfig)의 getConnectionTestQuery()를 실행하는 것을 확인할 수 있었다.

코드를 살펴보니 HikariConfig에 getConnectionTestQuery()라는 getter 메서드가 있는걸로 보아, 이에 대응하는 setConnectionTestQuery() 메서드도 존재할 것 같다.

지금까지가 Hikari CP 코드로만 살펴본 ReConnection 방법이었고, 조금 더 깊이 들어가보자.

앞선 코드에서 isUseJdbc4Validation은 어떨 때 true가 되고 어떨 때 false가 될까?

PoolBase 클래스가 초기화 될 때 HikariConfig에서 getConnectionTestQuery()를 호출하여 Connection Validation을 위해 사용할 쿼리(SELECT 1)의 여부를 확인하고 isUseJdbc4Validation에 true / false를 설정해준다.

PoolBase 클래스를 구상한 클래스는 위에서 보았던 HikariPool 클래스이다.

따라서 HikariDataSource를 생성할 때 HikariConfig로 setConnectionTestQuery()를 설정하지 않는다면, HikariPool을 생성할 때 isUseJdbc4Validation이 true가 되어 JDBC Driver 수준에서 구현한 Connection.isValid()를 사용하게 된다.

회사 프로젝트에서 setConnectionTestQuery()가 설정된 프로젝트를 보지 못했기 때문에 isUseJdbc4Validation으로 JDBC Driver에 Connection.isValid()가 사용될 것 같다.

우리 회사는 MySQL을 주로 사용하니 MySQL의 mysql-connector-j 라이브러리에 Connection 인터페이스를 구현한 클래스를 살펴보면 최종적으로 ReConnection을 어떻게 달성하는지 알 수 있을 것 같다.

mysql-connector-j 라이브러리에서 Connection 인터페이스를 구현한 ConnectionImpl 클래스에 isValid 메서드가 오버라이드 되어 있다.

pingInternal이라는 메서드가 있는걸 보아하니, Ping을 어디론가 날리는 것 같다.

session 인스턴스 변수에서 ping을 호출하고, 그 아래 첨부한 사진처럼 this.protocol.sendCommand()를 통해 최종적으로 Ping 요청을 보낼 Packet을 생성해서 요청을 보내는 것을 확인할 수 있었다.

결론을 정리하면 application.yaml, application.properties 내에서 Hikari 설정 시 connection-test-query 옵션에 SELECT 1과 같은 설정을 하면 Driver 수준에서 Ping 호출을 하지 않고 DB에 쿼리를 날리는 식으로 동작하는 것 같다.

그리고 설정을 해주지 않는다면 JDBC Driver 수준에서 Connection.isValid() 라는 메서드를 주기적으로 호출하면서 ReConnection을 자동으로 해준다.

더 깊게 파보고 싶지만 내일 출근을 해야 하니 여기까지만..

--

--

No responses yet