解决HikariCP连接池提示TDengine ERROR (8000000b)问题

  • Post author:
  • Post category:其他




一、异常堆栈日志

[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



版权声明:本文为weixin_36146275原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。