TigerBeetle 发布流程全解析:从 Release Manager 算法到二进制制品上线的工程实践
2026/9/13 17:44:25 网站建设 项目流程

TigerBeetle 发布流程全解析:从 Release Manager 算法到二进制制品上线的工程实践

【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle

TigerBeetle 以每周为默认节奏发布二进制版本,其发布过程由一套被刻意"过程化"的 Release Manager 算法驱动:周五准备变更日志并推送release分支,周末由 CFO(故障注入模拟器)持续 fuzz 该分支,周一触发 GitHub Actions 发布工作流并同步到各语言包管理器。本文以 docs/internals/releases.md 为主线,结合 src/scripts/release.zig、src/scripts/changelog.zig 与 CHANGELOG.md 的实际实现,完整还原 TigerBeetle 一次发布的幕后机制——包括版本号如何从 CHANGELOG 中推导、发布为什么幂等可重跑、热修复如何绕过 VSR 升级协议,以及多版本(multiversion)二进制如何支撑无缝升级。读完本文,你将能独立理解并执行 TigerBeetle 的发布全流程,也能将其中的"变更日志驱动版本 + 幂等发布 + 可跳过发布"等工程思路迁移到自己的项目。

注意:原文档作者声明"该流程仍在建立之中(being established),文档可能与现实并不完全一致",且 TigerBeetle 发布流程面向仓库维护者(maintainer),需要 GitHub Actions、各包管理器密钥与仓库权限;普通使用者只需关注如何消费制品,无需执行本文中的操作。

一次发布是什么:TigerBeetle 为什么只发二进制

TigerBeetle 以二进制形式分发,而不是源码包。原文档给出了两个核心原因:

  1. 正确性:真实机器码必须经过实际测试,才能排除大量配置错误类别(configuration errors)——例如某些只在特定优化等级、特定目标平台上才暴露的问题;
  2. 语言稳定性:Zig 语言本身尚未稳定(not stable yet)。以二进制发布可以把"语言尚不稳定"这一实现细节与用户隔离,Zig 只是实现手段(implementation detail),用户无需关心。

同时,TigerBeetle 二进制与各客户端库同版本(lockstep)发布。这是因为当前客户端库的实现与 TigerBeetle 主程序紧密集成、共享代码,要求服务端与客户端版本严格匹配。

这一点在 src/scripts/release.zig 的VersionInfo结构中有直接体现(第 54-66 行):每个版本同时携带:

  • release_triple:VSR 升级协议使用的版本三元组(major.minor.patch),是协议视角的版本;
  • release_triple_client_minrelease_client_min配置项,表示客户端最低兼容版本;
  • tag:git tag 与客户端库版本号,是符号视角的版本。正常情况下tagrelease_triple完全一致,热修复(hot-fix)时两者才可能不同。

版本发布时还会在publish阶段(src/scripts/release.zig)生成 Release Notes,其中明确写出:

  • Oldest supported client version(最老受支持客户端版本,即release_client_min);
  • Oldest upgradable replica version(最老可在线升级的副本版本,从多版本二进制的头部解析而来)。

并附带重要提示:不能用更新的客户端连接更老的集群——客户端只能兼容自己发布版本或更新的副本,且受新版本Oldest supported client version约束。这一提示同时写入各客户端发布说明,用于约束用户混用版本的边界。

Release Manager 算法:周五与周一

原文档为发布经理(release manager)提供了一份"简明算法"(succinct algorithm),完整的动机解释在其后。整个算法按一周为周期运行,默认在周一手动触发发布。

