Go 负责人 rsc 翻车,决定追加 godebug 行

大家好,我是煎鱼。

上年我给大家分享过《加大力度!Go 将会增强 Go1 向后兼容性》,当时是 Go 核心团队负责人 @Russ Cox(下称:rsc)主导和推进的。

没想到,那么快就发现新的坑。为此 rsc 光速推进了一个新的提案《cmd/go: separate default GODEBUGs from go language version》,现在已经是接受状态了。

今天就由本煎鱼为大家分享和说明具体的内容和缘由。

快速背景

当时提案的核心目的是:为了加强 Go 向后兼容性,具体的行为是:根据 go.mod 中的 Go 版本号来设置对应 GODEBUG,以提供超越当前兼容性准则所保证的兼容性。

使用案例来看,跟现有的 GODEBUG 其实是类似。例如 Go1.20 时引入了一个新的 GODEBUG zipinsecurepath。

该值会遵循一定的流程规范,当时官方给出的例子如下:

  • Go1.20 中默认值为 1,以保留旧的行为并允许不安全的路径。
  • Go1.21 会将默认值更改为 0,以开始拒绝 archive/zip 中的不安全路径。如果是这样,且 Go1.21 也实现了这个 GODEBUG 提案,那么当使用 Go1.21 编译的带有 Go1.20 的模块(go.mod)时,将继续允许不安全的路径。只有当这些模块版本更新到 Go1.21 时,它们才会开始拒绝不安全的路径。

以上是当时定的提案方向和实施的结论。看着很美好,不过咱们程序员写代码,总会有一些意料之外的场景导致新 "坑" 出现。

兼容规则小结

我们对现在 Go 处理兼容性的方式做一下规则总结,如下:

  1. 定义了名为 GODEBUG 设置的配置方式,让用户可以控制这些更改是否或何时在其特定程序中发生。
    1. 默认设置取自主软件包 go.mod 文件中的 "go" 版本行。
    2. 主软件包 Go 源文件可以用 //go:debug key=value 行做覆盖。
  2. 对于 Go Toolchain(工具链),决定当前工具链版本是否足够新以此满足构建模块的要求时,需要检查顶级的 "go" 行,以其为准。
  3. 任何模块中的 "go" 行必须是其依赖关系中所有 "go" 行的最大值。

翻车在哪

仅仅经过了 1.5 个大版本,Go 社区就发现了新的问题。这几个方面的机制产生了冲突,以一种令人遗憾的方式相互影响。

具体场景如下:

1、某个特定程序的维护版本 M 希望锁定从他们开始使用的发布版中的 GODEBUG 语义,他们可以通过设置"go"行来实现,即使在更新到更新的工具链时也可以。

2、但是当他们更新到依赖项的更新版本时,如果这些依赖项已经更新到更新的 "go" 行,那么这将迫使在顶层模块中使用更新的 "go" 行,从而更改了默认的 GODEBUG 设置。

3、如果这种情况发生在一个依赖项上,它可以被 fork+replace。但是对于具有庞大依赖树的模块项目,这可能会发生在许多依赖项上,此时 fork+replace 就不再具备可扩展性。

解决方案

变更方向

Go 官方将计划做两处修改,以便将原先默认的 GODEBUGgo.mod 中的 "go" 行分离,解决上述的兼容规则冲突的问题。

  • 添加一个新的基础设置:default=go1.X,作用和含义是:"按照 Go 1.X 的方式设置一切",与 "go 1.X" 行的意思相同。
    • 对于这个新的基础设置而言,go.mod 文件中的 "go 1.X" 行实质上意味着每个主程序包中的 //go:debug default=go1.X
    • 该设置也可以在 $GODEBUG 中使用。
    • 你可以有一个写着 "go 1.23" 的模块,但主程序却写着 //go:debug default=go1.21
  • go.modgo.work 中添加一行新的 godebug,仅在当前模块中使用(就像 toolchain、replace 和 exclude 仅在当前模块中使用一样)。它需要一个 k=v 参数,与 //go:debug 源代码行相同,但适用于模块中的所有软件包 main。

对我们影响最大的核心点之一是:go.mod 文件中的 go 行不再和 GODEBUG 强绑定,拆了一个新的 godebug 语义和用法出来,两者独立开来,不再在兼容性机制上重叠。

具体例子

基于上述的变更说明,godebug default 的案例,代码如下:

go 复制代码
go 1.23
godebug default=go1.21

也可以设置多种 godebug,go.mod 或 go.work 均是一样的表达方式。

代码如下:

go 复制代码
go 1.23

godebug (
	default=go1.21
	panicnil=0
)

优先顺序如下:运行时的 $GODEBUG > 软件包 main 中的 //go:debug 行 > go.workgo.mod(使用工作区时为 go.work,否则为 go.mod)

一些瓜

现阶段这个提案在 rsc 的高度关注下,快速穿越过各个提案阶段,已经 Accepted。根据我的观察,本次问题,大概率是 Kubernetes 团队在 Google 发现和在内部提出的。

因为 @Jordan Liggitt 光速出现在了评论区,也符合具有庞大依赖树的模块项目的特征,同时也强关注了本提案。

果然,无论是哪里。对于大客户和自家大佬的问题和进度都是特别关注和高资源推进的。(有内部渠道光速响应)

总结

本提案确实解决了前一轮 Go1 兼容性保障增强带来的副作用,共用预定义的泛化内容总是会带来一些奇奇怪怪的问题。本次拆开也便于直观上的理解。

也可以满足使用更新的 Go 版本,且同时 GODEBUG 语义使用更低版本的诉求,这我觉得是不错的。这样未来 Go 新版本好的优化也可以享受到。

根据提案内容和计划,预计本次变更最快可以在 Go1.23 享受到。大家可以期待一下!

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读

相关推荐
用户34232323763172 小时前
开源!Go+Wails+Vue3 手搓一个 PLC 实时监控桌面工具
go
止语Lab3 小时前
为什么你的 Go TCP server P99 延迟这么高
go
Andy Dennis9 小时前
nsq学习记录
消息队列·go·nsq
韦胖漫谈IT11 小时前
选语言不是站队,是选适合问题的工具
java·python·ai·rust·go·技术落地
喵个咪1 天前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
夜悊1 天前
Go网络编程的学习代码示例:客户端/服务端(C/S)模型
go
审判长烧鸡1 天前
【AI问答】GO代码循环返值
go
捧 花1 天前
Eino框架记忆功能实现指南
go·agent·eino
Java陈序员1 天前
主流数据库通吃!一款开源实用的数据库备份管理工具!
react.js·postgresql·go
云浪1 天前
搞懂 Go WaitGroup:一篇文章彻底理解并发等待机制
后端·go