Database

MariaDB Java Client + AWS Aurora MySQL 사용시 Aurora 버전 업그레이드 시 에러 및 해결방법 (Caused by java.sql.SQLNonTransientConnectionException: (conn=11812758) Connection is closed)

joohyukkim 2024. 2. 22. 23:41

1. 재현환경

  • MariaDB Java Client 2.x.x 버전
  • AWS Aurora MySQL 2.x.x 버전
    • Aurora Mysql 2.04.8 버전까지는 Good
    • 이후의 minor 버전부터 문제, Aurora Mysql 2.07.x 버전부터는 확실

2. 해결방법

필자와 같이 바쁜 직장인들을 위해 빠르게 결론부터 전달 드리겠습니다.

DB 커넥션 스트링에 &usePipelineAuth=false&useBatchMultiSend=false 부분을 추가하면 됩니다.


기본값이 true인 옵션 usePipelineAuthuseBatchMultiSendfalse 로 설정합니다.

 

예를들어 Connection String 이....

 

jdbc:mysql://mysql-aurora-seoul.myapp.kr:3306/my_db?serverTimezone=Asia/Seoul&characterEncoding=utf8

 

 

.... 라면 아래와 같이....

 

 

jdbc:mysql://mysql-aurora-seoul.myapp.kr:3306/my_db?serverTimezone=Asia/Seoul&characterEncoding=utf8&usePipelineAuth=false&useBatchMultiSend=false

 

 

.... 이런식으로 설정해줍니다.


3. 증상 (자바 에러 메시지)

3.a. 주요 에러 메시지

  1. Caused by: java.sql.SQLNonTransientConnectionException: (conn=11812758) could not load system variables
  2. Caused by: java.sql.SQLNonTransientConnectionException: (conn=11812758) Connection is closed

3.b. StackTrace 전체

아래 스택트레이스 중 at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115) 부분 보면 DBCP 초기화 과정에서 에러난 것을 확인 할 수 있습니다.

...다른 프레임워크 + 라이브러리 에러 생략
Caused by: java.sql.SQLNonTransientConnectionException: Could not connect to address=(host=****My_DB_URL****)(port=3306)(type=master) : (conn=11812758) could not load system variables
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:197)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1394)
    at org.mariadb.jdbc.internal.util.Utils.retrieveProxy(Utils.java:635)
    at org.mariadb.jdbc.MariaDbConnection.newConnection(MariaDbConnection.java:150)
    at org.mariadb.jdbc.Driver.connect(Driver.java:89)
    at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
    at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:364)
    at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206)
    at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:476)
    at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:561)
    at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115)
    at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180)
    at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:43)
    ... 42 common frames omitted
Caused by: java.sql.SQLNonTransientConnectionException: (conn=11812758) could not load system variables
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:188)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.requestSessionDataWithShow(AbstractConnectProtocol.java:1114)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.readPipelineAdditionalData(AbstractConnectProtocol.java:1075)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.postConnectionQueries(AbstractConnectProtocol.java:885)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.createConnection(AbstractConnectProtocol.java:600)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1389)
    ... 55 common frames omitted
Caused by: java.sql.SQLNonTransientConnectionException: (conn=11812758) Connection is closed
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:192)
    at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.cmdPrologue(AbstractQueryProtocol.java:1946)
    at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.executeQuery(AbstractQueryProtocol.java:249)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.requestSessionDataWithShow(AbstractConnectProtocol.java:1089)
    ... 59 common frames omitted

4. Deeper : 문제의 MariaDB-Java-Client 옵션 설명

useBatchMultiSendusePipelineAuth 옵션 모두 MariaDB JDBC 드라이버에서 제공하는 고급 설정으로 네트워크 지연을 줄이고 성능을 향상시키기 위해 설계되었습니다.
하지만 두 설정 모두 AWS Aurora와 호환되지 않습니다.

4.a. 설정 useBatchMultiSend

해당 옵션은 여러 쿼리를 배치로 묶어 전송할 수 있게 해줍니다.

if true, 활성화시...

  • useBatchMultiSendNumber 옵션 값(기본값 100)에 맞는 크기의 배치로 쿼리들을 전송하거나...
  • max_allowed_packet 옵션의 패킷 크기 제한이 허용하는 만큼의 쿼리를 배치로 전송합니다.
  • 활성화 시 클라이언트와 서버가 같은 호스트에 있지 않고 멀리 떨어져 있을 때 발생하는 많은 네트워크 지연을 피할 수 있습니다.

