본문 바로가기

MySQL

JPA + MySQL 파이프가 깨어짐 (Disconnector)

환경

- JDK 1.8 (java)
- SpringBoot
- MySQL
- JPA

Disconnect에 대한 테스트 (예외 발생)


spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
 
# sessionVariables=wait_timeout=20 핵심이다. (해당 세션일 때 wait_timeout의 시간을 20초로 세팅한다.)
# 의미 : request가 20초이상 들어오지 않으면 해당 connector의 연결을 끊어버린다.
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?sessionVariables=wait_timeout=20
spring.datasource.username=root
spring.datasource.password=root
 
# 테스트를 위해 Connection Pool을 1로 두었다.
spring.datasource.max-active=1
spring.datasource.max-idle=1
spring.datasource.min-idle=0

20초가 지난 후 DB 쿼리를 요청하면 어떻게 되는가?
20초 후 요청하면 에러가 발생한다.

Caused by: java.net.SocketException: Software caused connection abort: recv failed
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:170)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:100)
	at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:143)
	at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:173)
	at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2954)
	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3375)
	... 110 common frames omitted

- 사실.. 실제 에러는 broken pipe라고.. (파이프가 깨어짐) 발생하는데
최대한 유사한 현상재현을 한 것이다. (동일 SocketException 발생 ㅎ)
- spring.datasource.test-on-borrow=true 로 되어 있어도 에러는 발생한다! 
- test-on-borrow는 connector를 얻어올때 validation-query로 검증하는 설정인데 왜 에러가 나는지는 모르겠다. ;; 이론상 이해가 안된다.
예외가 발생하더라도 몇 초뒤 다시 연결을 시도하면 재연결이된다.

Evictor Thread로 Connector를 감시하자

- test-while-idle을 true로 하고 time-between-eviction-runs-millis을 6000으로 해보자 
- 6초마다 Evictor Thread가 활성화되어 Connection Pool에 있는 Connector들에 대해 유효검사를 실시한다.
기본 값이 -1이라 꼭 시간 설정을 해줘야한다.
- min-evictable-idle-time-millis은 해당 connector의 유휴시간이 지났을 때 Evictor Thread가 connector를 제거하도록 한다.
Test 설정 파일 - 최종

spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
 
# sessionVariables=wait_timeout=20 핵심이다. (해당 세션일 때 wait_timeout의 시간을 20초로 세팅한다.)
# 의미 : request가 20초이상 들어오지 않으면 해당 connector의 연결을 끊어버린다.
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?sessionVariables=wait_timeout=20
spring.datasource.username=root
spring.datasource.password=root
 
# connector 유효 검증 쿼리
spring.datasource.validation-query=SELECT 1
spring.datasource.connection-test-query=SELECT 1

# 테스트를 위해 Connection Pool을 1로 두었다.
spring.datasource.max-active=1
spring.datasource.max-idle=1
spring.datasource.min-idle=0
 
# Evictor Thread가 test-while-idle이 true면 6초마다 connection pool에 validation-query를 수행
# 또한 min-evictable-idle-time-millis의 유후 시간(3초)이 지난 connector도 제거 (기본값 -1 : Evictor Thread 비활성화)
spring.datasource.time-between-eviction-runs-millis=6000
spring.datasource.min-evictable-idle-time-millis=3000
spring.datasource.test-while-idle=true
 
# connector를 가져올때 validation-query를 수행하여 검증한다고함
spring.datasource.test-on-borrow=true

이렇게 하니깐 에러가 안난다. test-on-borrow=true 로 한 이유는 requst가 별로 없어 성능 이슈가 없기 때문에 안정성에 더 초점을 두었다.

많은 도움이 된 링크 (NAVER D2)

- http://d2.naver.com/helloworld/5102792