SpringBoot应用读写Cloud Spanner触发LeakedSessionException排查
Spanner会话泄漏异常排查与修复
根因判定
你看到的com.google.cloud.spanner.SessionPool$LeakedSessionException不是配置缺失导致的阻断式业务异常,是Spanner客户端会话池的资源泄漏检测告警:会话从连接池借出后未被正常归还,当该会话对象被JVM垃圾回收时,会话池的兜底检测逻辑会打出该warn级别日志,因此当前业务读写还能正常执行,不会立刻出现请求失败。
你捕获到的异常核心信息如下:
Exception: com.google.cloud.spanner.SessionPool$LeakedSessionException Message: Session was checked out from the pool at 2022-07-13T17:50:07.372Z Stacktrace 核心调用路径: com.google.cloud.spanner.SessionPool.checkoutSession(SessionPool.java:2037) com.google.cloud.spanner.DatabaseClientImpl.singleUse(DatabaseClientImpl.java:101) com.google.cloud.spring.data.spanner.core.SpannerTemplate.executeQuery(SpannerTemplate.java:448)
风险评估
- 低流量短期运行场景无直接业务影响:当前连接池仍有可用会话分配给新请求,业务读写逻辑可正常执行
- 高流量长期运行场景存在明确故障风险:泄漏的会话会持续占用连接池配额,当池内会话全部被泄漏占用后,新请求无法获取数据库连接,会直接抛出连接池耗尽异常,导致所有Spanner操作失败
- 存在不必要的资源开销:泄漏会话对应的Spanner服务端连接资源不会被主动释放,会持续产生额外的资源占用成本
触发原因
结合你使用的6.10.1版本客户端和调用栈信息,触发原因主要有两类:
- 客户端版本已知bug:6.10.1版本的Spanner客户端存在
singleUse()只读查询场景的会话归还逻辑缺陷,当查询返回的ResultSet未被显式关闭时,即使查询执行完成,会话也不会被自动归还到连接池,和你调用栈中DatabaseClientImpl.singleUse的调用路径完全匹配 - 代码资源释放不规范:如果存在手动从客户端获取会话、手动管理事务的逻辑,没有在finally块或try-with-resources块中完成资源关闭,也会触发会话泄漏
修复方案
按优先级执行即可:
- 升级客户端版本(首选方案)
将google-cloud-spanner依赖升级到6.24.0及以上稳定版本,该版本已经修复了singleUse查询场景的会话自动归还bug,配合对应兼容版本的Spring Data Spanner,无需修改业务代码即可解决绝大多数场景的泄漏问题。
你当前使用的依赖配置如下,直接替换version字段为高版本稳定版号即可:<dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-spanner</artifactId> <version>6.10.1</version> <scope>compile</scope> </dependency> - 规范资源释放逻辑
排查所有Spanner操作相关代码,所有ResultSet、手动获取的Session、手动开启的TransactionContext必须放在try-with-resources块中执行,确保无论执行成功还是抛出异常,资源都能被自动关闭:
手动管理事务的场景,必须将commit/rollback逻辑放在finally块中,避免异常分支跳过资源释放。// 错误写法:不关闭ResultSet,会触发会话泄漏 ResultSet rs = spannerTemplate.executeQuery(query, params, option); while (rs.next()) { // 处理结果逻辑 } // 正确写法:自动关闭资源 try (ResultSet rs = spannerTemplate.executeQuery(query, params, option)) { while (rs.next()) { // 处理结果逻辑 } } - 临时兜底配置(升级过渡阶段使用)
若暂时无法升级版本,可通过配置会话池参数临时降低泄漏影响:
注意:该配置仅作临时缓解,无法根治会话泄漏,升级版本和修正代码逻辑才是最终解决方案# 自动回收检测到泄漏的会话 spring.cloud.gcp.spanner.session-pool.close-sessions-on-leak=true # 配置会话池容量上下限,避免池被快速打满 spring.cloud.gcp.spanner.session-pool.min-sessions=10 spring.cloud.gcp.spanner.session-pool.max-sessions=400 # 回收超过空闲时长的会话 spring.cloud.gcp.spanner.session-pool.max-idle=10m
内容的提问来源于stack exchange,提问作者codingNubie
相关产品推荐
相关产品推荐

