每次请求都调一次 `context.WithTimeout`,返回的 cancel 函数从头到尾没人调用,go vet 报 `the cancel function is not used on all paths`,goroutine 数还随 QPS 线性上涨,压测十分钟就多出上千个。碰到这种情况,我一般先确认改动有没有真的生效。

原因不复杂,WithTimeout 内部会起一个定时器并挂在父 context 上,不调用 cancel 就不会释放,父 context 一直活着,子节点和定时器全被留住,属于典型的上下文泄漏,请求量一大就显形。

「context 泄漏检测」相关的验证我通常做两遍,改完一次、隔天再看一次,确认不是临时生效。改完顺手把文档补一句,省得下次自己重新推一遍。

go vet ./...
go vet -lostcancel ./...
staticcheck ./...

监控和日志的时间要对上,时区不一致会多绕很大一圈。临时改动用注释写上日期和原因,过两周自己都记不清为什么这么写。

具体做法是,每次创建超时上下文后都要把取消函数延迟调用;提前返回的分支同样要保证能走到取消这一步;把静态检查加进流水线拦住新提交的代码。

最后用这个标准收尾,跑完 go vet 不再有告警,压测十分钟 goroutine 数和内存曲线都能走平,不再上涨。

作者 9823189