news 2026/9/25 4:03:24

RisingWave 的 workspace-hack 构建机制:用 cargo-hakari 统一 Cargo 特性解析加速大型工作区编译

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RisingWave 的 workspace-hack 构建机制:用 cargo-hakari 统一 Cargo 特性解析加速大型工作区编译
  • 数据库
  • 流处理
  • 后端
  • 数据工程

【免费下载链接】risingwave

Event streaming platform for agentic AI. Continuously ingest, transform, and serve event streams in real time, at scale.

项目地址:https://gitcode.com/gh_mirrors/ri/risingwave
点击查看免费下载

本文以 RisingWave 仓库中的 src/workspace-hack/README.md 为核心线索,结合仓库内 40 余个 crate 的Cargo.toml、根工作区清单以及Makefile.toml/ CI 脚本中的真实用法,系统讲解 workspace-hack 这个"由 cargo-hakari 管理的魔法 crate"在 Cargo 大型工作区中解决什么问题、如何工作、如何重新生成与校验,以及仓库里围绕它留下的边界约束。读完本文,你将理解 Cargo 特性统一(feature unification)的代价与 workspace-hack 的化解思路,并能在自己的多 crate 工程里复现同样的构建优化实践。

一、背景:Cargo 特性统一为什么会成为大型工作区的痛点

Cargo 的 feature 解析遵循"特性统一"(feature unification)规则:当同一个依赖被多个包以不同的 feature 集合引用时,Cargo 会在解析阶段将各方声明的 feature 取并集,最终这个依赖只以一份"开了所有被请求 feature"的形式参与构建。

这套规则保证了最终二进制中依赖版本唯一、行为一致,但也带来了两个大型工作区特有的问题:

  • 特性解析开销大:工作区越大、依赖越多,Cargo 需要反复计算"每个依赖最终应启用哪些 feature",解析时间随 crate 数量与 feature 组合显著增长;
  • 编译产物碎片化:feature 集合参与了 crate 的构建指纹。一旦不同包对同一依赖请求了不同 feature 组合,即使最终并集相同,Cargo 也可能在多处重复编译同一依赖的不同 feature 变体,拉长全量构建与 CI 时间。

RisingWave 是一个拥有上百个成员 crate 的巨型 Rust 工作区(见根 Cargo.toml 的[workspace] members列表,涵盖src/batch、src/common、src/meta、src/storage、src/stream、src/frontend等全部核心模块),这类问题会被放大得十分明显。workspace-hack 正是为化解这一问题而生的仓库内建方案。

二、workspace-hack 是什么:仓库内那个"魔法 crate"

src/workspace-hack/README.md的开篇标题就是"How this magic works"(这个魔法是如何运作的),其核心内容可以浓缩为两点:

  1. 这个 crate 由cargo-hakari工具管理(README 原文:"This crate is managed by cargo-hakari");
  2. 它的作用一句话概括:无论工作区中哪个包正在被构建,都强制整个工作区使用统一后的 feature 集合(README 原文:"it forces the workspace to use the unified features, regardless of which package is being built")。

展开来说,workspace-hack 的运作思路是:由cargo-hakari自动分析整个工作区的依赖图,把"所有 crate 依赖过的第三方库及其全部 feature 并集"汇总成一份清单,写进 workspace-hack 自己的Cargo.toml;然后让工作区里每一个 crate 都把 workspace-hack 声明为依赖。这样,任何一次构建都必然包含这份"全量 feature"依赖,Cargo 解析器从任意入口出发得到的特征解析结果都一致,从而:

  • 消除"按包构建时 feature 组合漂移"导致的重复编译;
  • 让特性解析结果全局唯一,加快解析与增量编译命中率;
  • 保证测试、benchmark、各节点二进制(compute / meta / frontend / compactor)构建出行为一致的依赖变体。

需要说明的是,cargo-hakari把这一机制称为 workspace-hack package,其详细原理解释(包括"为什么能加快构建")在其官方文档中有专门章节,上文是基于仓库 README 表述与 Cargo 特性统一语义的展开。

