데이터베이스
, 스키마
, 테넌트
동의어로 생각하고 작성한다.
HikariDataSource
로 연결됨을 확인할 수 있었다.// MultiTenantConnectionProviderImpl
...
@Override
public Connection getAnyConnection() throws SQLException {
return dataSource.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
String tenantId = TenantContext.getCurrentTenantId();
Connection connection = getAnyConnection();
connection.setSchema(tenantId);
connection.createStatement()
.execute(String.format("use `%s`;", tenantId));
return connection;
}
...
Sentry의 이슈 현상 처럼 USE database
SQL문을 실행하는 과정에서 예외가 발생한다면 커넥션풀의 상태는 어떻게 될까?
예외 발생 후에도 여전히 ACTIVE 상태의 커넥션이 하나 존재한다.
Total
: 전체 커넥션의 수, 활성화 상태와 유휴 상태를 커넥션 수Active
: 활성화 상태, 어플리케이션에서 커넥션풀로부터 해당 커넥션을 가져감.Idle
: 유휴 상태, DB로부터 maximum-pool-size
만큼의 커넥션을 연결하고 커넥션풀에 담아둠.Waiting
: 대기 상태, 모든 커넥션이 Active 상태로 커넥션이 커넥션풀로 반환되기를 기다림.connectionTimeout (default : 30000 (30 seconds))
설정한 최대 대기 시간을 초과하면 SQLException을 발생시킴.Connection connection = getAnyConnection();
실행되면 커넥션풀은 커넥션은 반환하고 active 상태로 변경한다.@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
String tenantId = TenantContext.getCurrentTenantId();
Connection connection = getAnyConnection(); // 1. 커넥션풀로 커넥션을 가져옴(active=1, idle=9)
connection.setSchema(tenantId);
connection.createStatement()
.execute(String.format("use `%s`;", tenantId)); // SQLException 발생
// 해당 커넥션은 release 되지 못하고 커넥션 누수가 발생!!
...
}
maximum-pool-size
를 크게 잡았더라도 마이그레이션이 제대로 되어있지 않다면 사용 가능한 커넥션은 지속적으로 감소했을 것이다.// MultiTenantConnectionProviderImpl
...
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
String tenantId = TenantContext.getCurrentTenantId();
Connection connection = getAnyConnection();
try {
connection.setSchema(tenantId);
connection.createStatement()
.execute(String.format("use `%s`;", tenantId));
} catch (Exception e) {
releaseAnyConnection(connection);
throw e;
}
return connection;
}
...
maxLifeTime
설정으로 최대 시간 후에 강제로 반환하도록 할 수 있지 않을까?maxLifeTime (default : 1800000 (30분))
으로 테스트를 진행한다.
11:32
: HikariPool-1 - Pool stats (total=10, active=0, idle=10, waiting=0)
11:33
: exception 발생 - HikariPool-1 - Pool stats (total=10, active=1, idle=9, waiting=0)
12:31
: 30분 초과된 후 maxLifeTime 발동으로 커넥션 close 후 add 확인 가능12:36
: 상태는 이전과 동일 - HikariPool-1 - Pool stats (total=10, active=1, idle=9, waiting=0)maxLifeTime
은 커넥션풀에 유휴(idle) 상태의 커넥션이 유지되는 시간이다.connection의 최대 유지 시간을 설정합니다.
connection의 maxLifeTime 지났을 때, 사용중인 connection은 바로 폐기되지 않고 작업이 완료되면 폐기됩니다.
하지만 유휴 커넥션은 바로 폐기됩니다.
maxLifeTime
이 지나고 유휴 상태의 커넥션 9개가 close/add 되는 모습이다.connectionTimeout
maximumPoolSize
minimumIdle
idleTimeout
maxLifetime
show variables like '%max_connect%';
show variables like '%timeout%';
maxLifetime
,idleTimeout
유휴(idle) 상태의 커넥션을 관리한다. DB에 설정된 커넥션 수(90개)를 조금 더 효율적으로 사용하도록 하는 설정이다. 하지만, 활성화(active) 상태의 커넥션을 강제로 끊는 것이 불가하다.
HikariCP는 네트워크 지연 시간을 고려하여
maxLifetime
설정은 MySQL의 wait_timeout 설정보다 2~3초 정도 짧게 줄 것을 권고한다.
# HikariCP DEBUG Options
logging.level.com.zaxxer.hikari.HikariConfig=DEBUG
logging.level.com.zaxxer.hikari=TRACE
datasource.hikari.leak-detection-threshold=2000
잘봤어요