上下文被取消,谁是罪魁祸首?
context canceled 谁是罪魁祸首?一次很有意义的问题定位
名词解释
- context canceled:上下文取消
- context:上下文
背景
gitea是一个开源的git服务,我们使用 gitea sdk 的时候,复用 gitea client 时,当服务第一次启动的时候,请求client 1 正常运行, 但是如果请求了client 2 ,就会报 context canceled 错误,由于是第一次遇到这个 context canceled 错误,因此我们开始了调查, 最后发现是因为 client 2 调用了sync.RWMutex Lock,并且强制替换了ctx 最终导致 context canceled 错误。
定位开始
一开始我们怀疑是 gitea 的问题,但是其他相同的接口可以正常调用, 然后我们有怀疑是 gitea sdk 的问题,但是我们通过curl 调用 gitea 接口正常, 通过日志发现这个问题,在2月初都是正常的,但是过年后就出现了,必然是我们提交的代码导致的,因此我们继续向上排查, 看下是不是message-sever调用方式的问题,因为前端访问的接口就可以正常进行,因此我们把内部接口放开,通过api调用,系统重启后, 通过apidoc 调用接口,发现接口可以正常调用,但是一旦前端调用了client 2 ,就会报 context canceled 错误。
定位遭遇战
通过上面的定位,我们初步把问题放到了git-access代码仓,因为git-access里面存在context.withTimeout的代码,因此我们怀疑是不是 context.withTimeout的代码超时导致的。
因此我去查看了网站的代码,并且试图从http源码中正向定位找到问题,但是没有找到问题的根源。
参考 https://learnku.com/articles/63884 得到我们这篇文章,大概了解context canceled 的原因。
- withTimeout 会创建一个新的context,并且设置一个定时任务,会在timeout时间后,发送一个取消context的信号。
- context 主动canceled 会触发所有的子context 取消。
但是和我们的问题都不一致,因此我们尝试从问题日志去倒推。
不过我们日志仅仅保存30天,导致得到了30天之前的ok的错误结论。
定位启发
- 日志的重要性,日志是我们定位问题的重要依据。
- 定位问题先正向分析,如果正向分析没有找到问题的根源,就可以尝试从问题日志去倒推。
- 通过对现象的再次分析,发现了一定client2被调用就会有问题,于是把火力集中在client2上。
- 在对client2调用的的代码中,发现了sync.RWMutex Lock,认为是lock的原因。
- 经过讨论分析,问题的本质是全局context 对象被强制替换了。
追溯
为啥会有强制替换这个问题的原因呢?
因为在开发链路跟踪otelhttp的时候,需要把ctx 传递到下游,不然无法进行链路跟踪。
但是我们这里的ctx又是在init初始的时候创建的并且没有可以初始化的方法。
所有之前就把这个 context 强制替换了。
但是一定其他地方使用到之前的context的话,就会出现这个问题。