r2dbc-pool 설정시 커넥션 풀이 제대로 생성되지 않은 케이스 확인
문제 확인
r2dbc-mysql을 사용해 개발한 애플리케이션에서 netstat | grep 3306으로 조회시 ESTABLISHED로 조회되는 개수가 커넥션 풀에 설정한 값과 상이한것을 확인함 .
r2dbc 풀 설정시 initSize가 20, maxSize가 50인데 아래와 같이 설정과는 다르게 커넥션이 생성되 있는 것을 확인함.
- A 인스턴스는 2개
- B 인스턴스는 10개
- C 인스턴스는 20개
테스트 환경
- Kotlin:1.4.21
- spring boot:2.3.7.RELEASE
- dev.miku:r2dbc-mysql:0.8.2.RELEASE
원인 파악
as-is
import io.r2dbc.pool.ConnectionPool
import io.r2dbc.pool.ConnectionPoolConfiguration
import io.r2dbc.spi.ConnectionFactories
import io.r2dbc.spi.ConnectionFactory
import io.r2dbc.spi.ConnectionFactoryOptions.*
@Configuration
@EnableR2dbcRepositories
class DatabaseConfiguration: AbstractR2dbcConfiguration() {
override fun connectionFactory(): ConnectionFactory {
val connectionFactory = ConnectionFactories.get(
builder()
.option(DRIVER, "mysql")
.option(HOST, host)
.option(USER, userName)
.option(PORT, port)
.option(PASSWORD, passWord)
.option(DATABASE, db)
.build())
val configuration = ConnectionPoolConfiguration.builder(connectionFactory)
.maxIdleTime(Duration.ofSeconds(maxIdleTime))
.maxCreateConnectionTime(Duration.ofSeconds(maxCreateConnectionTime))
.maxLifeTime(Duration.ofMinutes(maxLife))
.initialSize(initialSize)
.maxSize(maxSize)
.build()
return ConnectionPool(configuration)
}
}
분명 ConnectionPoolConfiguration에 initialSize와 maxSize를 넣었는데 비정상적으로 동작하는 것을 확인.
to-be
option(DRIVER, "pool")
을 추가하였음. 이 설정은 추가하는 이유는 github.com/r2dbc/r2dbc-pool를 보면 Supported ConnectionFactory Discovery Options섹션에 아래와 같이 무조건 pool(Must be pool)로 설정하라고 되어 있음
driver | Must be pool |
import io.r2dbc.pool.PoolingConnectionFactoryProvider.*
import io.r2dbc.spi.ConnectionFactories
import io.r2dbc.spi.ConnectionFactory
import io.r2dbc.spi.ConnectionFactoryOptions.*
@Configuration
@EnableR2dbcRepositories
class DatabaseConfiguration: AbstractR2dbcConfiguration() {
override fun connectionFactory(): ConnectionFactory =
ConnectionFactories.get(
builder()
.option(DRIVER, "pool")
.option(PROTOCOL, "mysql")
.option(HOST, readPoolProperties.host)
.option(USER, readPoolProperties.username)
.option(PORT, readPoolProperties.port)
.option(PASSWORD, readPoolProperties.password)
.option(DATABASE, readPoolProperties.db)
.option(MAX_SIZE, r2dbcPoolProperties.maxSize)
.option(INITIAL_SIZE, r2dbcPoolProperties.initialSize)
.option(MAX_IDLE_TIME, Duration.ofSeconds(r2dbcPoolProperties.maxIdleTime))
.option(MAX_CREATE_CONNECTION_TIME, Duration.ofSeconds(r2dbcPoolProperties.maxCreateConnectionTime))
.option(MAX_LIFE_TIME, Duration.ofMinutes(r2dbcPoolProperties.maxLife))
.build()
)
}
netstat으로 조회시 초기값에 맞춰서 ESTABLISHED된 것을 확인함.
r2dbc-pool 소스를 디버그해보니 option(DRIVER, "pool")이 있고 없고에 따라서 ConnectionFactoryProvider
가 달라지는 것을 확인 함
option(DRIVER, "mysql")로 설정시 dev.miku.r2dbc.MySqlConnectionFactoryProvider
가 동작하고
option(DRIVER, "pool")로 설정시 io.r2dbc.pool.PoolingConnectionFactoryProvider
가 동작하는데dev.miku.r2dbc.MySqlConnectionFactoryProvider
에는 initSize, maxSize에 대한 설정이 없고(github.com/mirromutth/r2dbc-mysqlgithub.com/mirromutth/r2dbc-mysql) 풀링되는 커넥션 팩토리가 아니기 때문에 그때그때마다 필요한 커넥션을 생성하는 방식이었음.
결론
r2dbc-pool을 사용해 커넥션 풀링을 정상적으로 이용하고 싶다면 option(DRIVER, "pool")
옵션은 필수이다.