if false, 비활성화시...
쿼리들은 하나씩 전송되며, 결과를 받아올 때까지 다음 쿼리를 전송하지 않습니다.

 

4.b. 설정 usePipelineAuth

해당 옵션 활성화 시 DB 커넥션 생성 과정에서 실행하는 다양한 쿼리를 파이프라인을 통해 전송합니다.
모든 쿼리가 한 번에 전송되고, 그 결과는 모두 읽힌 후에 처리되고 이로 인해 연결 생성 속도가 최적화됩니다.

 

NOTE 1: 위에서 이야기하는 쿼리들은 Show variables...와 같은 것들로 에러 발생시의 Java StackTrace를 따라 DBCP 코드를 보면 확인할 수 있습니다

 

NOTE 2: 문제 재현시 출력되는 could not load system variables 에러 메시지를 통해 usePipelineAuth 옵션과 연결되어있다는 것을 확인할 수 있습니다.

5. Deeper : useBatchMultiSend 옵션 동작에 대한 추가 설명 (공식 블로그 글 참고)

5.a. 짧은 버전

  • 동작 : 여러 쿼리를 한번에 보내고 실행되는 대로 결과를 받아온다.
  • 장점 : 네트워크 지연을 줄인다.
  • 단점 : 부분적으로 실패하고 부분적으로 이미 실행될 수도 있다. ***조회만 전송할 때 요긴하게 쓰일 것으로 생각된다(이미 어딘가에서 그렇게 하고 있을것으로 예상된다.)

5.a. 긴 버전


6. 대응방안 리스트

1. MariaDB Java Client 버전 올리기

 

2. Aurora 전용 클라이언트 사용

 

2.a. 한줄 요약

DB 개발하는 측(AWS Aurora) 에서 만든 DB Connector (AWS DB Driver) 를 사용한다고 볼수있습니다.

 

2.b. 단톡방 동료분께서 말씀하시길... 

Aurora가 생각보다 JDBC 세부옵션에 대해서 안되는게 많습니다.
예를들어 cursor fetch 도 포함이에요.
Aurora 전용 클라이언트는 wrapper 입니다.
Aurora는 cluster 구성이고, cluster endpoint를 dns를 통해서 제공합니다.
dns로 ha를 제공할 때, ttl 때문에 instance ip가 바뀌지 않아 ha 전환이 되지 않는 문제가 발생하는 경우가 있는데요.기본 jdbc 드라이버는 dns flush나, failover detection 기능이 없기 때문에,aurora가 mysql이나 mariadb 드라이버를 래핑해서, failover 감지 시 dns flush를 하는 로직 정도 추가한걸로 알아요

 

3. ConnectionString 설정으로 해결


7. MariaDB Connector 버전 3.3 / SpringBoot 3.x 버전 업 이슈

7.a. mariadb connector 버전 3.3 부터는 더이상 Aurora HA 모드를 사용하는 연결 문자열의 사용 불가능해진다.(***이건 각자 상세하게 확인 부탁드립니다.)

 

REF: https://mariadb.com/docs/server/connect/programming-languages/java/upgrade/

 

Open Source Database (RDBMS) for the Enterprise | MariaDB

MariaDB is the leading enterprise open source database with features previously only available in costly proprietary databases. Enterprise grade, wallet friendly.

mariadb.com

 

7.b. Spring Boot 3.x 부터 기본 mariadb connector 버전이 3.3 🔥

아마 spring boot 3.x 부터 기본 mariadb connector 버전이 3.3 으로 들어가는 이슈가 있어 인지 하면 좋을거 같습니다.

해당 라이브러리 도입 검토 해보았을때 failover 시 기존 mariadb보다 조금 느린게 있었습니다.

https://github.com/awslabs/aws-mysql-jdbc

 

GitHub - awslabs/aws-mysql-jdbc: The Amazon Web Services (AWS) JDBC Driver for MySQL is a driver that enables applications to ta

The Amazon Web Services (AWS) JDBC Driver for MySQL is a driver that enables applications to take full advantage of the features of clustered MySQL databases. - GitHub - awslabs/aws-mysql-jdbc: Th...

github.com


 

8. References


9. Notes

집요하게 문제 짚어주시고 계속해서 같이 봐주신 저희 팀 기현님에게 감사를 🙏🏼

언제나 그렇듯이… 질문은 제 링크드인으로 메시지 주시면 빠른 답변 드리도록 하겠습니다!

https://www.linkedin.com/in/joo-hyuk-kim/