事情发生在我们线上的一个遗留问题,问题大致情况是我们引入的一个库会导致服务在运行一段时间后出现死锁,并且会造成整个网站无法访问。
我们发现通过配置库的一些参数可以避免死锁的情况发生,但是会引发事务无法回滚的新问题。
由于后者相对故障等级更低一些,我们首先进行了故障降级,选择了优先保障服务运行。 后面则是排查为什么会新的配置会出现事务无法回滚,这个事情由我负责排查。
从部门同事那了解了问题的详细情况后我就开始着手进行排查。首先是对问题进行梳理把新引入的库与框架之间的关系以及整体的一个运行流程进行了梳理。
如果要对整个流程做详细的了解,需要阅读的代码量是十分庞大的,不仅需要阅读库,还需要对框架源码进行阅读。
就解决问题而言,我认为需要做粗略的流程梳理即可,通过对整个流程的一个大致了解,然后确定问题的具体发生位置,然后对具体位置进行详细了解。
在粗略梳理的过程中,我从新引入库开始着手,主要是由于库的代码量相对较少,阅读起来相对更快一些,且如果仅从库层面就能解决问题,也可以省略的框架代码的阅读。
对于库源码的了解我通过 wiki + 源码的方式,这样能够更省力的了解库的整个运行机制。
然而了解了库的运行机制后,我发现问题可能出现在库与框架的配合上。需要涉及到框架源码的阅读。
在开始阅读框架源码前,我决定在本地构建一个复现环境,通过对构建出的本地环境对源码进行 debug 调试。
经过反复的 debug 最终终于确定了问题出现的原因,是由于新库引入后,事务开启时新建一个数据库连接,而通过 ORM 进行数据库查询时会创建另一个数据库连接。这就导致事务和 SQL 的执行不是在同一个数据库连接中,也就造成了数据库事务无法生效的问题。
确定了问题的原因解决方案就好确定了,我们只需要保证事务和 SQL 执行在同一个连接中即可,而解决这个问题只写了三行代码,既保证了功能的完整,没有侵入库和框架进行底层源码的改造。
这次问题的排查让我想到了福特公司用一万美元画一条线的故事。
画一条线,1美元;知道在哪儿画线,9999美元。
这次的问题离不开部门同时的帮助,在排查的过程中和同事进行了 2-3 轮的沟通。不仅让我对问题有一个全面的认识,也几次在我遇到困惑时,给了我解决问题的灵感,包括在最后解决方案制定后的论证,都蕴含了整个团队的心血。
也希望这次的一个排查经过能够对看文章的你有一些启发,欢迎你分享你的感悟和思考。