碰到一位同事求助解决消息中台一个线上的bug,具体描述如下:
首先有一张主表记录消息待发送的内容,一张子表记录本条消息的发送状态。若发送成功则将此条消息的发送状态修改为已发送并做逻辑删除。代码通过定时任务每2s轮询子表,如果状态是未发送且没有被删除的记录,则进行发送。发送前代码会对当前消息更新加入UPDLOCK与READPAST,期望下一次轮询时能够自动跳过被锁(发送中)的数据,发送完成后更新状态提交事务,行锁自动释放。
现在碰到的问题是子表整张表发生死锁,导致消息无法发送。经过代码review,也无法找到互相持有资源的情况,并且即便行锁一直没有释放,也不会影响新生成的消息发送。一开始以为是消息渠道的问题,但是在重启数据库后,bug消失了,所以还是认为由死锁引起的问题。
机缘巧合下发现有这样一个说法,Sql Server会自动升级锁,这是一个比较玄学的问题,因为对Sql Server用的不是很多,不甚了解。不过这也好解释,发生死锁的原因:
在sendFeishuMsgJob方法中,对数据表上锁,extracted方法中,需要等待表锁释放才能更新,然而数据表的锁需要extracted执行完才能释放