Skip to main content

上下文被取消,谁是罪魁祸首?

· 5 min read
xcentiot
product maintainer @ XcentIoT

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 的原因。

  1. withTimeout 会创建一个新的context,并且设置一个定时任务,会在timeout时间后,发送一个取消context的信号。
  2. context 主动canceled 会触发所有的子context 取消。

但是和我们的问题都不一致,因此我们尝试从问题日志去倒推。

不过我们日志仅仅保存30天,导致得到了30天之前的ok的错误结论。

定位启发

  1. 日志的重要性,日志是我们定位问题的重要依据。
  2. 定位问题先正向分析,如果正向分析没有找到问题的根源,就可以尝试从问题日志去倒推。
  3. 通过对现象的再次分析,发现了一定client2被调用就会有问题,于是把火力集中在client2上。
  4. 在对client2调用的的代码中,发现了sync.RWMutex Lock,认为是lock的原因。
  5. 经过讨论分析,问题的本质是全局context 对象被强制替换了。

追溯

为啥会有强制替换这个问题的原因呢?

因为在开发链路跟踪otelhttp的时候,需要把ctx 传递到下游,不然无法进行链路跟踪。

但是我们这里的ctx又是在init初始的时候创建的并且没有可以初始化的方法。

所有之前就把这个 context 强制替换了。

但是一定其他地方使用到之前的context的话,就会出现这个问题。