当前位置:网站首页>记一次 ThreadLocal 泄漏导致的 shardingsphere-jdbc-core 单元测试偶发失败的排查与修复
记一次 ThreadLocal 泄漏导致的 shardingsphere-jdbc-core 单元测试偶发失败的排查与修复
2022-08-02 14:14:00 【[email prote】
文章目录
现象
https://github.com/apache/shardingsphere/issues/19346
本地运行 shardingsphere-jdbc-core 的单元测试没问题,但 GitHub Actions 中的 CI 有一定概率失败。
排查过程
尝试本地复现
尝试 Maven 运行单个模块的测试
使用 Maven 构建 shardingsphere-jdbc-core,多次尝试未能复现:
mvn clean install -pl shardingsphere-jdbc/shardingsphere-jdbc-core
尝试使用不同 JDK 运行模块的测试

尝试了以下几种 JDK:
- Oracle Java 17.0.1
- Eclipse Temurin 17.0.1
- Docker Official OpenJDK 17.0.1
均未能复现问题。
分析问题
分析日志
重新观察报错信息:

可以发现几乎所有的报错都指向同一个地方:

都是因为 TransactionType 是一个 Mock 的对象,导致获取不到 ShardingSphereTransactionManager。
TransactionType 的来源?
来源一:
TransactionRule 的 defaultType
来源二:
来源于 TransactionTypeHolder。但 TransactionTypeHolder 中的值的根本来源是 TransactionRule 的 defaultType。所以根源还是来源一。

TransactionTypeHolder 使用了 ThreadLocal 存储 TransactionType。由于单元测试在同一个模块中默认单线程串行运行,所以此处考虑有没有可能是单元测试中存在 ThreadLocal 泄漏?

由于报错的直接原因是 TransactionType 是个 mock 对象,因此我们先看看哪里有可能会产生 mock 的 TransactionType。
通过全局搜索发现,代码中并没有直接 mock TransactionType。但是存在不少 TransactionRule 的 RETURNS_DEEP_STUBS。

很可能是这些 TransactionRule 的 DEEP_STUBS 返回的 mock 的 TransactionType。
通过在 TransactionTypeHolder 打断点也能确认,部分单元测试会将 mock TransactionType 放入 ThreadLocal

再次尝试本地复现
但是本地如何才能重现问题证明泄漏?
分析测试执行顺序
根据经验,单元测试偶发报错很大可能和执行顺序有关系,观察 GitHub Actions 报错的单元测试执行顺序:
发现其中的 org.apache.shardingsphere.driver.jdbc.adapter.ConnectionAdapterTest 使用了 mock TransactionRule 并且在报错的测试之前执行:
相关链接:https://github.com/apache/shardingsphere/runs/7407025246?check_suite_focus=true
但是本地执行单元测试时,ConnectionAdapterTest 永远在会报错的测试后面,通过 Pattern 指定也无法控制执行顺序。
想办法调整单元测试执行顺序
换个思路,如果我们把 ConnectionAdapterTest 测试类挪个位置?
在本地把 ConnectionAdapterTest 挪到 org.apache.shardingsphere.driver.jdbc.core.statement 包下面:

再次执行测试,本地复现成功:

修复方案
单元测试中凡是涉及 mock TransactionRule DEEP_STUBS 的地方都需要清理 ThreadLocal。
https://github.com/apache/shardingsphere/pull/19362
如何避免?
与 mockStatic 泄漏不同,使用 ThreadLocal 的是生产代码,对于不熟悉代码的人根本不知道里面使用了 ThreadLocal,更不知道会有泄漏的风险。最好的解决方案是从生产代码上避免使用 ThreadLocal,但需要考虑实际情况。
版权声明
本文为[[email protected]]所创,转载请带上原文链接,感谢
https://blog.csdn.net/wu_weijie/article/details/126059089
边栏推荐
猜你喜欢
随机推荐
golang gc垃圾回收
【网络安全】学习笔记 --02 安全通信协议
使用1D-1D EPE的光波导布局设计工具
Oauth2.0 认证服务器添加验证码登陆方式
企业的电子签名、私钥签名
Problems related to prime numbers - small notes
shader入门精要1
CDH(computational Diffie-Hellman)问题以及与离散对数、DDH问题的区别
线性结构,顺序结构
golang-reflect-method-callback
MySQL协议长什么样子
光波导的入射耦合和出射耦合区域
5款最好用的免费3D建模软件(附下载链接)
锥形相位掩模的Talbot图像
类模板/赋值运算和加等运算
创建系统还原点及恢复
图解MESI(缓存一致性协议)
深入理解Mysql索引底层数据结构与算法
学习笔记(01):activiti6.0从入门到精通-工作流的介绍以及插件的安装
Debug on pure method is called