三、仓库中的落地形态:从文件结构看这个 crate

3.1 crate 本体:生成物 + 空壳实现

src/workspace-hack/目录下只有三个文件,职责分工非常清晰:

  • Cargo.toml:由cargo hakari生成的清单文件,文件头部注释写明 "This file is generated bycargo hakari" 与再生成命令cargo hakari generate;
  • src/lib.rs:一个没有任何逻辑的 stub("This is a stub lib.rs."),workspace-hack 只是承载依赖声明的"汇聚点",本身不需要任何代码;
  • README.md:即本文所依据的核心文档,说明该 crate 的定位与维护方式。

在 Cargo.toml 中可以看到它的包属性:

[package] name = "workspace-hack" version = { workspace = true } edition = { workspace = true } homepage = { workspace = true } keywords = { workspace = true } license = { workspace = true } repository = { workspace = true } description = "workspace-hack package, managed by hakari" publish = false

其中两个细节值得注意:

  • version、edition、license等全部继承自[workspace.package](见根 Cargo.toml),保证它始终与工作区主版本保持一致;
  • publish = false表明这是一个纯内部构建辅助 crate,不会发布到 crates.io;文件头部的注释也提示了"可以选择发布该 crate"这一选项,但 RisingWave 选择了不发布。

3.2 被 37 个以上 crate 依赖:全员接入

workspace-hack 的价值取决于"被整个工作区共同依赖"。在 RisingWave 中,从核心模块到测试套件几乎全员声明了对它的依赖,统一使用相对路径形式:

workspace-hack = { path = "../workspace-hack" }

例如:

  • src/batch/Cargo.toml(批处理执行引擎)
  • src/cmd_all/Cargo.toml(all-in-onerisingwave二进制入口)
  • src/common/Cargo.toml、src/stream/Cargo.toml、src/storage/Cargo.toml、src/frontend/Cargo.toml、src/meta/Cargo.toml
  • 二层嵌套的 crate 则使用../../workspace-hack,如 src/batch/executors/Cargo.toml、src/meta/service/Cargo.toml、src/storage/compactor/Cargo.toml、src/utils/pgwire/Cargo.toml
  • 各类测试 crate 同样接入,如 src/tests/sqlsmith/Cargo.toml、src/tests/regress/Cargo.toml、src/tests/state_cleaning_test/Cargo.toml

以 src/cmd_all/Cargo.toml 为例,它构建的risingwave单二进制是 meta-node、compute-node、frontend-node、compactor 等全部组件的合集,因此它必须与所有模块共享同一套特性解析结果——把 workspace-hack 挂在这里,等于给整棵依赖树"钉死"了统一坐标。

四、从 Cargo.toml 的 HAKARI SECTION 看生成物的自描述结构

workspace-hack 的 Cargo.toml 中最核心的结构是文件中部由 hakari 划定的保护区:

### BEGIN HAKARI SECTION # Disabled by running `cargo hakari disable`. # To re-enable, run: # cargo hakari generate ### END HAKARI SECTION

cargo-hakari用BEGIN/END HAKARI SECTION注释把"由工具管理的部分"与其他手写内容隔开。工具每次重新生成时只重写这段区域,其余手写字段(包名、描述、publish 等)保持不变,避免工具与人工编辑互相覆盖。

值得如实指出当前仓库的状态:这一 HAKARI SECTION 内部目前只有说明注释、没有任何实际依赖条目,说明该仓库在某个时点执行过cargo hakari disable,当前 workspace-hack 处于"禁用状态"——即 crate 骨架与全员依赖声明仍在,但特征并集清单暂未生成。这与仓库中两处证据吻合:

  1. CI 脚本 ci/scripts/check.sh 中 hakari 检查被注释掉,并写明"Disable hakari until we make sure it's useful"(在确认它确实有用之前先停用):
    # Disable hakari until we make sure it's useful # echo "--- Rust cargo-hakari check" # cargo hakari generate --diff # cargo hakari verify
  2. src/object_store/Cargo.toml 中的注释说明该 crate 在引入 hdfs 后从 hakari 管理中排除,其workspace-hack依赖被注释掉。

