ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • HikariPool- Failed to validate connection warning 해결하기
    데이터베이스/MYSQL 2023. 7. 15. 17:24

     

     

     

     

    운영하는 서비스에서 30분 간격으로 HikariPool- Failed to validate connection warning 오류가 발생했다. 

     

     

     

     

     

    문제 상황 

    운영환경에서는 datadog에서 5분동안 warn 이나 error 로그가 10개 이상 발생하면 슬랙을 이용해서 오류 메세지를 보내도록 설정해두었는데, 

    동일한 warning이 resolve 후에도 30분 간격으로 발생해 오류 alert가 발생하고 있었다.

     

    data dog에서 확인해보니 오류 메세지는 다음과 같았다. 

     


    HikariPool-1 - Failed to validate connection software.aws.rds.jdbc.mysql.shading.com.mysql.cj.jdbc.ConnectionImpl@5f4fcf76 
    (The active SQL connection has changed due to a connection failure. 
    Please re-configure session state if required.). 
    Possibly consider using a shorter maxLifetime value.


     

     

     

     

     

    문제 시급도 및 중요도 파악하기

    actuator/health시에 나타나는 warning으로 파악하였고,

    운영에서 warning으로 인한 문제가 없다고 판단되어 시급도는 낮다고 판단하였다.

    하지만 warn으로 인해서 지속적인 알람이 발생하고 있었고 이로인해 중요한 문제를 놓칠 수 있어 중요도는 높다고 판단했다. 

     

     

     

     

     

     

     

    원인 파악하기 

    그렇다면 warning은 왜 일어난걸까?

    오류 메세지를 해석해보면 다음과 같다. 


    HikariPool-1 - Failed to validate connection software.aws.rds.jdbc.mysql.shading.com.mysql.cj.jdbc.ConnectionImpl@5f4fcf76 
    (The active SQL connection has changed due to a connection failure. 
    Please re-configure session state if required.). 
    Possibly consider using a shorter maxLifetime value.


    • HikariPool-1에서 MySQL 연결을 유효성 검사하는 과정에서 연결 실패로 인해 활성 SQL 연결이 변경 됨 ⇒ connection이 유효하지 않음
    • 필요한 경우 세션 상태를 다시 구성해야 함
    • maxLifetime 값을 더 짧게 설정하는 것을 고려해볼 수 있음

     

     

     

     

     

    여기서 말하는 Hikari Pool 의 MaxLIfeTIme이란 무엇일까? 

     

    📘 HikariCP - maxLifetime

    HikariCP에 정의된 내용을 보면 다음과 같다. 


     maxLifetime 

    This property controls the maximum lifetime of a connection in the pool. An in-use connection will never be retired, only when it is closed will it then be removed. On a connection-by-connection basis, minor negative attenuation is applied to avoid mass-extinction in the pool. 

     

    We strongly recommend setting this value, and it should be several seconds shorter than any database or infrastructure imposed connection time limit.

     

    A value of 0 indicates no maximum lifetime (infinite lifetime), subject of course to the idleTimeout setting. The minimum allowed value is 30000ms (30 seconds).

     

     Default: 1800000 (30 minutes)


    • Hikari Pool에서 connection의 최대 유지 시간을 제어하는 속성 (connection의 유휴 시간을 제한)
    • 사용 중인 connection은 종료되지 않고, 이미 닫힌 경우에만 제거
    • 한번에 대량으로 종료하고 생성하면 비용이 많이 소요되기 때문에 Pool에서 대규모 connection 소멸은 피함
    • 데이터베이스나 인프라에서 설정된 connection time limit 보다 몇 초 짧아야 함 
    • 값이 0이면 최대 수명이 없어짐
    • idleTimeout 설정에 따라 제한됨
    • 허용되는 최소값은 30초(30000ms)
    • 기본값은 30분(1800000ms)

     

     

     

    💡 참고) Hikari Pool에서 Connection을 관리하는 방법
    드라이버 방법
    JDBC4 지원 X validationQuery,ConnectionTestQuery  쿼리를 수행하여 Connection을 사용 여부 확인 (test-idle-while) ⇒ Query를 실행하여, DB 부하 발생 
    JDBC4 지원 O HikariCP 내부적으로 JDBC 구현체의 Connection.isValid() 를 호출 하여 커넥션 유효 검증 ⇒ Connection Pool에서 한 개의 Connection을 받아 사용하기 전에 isConnectionAlive 라는 메서드가 실행, Connection이 생성될 때 Hikari PoolEntry에 Scheduled Event를 걸어 maxLifetime 시간 이후에 강제적으로 Connection을 종료, 단, maxLifeTime에 2.5%의 변화를 주어 모든 Connection이 한순간에 종료되지 않도록 설정(negative attenuation)하여 고른 시간 분포를 이용해 가용 커넥션 부족 이슈를 방지

    참고) HikariPool 설정에서는 ConnectionTestQuery property가 null인 경우 Jdbc4Validation으로 동작

     

     

     

     

     

     

    그렇다면 데이터 베이스에서 결정하는 connection 유지 시간은 어떻게 알 수 있을까? 🤔

    DB로는 MySQL을 사용하고 있고, MySQL은 wait_timeout을 만큼 connection시간을 유지한다. 

     

    📘 Database wait_timeout

    데이터 베이스는 wait_timeout 만큼 기다렸다가, 사용중이지 않은 connection을 종료한다. 


     MySQL wait_timeout 

    The number of seconds the server waits for activity on a noninteractive connection before closing it.


    • 활동하지 않는 connection을 끊을때까지 서버가 대기하는 시간

    활동하지 않는 커넥션은 MySQL daemon이 global wait_timeout 값을 기준으로 종료시킨다. 

     

     

     

    💡 참고) wait_timeout의 구성
    • session wait_timeout : 현재 연결된 session에 대한 wait_timeout 값
    • global wait_timeout : 전역으로 적용되는 global wait_timeout 값

     

     

     

    정리하면, Hikari CP의 maxLifetime이 MySQL의 global wait_timeout보다 길었기 때문에 발생한 문제 다. maxLifetime은 별도의 설정이 없어 default 값인 180000ms로, MySQL의 wait_timeout은 확인결과 이보다 짧은 값으로 설정되어있었다. 

    30분마다 pool에서 사용되지 않는 connection을 종료시키려고 하였으나 이미 MySQL 프로세스는 종료된 상태로 닫힌 connection에 액션을 취하려고 하여 warn이 발생한 것이다. 

     

     

     

     

     

     

    해결 방법 

    그러면 maxLifetime은 wait_timeout 보다 얼마나 짧게 설정되어야 할까? 

    HikariCP는 네트워크 지연을 고려해서 maxLifetime을 wait_timeout 설정보다 2~5초 정도 짧게 줄 것을 권고하고 있다.

    (참고 : https://pkgonan.github.io/2018/04/HikariCP-test-while-idle)

     

     

    예전의 HikariCP는 커넥션 풀을 관리하기 위해 HouseKeeper라는 Thread가 30초마다 돌면서 커넥션을 종료했기 때문에 29.xx초까지의 커넥션에 대해 유효성 체크가 누락될 수 있어 30초정도 짧게 줄 것을 권고하였다. 하지만 현재 방식은 ThreadLocal을 사용하는 방식으로 변경되어 각각 타이머를 통해 max-lifetime에 도달했는지 체크하기 때문에 네트워크 통신 등을 감안해서 DB의 wait_timeout보다 2~5초 정도만 짧게 주면 된다. (시간이 많이 짧게 되면 대량의 커넥션 종료가 발생할 수 있음) 

     

     

     

     

     

    결과 

    서비스의 Hikari maxLifetime을 MySQL의 wait_timeout보다 3초 정도 작게 설정하여 문제를 해결했다. 

    spring :
      datasource:
        hikari:
          max-lifetime: {MySQL의 wait_timeout보다 3초 작은 값 (ms 기준)}

     

     

     

    설정 후 로그를 확인하니 더이상 Failed to validate connection WARN 로그는 발생하지 않는 것을 확인할 수 있었다!!💃🕺

     

     

     

     

     

     

     

     

     

     

    (참고한 블로그 🙌)

     

    GitHub - brettwooldridge/HikariCP: 光 HikariCP・A solid, high-performance, JDBC connection pool at last.

    光 HikariCP・A solid, high-performance, JDBC connection pool at last. - GitHub - brettwooldridge/HikariCP: 光 HikariCP・A solid, high-performance, JDBC connection pool at last.

    github.com

     

    HikariCP는 test-while-idle과 같은 커넥션 갱신 기능이 없을까?

    경험과 기억을 공유하다

    pkgonan.github.io

     

    '데이터베이스 > MYSQL' 카테고리의 다른 글

    mysql datetime과 timestamp의 차이  (0) 2023.10.31
Designed by Tistory.