我有一个使用 HikariCP 的 Spring Boot 应用程序。该应用程序连接到谷歌扳手数据库。我看到以下由 HikariCP 保持活动查询引起的会话泄漏异常
at com.google.cloud.spanner.SessionPool$PooledSessionFuture.markCheckedOut(SessionPool.java:1338)
at com.google.cloud.spanner.SessionPool$PooledSessionFuture.access$6200(SessionPool.java:1317)
at com.google.cloud.spanner.SessionPool.checkoutSession(SessionPool.java:3270)
at com.google.cloud.spanner.SessionPool.getSession(SessionPool.java:3251)
at com.google.cloud.spanner.SessionPool.getMultiplexedSessionWithFallback(SessionPool.java:3186)
at com.google.cloud.spanner.DatabaseClientImpl.getMultiplexedSession(DatabaseClientImpl.java:72)
at com.google.cloud.spanner.DatabaseClientImpl.singleUseReadOnlyTransaction(DatabaseClientImpl.java:182)
at com.google.cloud.spanner.connection.SingleUseTransaction.executeQueryAsync(SingleUseTransaction.java:237)
at com.google.cloud.spanner.connection.ConnectionImpl.internalExecuteQuery(ConnectionImpl.java:1592)
at com.google.cloud.spanner.connection.ConnectionImpl.internalExecute(ConnectionImpl.java:1022)
at com.google.cloud.spanner.connection.ConnectionImpl.execute(ConnectionImpl.java:1001)
at com.google.cloud.spanner.jdbc.AbstractJdbcStatement.lambda$execute$3(AbstractJdbcStatement.java:290)
at com.google.cloud.spanner.jdbc.AbstractJdbcStatement.doWithStatementTimeout(AbstractJdbcStatement.java:230)
at com.google.cloud.spanner.jdbc.AbstractJdbcStatement.execute(AbstractJdbcStatement.java:289)
at com.google.cloud.spanner.jdbc.JdbcStatement.executeStatement(JdbcStatement.java:257)
at com.google.cloud.spanner.jdbc.JdbcStatement.execute(JdbcStatement.java:248)
at com.zaxxer.hikari.pool.PoolBase.isConnectionDead(PoolBase.java:165)
at com.zaxxer.hikari.pool.HikariPool$KeepaliveTask.run(HikariPool.java:854)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
如果我设置泄漏检测阈值,我的应用程序就会开始崩溃。顺便说一句,从业务逻辑内部触发的数据库查询也是泄漏会话。我正在使用 FluentJDBC 来查询数据库。 POM和代码片段如下
var sourceDbHikariConfig = new HikariConfig();
sourceDbHikariConfig.setDriverClassName(config.getDriverClassName());
sourceDbHikariConfig.setJdbcUrl(config.getUrl());
sourceDbHikariConfig.setUsername(config.getUsername());
sourceDbHikariConfig.setPassword(config.getPassword());
sourceDbHikariConfig.setConnectionTestQuery(config.getConnectionTestQuery());
sourceDbHikariConfig.setKeepaliveTime(config.getKeepAliveTime());
sourceDbHikariConfig.setMaximumPoolSize(config.getMaxPoolSize());
sourceDbHikariConfig.setMinimumIdle(config.getMinIdleTime());
sourceDbHikariConfig.setConnectionTimeout(config.getConnectionTimeout());
return new HikariDataSource(sourceDbHikariConfig);
聚甲醛
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.myproject</groupId>
<artifactId>my-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>my-project</name>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.codejargon</groupId>
<artifactId>fluentjdbc</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner-jdbc</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-bigtable</artifactId>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>0.14</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.39.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
TLDR:至少更新至 Spanner JDBC 驱动程序版本 2.16.2。不过,建议始终使用最新版本的 Spanner JDBC 驱动程序。在撰写本文时,这是版本 2.20.1。
不幸的是,这是将 HikariCP 与 Spanner JDBC 驱动程序一起使用时的一个已知问题。它已在 JDBC 驱动程序版本 2.16.2 中修复。泄漏的原因是 Hikari 执行查询,然后从未真正读取返回的数据,也从未关闭
ResultSet
。相反,用于执行保持活动查询的 Statement
被关闭,这应该足以释放该语句使用的所有资源。然而 JDBC 驱动程序未能清理尚未使用的 ResultSet
。