这也解释了为何src/lib.rs只是一个空壳:当清单区为空时,workspace-hack 退化为一个无依赖、无逻辑的占位 crate,依赖它的各模块不受影响,而一旦需要重新启用,只需跑一次cargo hakari generate即可。

五、日常维护:重新生成与校验的命令流程

README 指出该 crate 完全由 cargo-hakari 管理,因此日常维护不涉及手写依赖,而是围绕两条命令展开:

  • cargo hakari generate:扫描整个工作区,重新计算所有依赖的 feature 并集,并将结果写回 workspace-hack 的 HAKARI SECTION。这也是 Cargo.toml 头部注释标注的再生成命令;
  • cargo hakari verify:校验当前生成的清单是否与工作区实际状态一致,若依赖或 feature 发生变化而未重新生成,会报错退出,起到 CI 门禁作用。

RisingWave 通过 cargo-make 把这条流程封装成了标准化任务。在根 Makefile.toml 的check-hakari任务中可以看到完整闭环:

[tasks.check-hakari] private = true category = "RiseDev - Check" description = "Run cargo hakari check and attempt to fix" install_crate = { min_version = "0.9.24", crate_name = "cargo-hakari", binary = "cargo", test_arg = ["hakari", "--help"], install_command = "binstall" } script = """ echo "Running $(tput setaf 4)cargo hakari$(tput sgr0) checks and attempting to fix" cargo hakari generate --diff --quiet || cargo hakari generate cargo hakari verify > /dev/null test $? -eq 0 || exit 1 """

这里体现了工程化的容错设计:先用cargo hakari generate --diff --quiet做"只对比、不落盘"的检查,如果 diff 非空(说明清单已过期)则自动执行完整cargo hakari generate修复,最后用cargo hakari verify严格校验并让 CI 失败。另外,install_crate声明了 cargo-hakari 的最小版本0.9.24,并用cargo binstall安装,与 Makefile.toml 的install-tools任务中批量安装 cargo-hakari 的做法一致,保证开发者与 CI 环境工具版本一致。

六、边界约束:与 workspace-config 的互斥、与依赖检查工具的协作

6.1 workspace-hack 不能依赖 workspace-config

仓库里还有一个与 workspace-hack 定位类似但用途相反的 crate:src/utils/workspace-config/README.md。它同样是利用 Cargo 特性统一机制来"强制"某些配置,但方向恰好互补:

  • workspace-config负责把部分依赖的 feature 固定下来(例如静态编译期的日志级别、通过rw-static-link特性静态链接 OpenSSL),并且只能被最终二进制入口risingwave_cmd与risingwave_cmd_all依赖;
  • 它的 README 中明确写了一条硬性约束:"It should not be depended by workspace-hack, otherwise the features will be always enabled."——如果 workspace-hack 依赖了 workspace-config,那么 workspace-config 所固定的 feature 会被工作区中所有 crate 无条件继承,"只为最终二进制生效"的设计意图就被破坏了。

这两条约束合在一起,勾勒出了仓库对特性统一工具的精细分工:workspace-hack 解决"全局解析一致",workspace-config 解决"二进制专属配置",二者互不越界。

6.2 依赖检查工具对 workspace-hack 的豁免

workspace-hack 这类"只声明依赖、从不被使用"的 crate 会触发cargo-machete、cargo-udeps这类未使用依赖检查工具的误报。RisingWave 在根 Cargo.toml 中显式做了豁免配置:

[workspace.metadata.cargo-machete] ignored = [ "workspace-hack", "expect-test", "pretty_assertions", "serde", ... ] [workspace.metadata.cargo-udeps.ignore] normal = ["workspace-hack"] development = ["expect-test", "pretty_assertions"]

此外,最依赖 workspace-hack 的 src/cmd_all/Cargo.toml 也在cargo-machete与cargo-udeps两处ignored列表中单独列出了workspace-hack。这一方面说明工具链已经理解"这类 crate 的依赖是构建期全局性的,不能按普通未使用依赖处理",另一方面也说明:任何新增的依赖检查/清理工具接入时,都需要把 workspace-hack(以及 workspace-config)纳入豁免名单,否则会误报。

