测试超时卡死,却不知道是哪个 goroutine 在作妖?Go 官方提案:给每个测试自动打标签

测试超时卡死,却不知道是哪个 goroutine 在作妖?Go 官方提案:给每个测试自动打标签

💡 原文中文,约5700字,阅读约需14分钟。
📝

内容提要

Go提案#75047拟为每个测试的goroutine自动添加test和test_iter标签,便于CI超时排查时定位卡死goroutine所属测试。配套CL已实现标签注入与崩溃回溯显示。社区正讨论标签命名、分隔符及迭代号显示方式,评审委员会认可方向,细节仍在打磨。

🔎

延伸解读

从“知道谁没跑完”到“知道谁卡在哪”

Go 1.21 后测试框架能列出未结束的测试函数,但无法将 goroutine 堆栈与具体测试关联。提案 #75047 通过为每个测试的 goroutine 自动添加 test 和 test_iter 标签,让 CI 超时排查时能直接定位卡死 goroutine 所属的测试及迭代次数,减少人工猜测调用关系的时间。

命名与分隔符:小细节背后的大影响

社区争论焦点包括使用一个还是两个标签、key 采用下划线还是点分命名、迭代号分隔符如何选择。若将迭代号拼入测试名,profile 聚合时同一测试的样本会被拆散;而 t.Run 子测试名可能已含 #,再叠加迭代号易造成混淆。这些细节一旦确定将长期向后兼容,因此评审委员会虽认可方向,仍谨慎打磨。

性能取舍:外层打标,不碰内层循环

提案明确不在 benchmark 高频路径上实时打标签,因为 context.Context 每次设置标签都会分配新对象,内层循环开销不可忽视。标签仅在测试或基准测试的外层粒度打一次,兼顾排查需求与性能。配套 CL 已实现标签注入及崩溃回溯显示,但作者坦言仍需打磨,最终细节可能随命名方案调整。

Q&A

Go 提案 #75047 是什么?它想解决什么问题?

Go 提案 #75047 名为“testing: add goroutine labels to tests”,旨在为每个测试、基准测试和模糊测试自动添加 goroutine 标签,以便在 CI 超时排查时快速定位卡死的 goroutine 属于哪个测试。

这个提案具体会给 goroutine 打上哪些标签?

提案建议为每次测试调用打上两个标签:一个 key 为 test(或 test.name),value 为完整测试名;另一个 key 为 test_iter(或 test.iter),记录该测试在 -count 参数下的运行次数。

为什么需要给测试的 goroutine 打标签?

因为当 CI 超时导致大量 goroutine 堆栈输出时,仅知道哪些测试未完成还不够,还需要知道每个卡住的 goroutine 属于哪个测试,以便快速定位问题。标签能直接显示归属,提升排查效率。

社区对标签方案有哪些主要争论点?

争论焦点包括:使用一个标签还是两个标签、标签 key 的命名风格(下划线还是点分)、拼接时的分隔符选择(如 # 可能引起混淆),以及迭代号是否从第一次就显示。

评审委员会对这个提案的态度是什么?

评审委员会(如 aclements)完全认可提案方向,认为唯一悬而未决的是标签的具体形式,并倾向于迭代号从第一次就打印,命名上可能更偏好 test.name / test.iter 的点分风格。

这个提案有哪些配套的代码变更(CL)?

配套 CL 包括:CL 694119 让 runtime 在崩溃回溯中显示标签;CL 696117 和 CL 696595 在 testing 包内实现标签注入。这些 CL 已基本完成,但还需打磨。

如果提案落地,对日常开发有什么影响?

影响包括:CI 超时排查时可直接搜索 test: TestXxx 定位堆栈;压测和混沌测试中可按 test 标签聚合分析 CPU 或 goroutine 数量;生态项目(如 grpc-go)已参考类似命名思路,推动 goroutine label 规范化。

🏷️

标签

➡️

继续阅读