一,背景
有一天,业务反馈部分自动执行流程中断了,我第一反应是不是程序报错,排查半天也没有error错误。 那就开始扒代码呗(代码非本人开发)。
二,抽丝剥茧
直接上简化版的代码
@Transaction(rollbackFor = Exception.class)
publict Result a(){
//做一些操作
try{
CompletableFuture.supplyAsync(()->
serviceB.b();
);
}catch(Exception ex){
//做一些通知操作,并不回滚a的事务,后续由补偿任务触发b的生成
}
}
@Transaction(rollbackFor = Exception.class)
publict Result b(){
//做一些操作
}
意思就是b的一些操作没有落库,那肯定就是回滚了,由于是异步,异常日志都没有打出来,因为捕获异常不应该在completableFuture的外面,应该这样写。
CompletableFuture.supplyAsync(()->
try{
serviceB.b();
}catch(Exception ex){
//做一些通知操作,并不回滚a的事务,后续由补偿任务触发b的生成
}
);
这个暂且不提,现在就看b(),里面有没有线索了,好在里面有一些日志,但是日志打着就断了,看不到具体执行到哪一行出错了。结合当前错误是偶发的,也就是说部分数据异常,大部分的数据是正常执行的。测试环境当然也复现不了。
大胆猜测,小心求证,因为该方法是单机执行,假设是由于数据库执行异常了,导致了当前事务回滚,好像可以对得上,那就看下应用有无数据库的error异常,结果没有,想会不会是数据库连接超时了,找日志warn一看,果然有不少超时的连接。 那为啥,应用不报错呢,这个时候注意到数据库连接有个参数,
autoReconnect=true&failOverReadOnly=false
果然,无事务情况下,直接就重连了,也就响应慢一点,有事务的话,肯定先回滚当前事务了,这就和我们的现象对得上了。
三,解决方案
下面就是求证了,把hikari连接池设置只有1个连接,debug进去,设置为超时,执行,果然回滚,至此,原因确认了。 解决也好解决,设置好连接池的如下两个参数:
dataSource.setMaxLifetime(800_000);
dataSource.setIdleTimeout(600_000);