七、总结:在大型工作区复现这套实践

回到 src/workspace-hack/README.md 那句"无论构建哪个包,都强制使用统一 feature"的核心表述,RisingWave 的这套实践可以提炼为可在其他工程复用的四步方案:

  1. 引入 cargo-hakari:安装工具(仓库要求最小版本 0.9.24),让src/workspace-hack成为工作区成员(对应根 Cargo.toml 中的"src/workspace-hack");
  2. 全员依赖:工作区所有需要统一解析的 crate 通过workspace-hack = { path = "../workspace-hack" }声明依赖,让任意构建入口都触达统一 feature 清单;
  3. 生成与门禁:用cargo hakari generate维护清单、cargo hakari verify做一致性校验,并把二者封装进Makefile.toml的check-hakari任务或 CI 脚本;
  4. 处理边界:为依赖检查工具配置豁免;明确哪些 crate(如 object_store 这类带特殊条件依赖的)需要排除在 hakari 管理之外;同时注意与 workspace-config 这类"二进制专属特性"工具保持互斥,避免特性被意外全局放大。

最后需要再次强调当前仓库的客观状态:workspace-hack 的清单区处于cargo hakari disable之后的空状态,CI 中的 hakari 检查被注释,src/object_store/Cargo.toml 也标注了排除说明。这意味着读者若在本仓库执行cargo hakari generate,会重新生成并激活这套统一机制——这正是 README 所描述的"魔法"从概念走向可运行的完整路径。

  • 数据库
  • 流处理
  • 后端
  • 数据工程

【免费下载链接】risingwave

Event streaming platform for agentic AI. Continuously ingest, transform, and serve event streams in real time, at scale.

项目地址:https://gitcode.com/gh_mirrors/ri/risingwave
点击查看免费下载

相关推荐

上一篇:Qwen3-Next重磅发布:80B参数如何实现10倍推理提速?
下一篇:5步精准定位:彻底解决PyTorch动态链接库加载失败难题

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 4:01:38

免费AI知识管理:从三千条收藏到个人知识库的搭建方法论

1. 为什么我花了一个月做这件事——从三千条收藏到一张知识地图1.1 一个普通人的信息困局先把话说在前面:这不是一篇凡尔赛式的"我教你知识管理"教程,而是一个被信息洪流冲昏头脑的普通人,花了一个月时间自救的真实记录。我的困境可…

作者头像 李华
网站建设 2026/9/25 4:01:31

拒绝盗版影视源码,合规搭建私人视频点播站的正确姿势

抱歉,这个标题我不能写。“神马影视8.8 2026版”这类影视源码系统,在公开语境里基本都指向同一类东西:聚合盗版片源、绕开版权方授权、靠广告和会员充值变现的盗版影视CMS源码。这类系统本身就涉及版权侵权,部署出来大概率也是用于…

作者头像 李华
网站建设 2026/9/25 4:01:17

CANoe中DBC/CDD导入报错全解析:从文件原理到实战排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:01:05

BQ27441电量计初始化实战:从SEALED解锁到SOC准确读取

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 3:59:40

双一流新周期:学科评估与动态调整下的择校与学科建设策略

大家这几天应该都刷到这条消息了:新一轮“双一流”建设启动,高校圈、考研圈、家长群一下子就热闹起来。很多人看到“双一流”三个字,第一反应是又出一份“大学排名”,跟自己没啥关系。其实不是。家里有孩子要高考的,学…

作者头像 李华
网站建设 2026/9/25 3:58:44

Python装饰器完全指南:从闭包原理到工程实践

1. 装饰器到底在解决什么问题先讲个真实的场景。前几年我维护过一整套内部运营后台,光类似的接口就有三四十个,早期代码写得比较随意,登录校验是这么干的:def get_user_info(user_id):# 假设这里有权限判断,每次都要复…

作者头像 李华