周五:准备发布

  1. 打开 devhub 检查状态:确认自己是本周的 release manager、VOPR(Viewstamped Replication 确定性模拟器)结果正常(近期提交无失败且成功运行充足)、各项图表正常(例如过去一周 RSS、数据文件大小、可执行文件大小没有剧烈变化)。devhub 是发布轮换(rotation)与模拟器状态的集中入口,也托管发布轮换表(见下文"发布物流"一节)。

  2. 生成变更日志脚手架

    $ ./zig/zig build scripts -- changelog

    该命令会:将本地仓库同步到远端(git fetch origin --quiet)、基于origin/main创建用于 changelog PR 的分支、并在 CHANGELOG.md 顶部追加一份新版本的脚手架。脚手架中的版本号会把 patch 版本自动 +1:

    ## TigerBeetle 0.16.3 <- Double check this version. Released 2024-08-29 - [#2256](https://github.com/tigerbeetle/tigerbeetle/pull/2256) Build: Check zig version - [#2248](https://github.com/tigerbeetle/tigerbeetle/pull/2248) vopr: heal *both* wal header sectors before replica startup ### Safety And Performance - ### Features - ### Internals - ### TigerTracks 🎧 - []()

    这一脚手架实际上由 src/scripts/changelog.zig 程序化生成,而不是手写。其核心逻辑(format_changelog,第 50-119 行):

    • 运行git log --merges --first-parent origin/release..origin/main拉取自上次发布以来所有合入 main 的 merge 提交;
    • 解析每个 merge 提交的 PR 编号与标题(Merge pull request #NNNN from ...,第 121-151 行);
    • 若当天日期已出现在现有 changelog 中,直接报错ChangelogAlreadyUpdated,避免重复执行;
    • 解析当前最新条目(通过ChangelogIterator),若其是版本化条目,则生成patch + 1的新版本号并打印## TigerBeetle {next};若当前条目是(unreleased),则沿用## TigerBeetle (unreleased)头(第 71-80 行);
    • 自动列出至多 128 个 PR(超过会@panic("suspiciously many PRs merged"));
    • 追加固定的四个分区骨架:### Safety And Performance### Features### Internals### TigerTracks 🎧

    因此,脚手架生成的## TigerBeetle 0.16.3只是"自动推测"的版本号,发布经理必须人工 double-check。如果本周要跳过发布,则把标题替换为## TigerBeetle (unreleased)

  3. 填写 changelog:将 PR 归类到三个桶(Safety And Performance / Features / Internals),丢弃次要(minor)PR,把相关的 PR 合并为一条要点,再次核对版本号;如果本次发布包含重大功能,要在开头段落(lead paragraph)中说明;对于安全/性能类变更,要从用户视角表述其影响;最后——挑选本周的 TigerTrack 曲目(发布说明的惯例彩蛋)。

  4. 提交 changelog 并提 PR 供评审

  5. PR 合并后推送release分支

    $ git fetch origin && git push origin origin/main:release
  6. 在 Slack 中发布:发布 changelog 的可转推(tweet-able)摘要,以及发布插画(release sketch)的点子。

  7. 此后 CFO 将在周末持续 fuzzrelease分支。CFO(Chief Fuzz Officer,即 VOPR 模拟器)是 TigerBeetle 的确定性故障注入模拟器,周末覆盖测试用于在发布前暴露潜在故障。

  8. 在 Slack 中 @ 下周的 release manager

周一:正式发布

  1. 另一位(不同!)release manager检查release分支上无 VOPR 失败。
  2. triage devhub 上未处理的问题:能立即处理的立即处理;无法行动的(unactionable)关闭并附评论或打上triaged标签;否则转给能处理的人。
  3. 触发发布工作流:通过 GitHub Web 界面触发release.yml工作流,务必从release分支触发,否则会因权限问题失败。
  4. 请另一个人批准该 GitHub 工作流(人为的四人眼评审)。
  5. 将新的发布插画添加到对应的 GitHub Release 页面。

发布物流与轮换

发布是手动触发的,默认在周一。默认的发布轮换表(rotation)托管在 devhub:中间名(middle name)是本周的默认 release manager,应当在周一执行 Release Manager 算法;如果周一本人不在,由志愿者顶替该次发布。

跳过发布:被刻意设计为"便宜"的选项

由于发布频率高,跳过单次发布完全不是问题。事实上,"让发布容易被跳过"正是这套流程的明确目的之一:

  • 如果某个 PR 让人觉得"必须赶进下一个版本",默认做法是让 PR 按自然节奏落地,然后跳过这次发布
  • 如果纠结"该发布还是跳过",默认答案是跳过——跳过是廉价的(Skipping is cheap!)。

跳过发布时,changelog 仍然要在周一写好并合入,但使用## TigerBeetle (unreleased)作为标题。下一个版本发布时需要:

  1. 手动设置下一个有效版本号;
  2. 把所有此前未发布的变更合并成一条带版本号的 changelog 条目,以便升级用户了解增量信息。

从源码看,"unreleased"状态被ChangelogIterator显式建模:src/scripts/changelog.zig 中,当首行等于## TigerBeetle (unreleased)releasenull;src/scripts/release.zig 在解析版本时也会跳过release == null的条目,从而把此前 unreleased 的变更自然并入下一个版本号。

错误处理:幂等、fix-forward 与热修复

原文档把发布异常分为三种场景,分别给出处置策略:

1. 发布失败:流程对客户端包是幂等的

发布流程对客户端包是幂等的(idempotent):发布脚本会先检查每个包版本是否已发布,已发布则跳过(对应 src/scripts/release.zig 的is_already_published,它对每个语言包的release_published_latest返回值与当前 tag 比对,命中则直接return true)。

因此,无论发布是完全失败还是部分失败(例如 Node.js 包已上传但 Java 包失败),都可以安全地重跑发布工作流:修复底层问题 → 删除 draft release → 重新触发工作流。不会烧掉任何版本号(No version number will be burned)。

这一幂等机制在 CHANGELOG.md 的 0.17.9 条目中有记录:PR #3682 "Make publishing TigerBeetle client artifacts idempotent",是近期引入的发布健壮性改进。

2. 发布成功但发现 bug:走正常的 fix-forward

如果发布已经成功,但随后在代码中发现问题需要快速修复,首选方式是做一次正常的 fix-forward 发布(即在下一次正常发布中携带修复)。虽然默认每周发布一次,但一天内做多次发布也是可行的。需要注意:fix-forward 发布会走正常的 VSR 升级协议,即集群通过多版本二进制在线滚动升级。

3. 发布很糟且正常升级协议失效:同三元组热修复

如果发布本身有问题且正常升级协议无法工作(例如副本启动即崩溃),可以制作一个使用相同 VSR release triple的发布,从协议视角它被视为"同一个"版本。这种情况下,git tag 与二进制内的 VSR release 会不一致。要制作这种发布,需要手动调整release.zig中的version_info.release_triple

源码印证:src/scripts/release.zig 中默认assert(std.mem.eql(u8, version_info.release_triple, version_info.tag)),注释明确写道"热修复发布时 tag 可以不同,手动设置 tag 并移除该 assert";若两者不同,会打印log.warn("tag != release, tag={s}")。同时 src/multiversion.zig 的多版本机制允许二进制内含多个旧版本代码(releases_bundled),正是 VSR 升级协议能在运行时切换代码版本的基础。

验证错误处理

如果验证失败的原因是验证代码自身有 bug(例如客户端的validate_release),只需在main分支上修复即可:release_validate.yml工作流会针对最新已发布 tag 的检出,从main运行验证逻辑,因此验证代码的修复会在下次验证运行时自动生效。

版本管理:CHANGELOG 是唯一事实来源

因为发布频繁,TigerBeetle刻意不在源码中硬编码版本号。版本号的唯一事实来源(source of truth)是 CHANGELOG.md:顶部条目的版本号就是下一个新发布的版本号

版本号是单调递增的,但允许存在缺口(gaps)——这正是"跳过发布"机制的直接后果:跳过的版本不会出现在 CHANGELOG 中,因此版本序列会出现 0.17.9 → 0.17.11 这类跳号。

从 src/scripts/changelog.zig 与 src/scripts/release.zig 的交互可以看到版本推导的完整链路:

  • changelog 脚手架从最新条目推导patch + 1
  • src/scripts/release.zig 解析 CHANGELOG 顶部条目得到release(当前版本)与release_multiversion(上一个已发布版本,用于告诉多版本二进制要内嵌哪些旧代码),并断言当前版本一定大于上一个多版本发布版本(assert(release.value > ...),且必须大于引导版本0.15.4,见第 122-127 行);
  • src/config.zig 在编译期通过 build options 接收-Dconfig-release-Dconfig-release-client-min,解析为vsr.Release并断言release >= release_client_min

这也解释了为什么./zig/zig build scripts -- changelog生成的脚手架头部要特别标注"Double check this version"——版本号来自对 CHANGELOG 的机械推导,人工必须复核。

Changelog 的编写原则

原文档明确了 changelog 的三个用途:

  • 对所有人:给项目一个可见的"脉搏"(pulse);
  • 对 TigerBeetle 开发者:讲述细粒度的项目演进故事,形成共享上下文,为月度 newsletter 提供素材;
  • 对 TigerBeetle 用户:告知所有可见的、可能相关的变化。

据此,编写时有四条指导原则:

  • 平凡的变更(trivial changes)考虑跳过
  • 有意义的内部变更(internals)即使外部不可见也不能跳过
  • 如果一系列 PR 背后有故事,把它讲出来(tell the story);
  • 别忘了本周的TigerTrack

实际条目格式可参考仓库根目录 CHANGELOG.md 的 0.17.9 条目:每个分区下列出 PR 编号链接与一至两句描述,多个相关 PR 合并为一个要点(如 "Add a u128 bounds check in the Ruby client and make its status return type more idiomatic" 合并了 #3830 与 #3811)。

发布制品与发布(Publishing)

一次发布的规范形态:dist/目录

一次发布的规范形态(canonical form)是一个dist/文件夹,包含以下制品:

子目录内容目标注册中心
tigerbeetle/所有受支持架构的tigerbeetle二进制(.zipGitHub Release + Docker(ghcr.io)
dotnet/NuGet 包NuGet
go/go 客户端源码 + 各平台预编译原生库独立的 tigerbeetle-go 仓库(通过提交)
java/.jar文件Maven Central
node/npm 用的.tgznpm

从 src/scripts/release.zig 可以看到 TigerBeetle 二进制实际构建的目标矩阵:

  • x86_64-linux
  • x86_64-windows
  • aarch64-linux
  • aarch64-macos(会构建成 universal binary)

每个目标还会额外构建debug 变体-debug,对应build.mode=Debug,供排查问题使用),并打 zip 包:tigerbeetle-{target}.ziptigerbeetle-{target}-debug.zip。构建完成后会在本机目标上运行./tigerbeetle version --verbose断言process.verify=true(debug)与正确的构建模式(第 304-318 行),确保制品自检通过。

发布脚本同时会为每个客户端语言执行各自的构建(.dotnet.go.java.node.python.ruby.rust——rust 当前处于禁用状态,见第 241-244 行注释 "Currently disabled"),并把产物写入zig-out/dist下对应的子目录。此外还会额外构建 Vortex 驱动器(vortex-driver-zig-{target}.zip,第 333-368 行),供故障注入测试使用。

发布(Publishing)流程

发布的同步机制如下:

  1. 发布开始时先创建一个draft releasegh release create --draft,src/scripts/release.zig),且发布前会做 sanity check:新 tag 必须尚不存在,而 multiversion 参考 tag 必须已存在(第 692-711 行);
  2. 制品依次上传到:GitHub Release(二进制 zip 与 vortex 包,第 808-823 行)、npm、Maven Central、NuGet;Go 客户端以新 commit 推到独立的 tigerbeetle-go 仓库(并打上v{tag}tigerbeetle-{sha}两个 tag,第 870-926 行);文档则上传到独立的 docs 仓库(publish_docs,第 1223-1279 行,即使文档无变化也会提交一个--allow-emptycommit,确保 docs 仓库的最新 commit 总是指向最新发布);
  3. 只有所有注册中心都发布成功,发布才会从 draft 转为正式(gh release edit --draft=false --latest=true,第 836-840 行);文档发布放在最后,即使失败也不影响其余制品。

发布还包含 Docker 镜像(publish_docker,第 1126-1208 行):用docker buildx build构建linux/amd64,linux/arm64多架构镜像并 push 到 ghcr.io,latest{tag}{tag}-debug共三个 tag;发布后还会docker run ... version --verbose做事后验证。注意源码注释特别说明:Docker 不是运行 TigerBeetle 的推荐方式,容器镜像只是为期望它的用户提供的便利(第 1124-1125 行)。

密钥管理

所有发布密钥都以GitHub Actions secrets的形式存储在release环境中。其中 Go 与 docs 使用个人访问令牌(PAT),这类令牌一年后过期。刷新步骤:

  1. 用个人 GitHub 账号创建 fine-grained PAT;
  2. 将令牌作用域限定到 tigerbeetle GitHub 组织;
  3. 授予相关仓库的写权限(不同仓库使用不同令牌);
  4. 在 tigerbeetle 仓库的release环境中更新令牌。

Ruby 客户端则使用OIDC trusted publishing(src/scripts/release.zig):从 GitHub Actions 获取 OIDC token,与 rubygems.org 交换 API key,免去长期密钥管理。

深入源码:release.zig 如何编排这场"元构建"

src/scripts/release.zig 被刻意实现为独立的 Zig 脚本,而不是build.zig中的一个步骤。文件头注释(第 1-16 行)说明了原因:这是一个"元"构建系统(meta build system),需要把zig buildgo buildnpm publish对等的工具编排在一起。它支持通过 CLI 参数精细控制:

  • --sha:发布所基于的 commit SHA;
  • --languages:只构建/发布特定语言(Language枚举:dotnetgojavanodepythonrubyrustzigdocker,第 38 行);
  • --build/--publish:区分构建与发布两个阶段;
  • --no-changelog:当当前代码没有 changelog 条目时使用(即顶部条目描述的是历史发布),用于在 main 分支上测试发布流程(第 45-49 行),此时会以65535.0.0作为假版本,且禁止 publish;
  • --devhub:只构建生产用 x86_64 Linux 目标,加快 devhub 触发的构建速度(仅允许与--languages=zig组合,第 76-80 行)。

发布过程中的出错体验也被刻意优化:每个命令的输出保持 O(1) 行,这样一旦出错,能立刻看到是哪条命令出了问题,并可直接复制粘贴到本地终端复现(第 13-16 行)。

另外值得注意的是,构建过程会对每个语言修改其版本文件(如 Java 的pom.xml、Node 的package.json/package-lock.json、Ruby 的version.rb、Rust 的Cargo.toml),但都通过backup_create/backup_restore(第 1293-1301 行)在构建前后备份恢复,保证主仓库不被污染。

相关文档

  • docs/internals/README.md:TigerBeetle 内部文档索引,其中将本文件描述为"我们的发布流程";
  • docs/internals/upgrades.md:VSR 升级协议(fix-forward 发布与热修复背后的协议基础);
  • docs/internals/vopr.md 与 docs/internals/testing.md:周末 fuzz 的 CFO/VOPR 模拟器说明;
  • CHANGELOG.md:版本号的唯一事实来源,也是发布脚手架的输入与输出;
  • src/scripts/release.zig:发布编排脚本(构建 + 发布);
  • src/scripts/changelog.zig:changelog 脚手架生成器;
  • src/multiversion.zig:多版本二进制实现,支撑运行时无缝升级。

【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询