解决HikariCP连接池提示TDengine ERROR (8000000b)问题
一、异常堆栈日志
[xxx-xxx-xxx/v1.0.0 - 192.168.0.9:8080]2022-10-0802:00:09.086WARN 1[][http-nio-6666-exec-8]
com.zaxxer.hikari.pool.PoolBaseTDengineHikariCP - Failed to validate connection com.taosdata.jdbc.TSDBConnection@13788ad1
(TDengine ERROR (8000000b): Unable to establish connection). Possibly consider using ashorter maxLifetime value.
二、查看HikariCP源码
2.1 CodahaleHealthChecker类
定位到
CodahaleHealthChecker
类确认异常发生位置,在
check函数
中执行健康检查
pool.getConnection(checkTimeoutMs)
public final class CodahaleHealthChecker{
...
...
...
@Override
protected Result check() throws Exception
{
try (Connection connection = pool.getConnection(checkTimeoutMs)) {
return Result.healthy();
}
catch (SQLException e) {
return Result.unhealthy(e);
}
}
...
...
...
}
2.2 HikariPool类
定位到
getConnection函数
,看到是属于
HikariPool类
的函数,其内部实现了
isConnectionAlive(poolEntry.connection)
的判断
public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateListener
{
...
...
...
public Connection getConnection(final long hardTimeout) throws SQLException
{
suspendResumeLock.acquire();
final long startTime = currentTime();
try {
long timeout = hardTimeout;
do {
PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
if (poolEntry == null) {
break; // We timed out... break and throw exception
}
final long now = currentTime();
if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
timeout = hardTimeout - elapsedMillis(startTime);
}
else {
metricsTracker.recordBorrowStats(poolEntry, startTime);
return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
}
} while (timeout > 0L);
metricsTracker.recordBorrowTimeoutStats(startTime);
throw createTimeoutException(startTime);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
}
finally {
suspendResumeLock.release();
}
}
...
...
...
}
2.3 分析静态代码块
从
isConnectionAlive(poolEntry.connection)
的实现看到,大概是因为调用
connection.createStatement()
发生异常触发了
catch代码块
的执行,抛出:
(TDengine ERROR (8000000b): Unable to establish connection). Possibly consider using ashorter maxLifetime value.
boolean isConnectionAlive(final Connection connection)
{
try {
try {
setNetworkTimeout(connection, validationTimeout);
final int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;
if (isUseJdbc4Validation) {
return connection.isValid(validationSeconds);
}
try (Statement statement = connection.createStatement()) {
if (isNetworkTimeoutSupported != TRUE) {
setQueryTimeout(statement, validationSeconds);
}
statement.execute(config.getConnectionTestQuery());
}
}
finally {
setNetworkTimeout(connection, networkTimeout);
if (isIsolateInternalQueries && !isAutoCommit) {
connection.rollback();
}
}
return true;
}
catch (Exception e) {
lastConnectionFailure.set(e);
logger.warn("{} - Failed to validate connection {} ({}). Possibly consider using a shorter maxLifetime value.",
poolName, connection, e.getMessage());
return false;
}
}
三、翻阅TDengine官方文档
3.1 参考TDengine与连接池HikariCP的使用[^1]
3.2 查看应用的yaml配置
目前
max-lifetime
设置了1800000(单位ms)即30min,此时会抛出Failed to validate connection的异常
#数据源配置
spring:
datasource:
tdengine:
driver-class-name: com.taosdata.jdbc.TSDBDriver
jdbc-url: jdbc:TAOS://xxx-hostname:6030/xxx?enableMicrosecond=true&charset=UTF-8&locale=en_US.UTF-8
username: xxx
password: xxx
...
...
...
#连接池名
pool-name: TDengineHikariCP
#最大生命周期
max-lifetime: 1800000
#连接超时时间
connection-timeout: 30000
connection-test-query: select 1
四、更新max-lifetime
按TDengine官方示例,将
max-lifetime
设置为0,表示最大生命周期。这里
正确的做法
应该是逐步调小max-lifetime的数值,直到不再抛出异常,参考Github HikariCP issues[^2]
#数据源配置
spring:
datasource:
tdengine:
...
#最大生命周期
max-lifetime: 0
...
五、观察应用运行状态
检查是否仍抛出异常
(TDengine ERROR (8000000b): Unable to establish connection). Possibly consider using ashorter maxLifetime value.
参考内容:
[1]:
TDengine与连接池HikariCP的使用 https://docs.taosdata.com/2.4/reference/connector/java/#hikaricp
[2]:
逐步调小max-lifetime参数值 https://github.com/brettwooldridge/HikariCP/issues/1651