阅读视图

发现新文章,点击刷新页面。

开源软件有断供的风险吗?

近期,Linux 上游因为受美国出口管制条例的影响,将移除部分开发者的 MAINTAINER 权限,引起了新一轮对开源依赖的重新评估。

关于其中开源精神和社群治理的讨论,卫 Sir 的两篇文章已经讨论得比较清楚(见尾注)。本文主要从软件供应链的角度出发,回应对“开源软件的断供风险”的担忧。

简言之,开源协议只是向用户授权自由使用特定版本源代码。除此以外,大部分开源协议都明确声明不提供维保,更不承诺有一个长期迭代的上游分支

开源协议提供了什么样的开源软件?

大部分人对开源协议的了解是不准确的。人们往往望文生义的认为开源软件只意味着源代码公开,至于软件的用途和使用限制,都是可以商议的。

严格意义上的开源定义由开源促进会(OSI)提供,它定义了开源软件的分发条款必须符合的若干个标准,此外才是可以商议的部分。我们常见的 MIT License 为例,先读一遍协议原文,看看 MIT License 到底以什么形式分发软件。

我们采用开放原子开源基金会“源译识”项目的中英对照翻译成果:

Copyright [YEAR] [COPYRIGHT HOLDER]

版权所有 [年份] [版权持有人]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

特此向获得本软件及相关文档(合称“本软件”)副本的任何人免费授予不受限制地利用本软件的许可,包括而不限于:使用、复制、修改、合并、发布、分发、分许可和/或销售本软件副本,并允许本软件的接收者也获得前述许可,但须遵守以下条件:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

以上版权声明及本许可声明应包含在本软件的所有副本或主要部分中。

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

本软件系“按原样”提供,不包含任何形式的明示或默示保证,包括但不限于适销性、特定目的适用性及不侵权的保证。在任何情况下,无论是在合同、侵权或其他案件中,作者或版权持有人均不对因本软件、或因本软件的使用或其他利用而引起的、引发的或与之相关的任何权利主张、损害赔偿或其他责任承担责任。

MIT License 的条款包含了所有常见开源协议都具备的两大特征。第一个是授予获得以本协议许可的软件的任何人几项基本权利,主要是任意使用、任意修改和任意分发。第二个是强调该软件“按原样”(AS IS)提供,没有任何维护保障服务,也不承担任何用户使用软件带来的责任。

这两个条款所基于的开源精神,是软件作者已经无偿将代码公开发布并允许你自由使用、修改和重新分发了,那么用户也不应该再强求软件作者承担额外的责任,包括持续维护软件,或必须处理用户使用软件产生的问题。

此外,不同于朴素理解下,“一个软件是 MIT License 许可的开源软件”,意味着它将会以 MIT License 持续发布新版本,实际上,软件作者有权以任何协议发布其代码,而开源软件协议也只绑定在软件的特定发布版本上。

因此,我们可以看到,开源协议本质上只是绑定在特定版本软件上的一系列分发条款,或者说,开源协议本身就是对一个代码快照进行许可。在这种情况下,被“提供”的软件就是这个快照本身,而开源协议授予的一系列基本权利是不可撤销的。在这个维度下,“开源断供”从未发生过。以多次变更协议的 Redis 为例,今天全球用户仍然能自由使用其最后一个以 3-Clause BSD License 许可的版本(7.2.5)。

如何自己维护开源依赖?

既然从开源协议授予用户的权利,以及开源协议中大多包含的免责协议角度看,“开源断供”从未发生,那么甚嚣尘上的对于“开源软件的断供风险”的担忧到底指的是什么呢?应该说,这种担忧主要是对开源软件过度期待带来的误解。

今天,任何现实世界的应用软件都不可能脱离开源软件实现。换句话说,任何你实际使用的软件,都或多或少的依赖了开源软件。例如,只要你的应用涉及 TLS 网络通信,最常用的 OpenSSL 软件库就是一个开源软件;任何 Java 应用只要有输出日志的需求,很大概率就会依赖 Apache 软件基金会的开源软件 Log4j 等。

从软件供应链的角度来看,开源软件充满了终端应用的整个供应网络。上面提到的两个开源软件,分别出过 Heartbleed 安全漏洞Log4Shell 安全漏洞,影响了数以千万计的在线应用。

好在开发这两个软件的开源社群对安全漏洞都能做到快速响应,第一时间就提供了修复补丁。所有用户企业得以在第一时间自检是否收到漏洞影响并打上补丁修复。

现在,我们考虑到这些修复补丁是以新版本发布来提供的,按照上面的讨论,这一新版本其实有可能并不以开源协议来提供。举一个实际的例子,如果一个企业用 Akka 开发了核心业务系统,今年 Akka 归属的商业公司 Lightbend 变更了 Akka 的协议,后续 Akka 的安全漏洞补丁许多就只以新的专有协议发布。

这里就出现了“开源断供”的风险,即用户使用了开源软件,一旦该软件出现问题,上游有能力修复的情况下,出于商业考量或法律约束,不向特定用户提供修复版本。换句话说,开源软件的用户不能想当然地认为开源软件必定有人维护,且该软件的后续版本一定会以开源协议提供

因此,企业开发和使用软件时,必须厘清软件的开源依赖,判断哪些核心依赖需要维护保障,进而采取相应措施。

鉴于开源软件供应链无处不在的影响,市场上出现了一系列分析软件依赖的工具和标准:

这些工具大多提供为应用软件生成软件物料清单(Software Bill of Materials, SBOM),即罗列出软件所有依赖项的名字、协议属性和代码数字签名等等。这应当会成为未来软件供应链一个必然的趋势,欧盟今年出台的《网络弹性法案》就有此要求,从制造业的发展来看,物料清单也是产业发展成熟的一个最佳实践。

SBOM 示例片段

如何获得有维保的开源软件?

能够分析清楚自己开发和使用应用软件所依赖的开源软件之后,下一步就是如何获得关键依赖的维护保障。

前文已经说明,常见的开源协议都是不提供维护保障的,甚至这些协议都会有专门的免责声明和责任限制条款。因此,单纯祈祷开源软件的开发者持续保持热情,响应问题发布版本,是不可靠的。

另一方面,今天任何人都不可能完全不依赖开源软件开发一个新应用,甚至任何新应用当中绝大部分依赖都是开源依赖。因此,为了避免所谓“开源断供”的风险而选择由公司承担所有依赖代码的编写工作,是绝无可能的。没有任何一家公司能够负担得起这样的研发成本。

因此,使用开源软件,并确保核心依赖得到某种形式的维护保障,就是企业用户必须考虑的问题。我们可以把开源依赖分成四类:

  1. 稳定的依赖。依赖库本身并不复杂或完成度极高,在可预见的未来没有任何迭代需求。例如 Hash 算法的实现或特定数据结构的软件库等。这类依赖只需下游用户固定住一个版本即可高枕无忧。甚至可以说最担心的就是上游没事找事胡乱迭代,下游激进跟进版本以后出现故障。例如各种 npm 生态种的迷你库曾经引发的互联网风暴。
  2. 可靠的依赖。例如前文提到的 OpenSSL 和 Log4Shell 等,虽然它们都出现过严重的安全漏洞,但是软件开发总是要有漏洞的,这两个社群能够即时发布开源的补丁以供下游使用,这样的依赖就是可靠的。基石性的开源软件往往需要十分可靠才能得到大范围应用,例如 Linux 和 Kubernetes 等等。当然,依赖是否可靠也是动态变化的,例如维护人员的变动或者去世,还有维护组织经营状况和所属环境的改变等等。
  3. 可替换的依赖。如果一个开源依赖既不稳定,也就是需要不断迭代以适应需求或使漏洞尽量收敛,又不可靠,也就是不存在一个可持续的上游社群维护,那么企业可以放心使用这一依赖的唯一出路,就是确保该依赖是可以替换的。换句话说,一旦这个开源依赖出现问题,可以替换成另一个没有问题的开源软件,或者由公司员工制作一个替代软件,或者向供应商采购替代软件。
  4. 风险。除了以上三类依赖,其余的软件都是有风险的。它们既不稳定,也不可靠,一旦出现问题,公司也没有任何替换的预案。

这样分类过后,我们可以清楚的看到,公司要想获得有维保的开源软件,主观能动的解决方案,就是确保雇佣的员工能够兜底核心依赖,或者从供应商采购维保服务或替代软件。无论哪种方式,都需要企业支付对应的成本。

因此,企业要想安心使用开源软件,首先需要建立起来的认识就是,开源依赖总是存在于软件供应链上,保障开源依赖的供应链安全是有成本的

在此基础上,根据自身应用的实际形态和复杂度,衡量成本,相应地选择(1)雇佣软件工程师维护(2)采购供应商的服务或软件;或者,也可以(3)直接跟开源软件的作者签订协议或提供赞助,确保或促进上游的可靠性、可持续性。

Linux 基金会近期事件中的“开源断供”风险

其实,前段时间 Linux 基金会移除 Linux 项目中特定开发者的 MAINTAINERS 权限,跟上面分析的“开源断供”风险还有一些不同。

首先,根据目前的信息,Linux 基金会实际做的操作是根据一份美国政府的制裁名单,确保被制裁的个人或为被制裁企业提供服务的个人不出现在 MAINTAINER 列表上。这在规则上,并不影响 Linux 项目接收这些人提交的代码补丁。此外,被制裁的个人和企业实际上仍然可以自由地使用 Linux 软件。从供应链角度上看,没有直接的“断供”风险。

但是,这样的行为对特定下游用户的使用仍然是有实际影响的。例如,移除特定 MAINTAINERS 可能会导致 Linux 的某些模块现在事实上处于无人维护的状态,因此,原本将这些模块视为可靠依赖的下游,将不得不重新评估如何处理对相关模块的依赖。此外,企业由于受到制裁,而无法将任何员工培养成关键开源项目的维护者,可能会严重影响企业采用该开源项目的成本评估。这些就是更加复杂而大部分企业暂时遇不到的情况了,本文不做展开讨论。

尾注

卫 Sir 关于 Linux 基金会移除 MAINATINER 事件的相关评论文章如下:

此外,大部分人对于开源理解的种种问题,实际上都可以通过阅读开源协议、开源定义来解决。我在日常交流中发现,绝大部分开发者和软件使用者根本没读过开源协议的原始文本,全靠各种二手信息、一图流懒人包,甚至一句话、一个词概括来了解某个开源协议的内涵。这不仅会导致“开源断供”式的误解,也可能导致近期另一个热点事件上出现的关于开源与营利,开源的产权保护方面的一系列误解。

建议各位读者在讨论开源问题时都先尝试阅读理解相关材料的原文。卫 Sir 出品的一系列人话解读开源协议文章就是一个很好的起点。

开源嘉年华纪实

上周在北京参与了开源社主办的 2024 中国开源年会。其实相比于有点明显班味的“年会”,我的参会体验更像是经历了一场中国开源的年度嘉年华。这也是在会场和其他参会朋友交流时共同的体验:在开源社的 COSCon 活动上,能够最大限度地一次性见到所有在中国从事开源相关工作的人。

我在本次会场上有两个演讲,分别是 Rust 分论坛上分享的《高性能 Rust 编程:如何减少数据拷贝和并发开销》,以及开源运营分论坛介绍的《商业开源如何重塑社群信任》。本文将简要介绍我的分享内容,以及参会的见闻纪实。

开源社的午后香茗

开源社的所有成员都是志愿者。也就是说,开源社没有任何一个全职员工,所有成员都是依靠对开源的热爱自愿贡献的。我从 2022 年开始志愿参与开源社的工作和活动,从 2023 年开始参与顾问管理委员会提供建议。

今年的 COSCon 正好也是开源社成立十周年的嘉年华,在活动现场作为组织成员有幸收到了开源社准备的一盒普洱茶:

午后香茗

茶客基操

作为一个典型潮州人和饮茶爱好者,这是我本次活动最满意的收获。可以看到,从会场回来三天,每天一颗普洱茶,正好。

主题分享:高性能 Rust

第一天下午我分享的主题是《高性能 Rust 编程》。

我从今年初开始把 Rust 语言作为我的主力编程语言。这主要是我观察到我长期投入的数据系统领域,出现了一个由于云基础设施的崛起带来的创新机会,而这个创新机会的探索者们大多采用 Rust 语言编程和构建生态。于是,我也加入其中。

我的 Rust 码力值

可惜的是,截止我演讲的前一刻,我的 Rust 代码量仍然以 1% 的微弱差距落后于我早年编写 Java 大数据系统时生产的代码。

不过,在过去的一年里,我充分体会到了 Rust 生态的勃勃生机,全面投入到生态共建当中,包括:

本次分享的内容就是在这些工作当中积累的经验,每一页 PPT 都是朴实无华地贴代码讲代码(bushi)。主要介绍的两个核心论点:一个是在 Rust 所有权系统下,如何规避常见的由于绕过所有权挑战带来的拷贝开销;另一个是破除 Async Rust 的性能迷思,介绍其远不及完善的应用现状。

目前,关于 Rust 编程的最佳实践,包括工程组织、接口设计和性能优化,还处于一个激烈讨论的环境中。整个 Rust 生态的繁荣和完整程度,我经常把它比作 Java 1.5-1.7 时代,即当今统治生态的类库和软件尚未诞生或方兴未艾。应该说,Rust 生态的广阔天地,大有可为。任何想要赶上 Rust 主导的软件发展浪潮的人,现在上车还为时未晚。

主题分享:商业开源

第二天下午,我在开源运营和开发者关系论坛分享了《商业开源如何重塑社群信任》的主题。




这个话题其实不太应该出现在“开源运营和开发者关系”论坛,而是比较适合一个独立的“商业开源”论坛,即讨论在商业活动中如何利用开源要素创造优势,还有营利性组织如何做出开源贡献以及背后的原因。

我在做这个分享的时候更多像是对自己近年来的思考做一个总结,从开源项目的分类学,商业开源的方法,到商业开源和“开源商业化”的思辨。这大概会导致信息量太大而听众难以接受。

我目前在实践自己分享里提到的商业开源理念,也颇感有趣的看到我对“开源商业化”的批判接连被印证。或许以后我再讲这个话题的时候,就不会像这次一样做一个总纲式的分享,而是抽取一两个典型的案例和瞬间来传播。

承接上一个主题讲的这一张 PPT 实际不在演讲的 PPT 里🤣

开源社的下一个十年

这个话题其实我讲不了(笑)。只是在这里介绍一下我所认识的开源社,它是做什么的以及未来可能会做什么。

近年来,开源社除了一年一度举办 COSCon 以外,还联合其他社群伙伴推出了《中国开源年度报告》中国开源先锋中国开源码力榜等报告和评选活动,丰富了开源社作为一个公益团体在开源主题上传递出的观点。

但是,相比起开放源代码促进会(OSI)TODO Group 这样,在通用开源和企业开源领域能够提出见解和倡议的组织,开源社在掌握开源话语权这个议题上显得比较弱势。随着中国开源力量的崛起,如何引导各方参与者正确认识开源,在开源环境当中高效协作创新,将是一个无可回避的问题。我热切期望开源社能够发挥自己所在生态位的优势,联合开源社的志愿者,向社会不断输出批判性观点,帮助中国开源茁壮成长。

此外,开源社从去年开始,成立了一系列开源社城市社区(KCC),包括北京、上海、广州、深圳、成都、杭州、长沙、大连、南京和新加坡等。在一年一度的开源嘉年华之外,日常组织起中华开源的 Meetup 活动,日积月累的提高开源理念在群众当中的影响力。如果任何个人或组织想要在本地聚集开源人办活动,都可以考虑联系 KCC 合作落地。

开源嘉年华:交个朋友

最后,我想以“交个朋友”作为本次开源嘉年华参与体验的总结。

这一次 COSCon 的绝大部分分论坛,都是由开源社和一个或多个合作社群共同举办的,所以才能攒成 21 个论坛的盛况。

在主会场上,我能看到开源公益的从业者分享他们的故事,看到青少年开源开发者介绍自己的作品,看到开源人展现自己多彩的生活。

开源从来都是一个广阔的概念,秉持着开放和合作的基本理念在不同的细分领域创新价值。但是,开源也只是每个开源人生活当中的一部分。本届开源嘉年华的主题“开源新生活(Open Source, Open Life)”,传递出的是开源社一直以来的人文关怀。

希望所有参与开源的贡献者,都能在开源共同体当中认识新朋友,开心开源,在开源生活中收获快乐 :D

从参与 Rust 标准库开发看开源贡献的源动力

首先介绍一下我在 Rust 标准库当中做的两个微小的工作。

第一个是从去年 8 月 14 日发起,今年 4 月 6 日合并,历时约 8 个月,目前仍在等待 stabilize 的为 OnceCellOnceLock 增加新接口的提案:

第一次提交贡献

第一次贡献成功合并后,马上第二个工作是从今年 4 月 8 号开始,7 月 6 号合并,历时约 3 个月,同样还在等待 stabilize 的为 PathBufPath 增加拼接扩展名新接口的提案:

第二次提交贡献

可以看到,这两次贡献的内容可算是同种类型的,第二次提交从发起到合并的时间比第一次缩短了一半以上。本文先介绍 Rust 标准库提案的基本工作流程,然后介绍这两个贡献背后的故事,最终讨论开源贡献的源动力从何而来。

Rust 标准库提案的工作流程

标准库的贡献有很多种,上面我所做的两个贡献都是扩充现有数据结构的接口,也就是 API Change 类型的提案。

Rust 社群为 API Change Proposal (ACP) 设计了专门的流程,流程文档记录在 std-dev-guide 手册中。

简单来说,一共分为以下三个步骤:

  1. rust-lang/libs-team 仓库中创建一个新的 ACP 类型的 Issue 开始讨论;
  2. 讨论形成初步共识以后,修改 rust-lang/rust 仓库中标准库的代码并提交 PR 开始评审;
  3. 针对实现达成共识以后,在 rust-lang/rust 仓库中创建对应的 Tracking Issue 并由此获得标注 unstable feature 需要的参数(Issue 编码);PR 合并后,由 Tracking Issue 记录后续的 stabilize 流程

不过,其实我所做的两个简单改动并没有严格遵循这个流程。因为我首先不知道这个流程,加上实际改动的代码非常少,我是直接一个 PR 打上去,然后问一句:“老板们,这里流程是啥?我这有个代码补丁还不错,你看咋进去合适。”

开源贡献的源动力从何而来

为什么要给 OnceCell 和 OnceLock 加接口?

真要说起来,给 OnceCell 和 OnceLock 加接口的起点,还真不是直接一个 PR 怼脸。上面 PR 截图里也能看到,首先是我在原先加入 once_cell 功能的 Tracking Issue 提出为何没有加这两个接口的问题:


进一步地,之所以会提出这个问题,是我当时在实现 Apache Kafka API 当中 RecordBatch 接口,其中涉及到一个解码后缓存结果的优化。因为 Rust 对所有权和可变性的各种检查和契约,如果想用 OnceCell 来实现仅解码(初始化)一次的效果,又想要在特定上下文里取得一个可变引用,那就需要一个 get_mut_or_init 语义的接口。

实际上,这个接口的实现跟现有的 get_or_init 逻辑几乎一致,除了在接收者参数和结果都会加上 &mut 修饰符以外,没有区别。所以我倾向于认为是一开始加的时候没有具体的需求,就没立刻加上,而不是设计上有什么特殊的考量。

于是,开始讨论后的下一周,我就提交了实现功能的代码补丁。很快有一位志愿者告诉我应该到 rust-lang/libs-team 仓库走 ACP 的流程,我读了一下文档,尝试跟提出这个要求的志愿者 confirm 一下具体的动作。不过马上就是两个多月的毫无响应,我也就把这件事情暂时搁置了。

经典开始等待……

11 月,两位 Rust 语言的团队成员过问这个补丁。尤其是我看到 Rust 社群著名的大魔法师 @dtolnay 表示这个补丁确实有用,应该合进去以后,我感觉信心倍增,当周就把 ACP 的流程走起来了。

随后就是又两个月的杳无音讯……

今年 1 月,@dtolnay 的指令流水线恰巧把这个 PR 调度上来,给我留了一个 Request Changes 让我修一下编译。发现我凭直觉写出来的合理代码遇到了另一个 Rust 编译器的 BUG 导致编译不过:

解决以后,社群志愿者 @tgross35 指导我给新的功能添加 unstable feature 的属性注解,也就是:

1
#[unstable(feature = "once_cell_get_mut", issue = "121641")]

另一位社群志愿者 @programmerjake 帮我指出几个文档注释的问题,为标准库添加新接口不得不写好接口文档,摆烂不了。

在此过程中,我给 OnceLock 也同样加了 get_mut_or_initget_mut_or_try_init 接口,因为它和 OnceCell 本来就是同期进来的,接口设计也一模一样。我想那就一起加上这个接口,免得 OnceLock 后面有同样的需要还要另走一次 ACP 流程。

这悄然间又是两个月过去了……

终于,在 @programmerjake Review 完之后一周,@dtolnay 出现并 Appove 后合并了代码。

第一次 Rust 主仓库参与贡献至此告一段落,唯一剩下的就是等 libs-team 主观认为合适之后启动功能 stabilize 的流程。

上个月,有人发现之前写的文档注释还是有错误,于是提交 PR 修复。这就是某种开源协同让代码质量向完美收敛的过程。

为什么会想到给 Path 加接口?

这个动机要追溯到我用 Rust 重写 License Header 检测工具 HawkEye 的时候了。

Rust 重写 HawkEye 的动机,则是为了支持调用 Git 库跟 gitignore 的机制做集成。虽然原先 Java 的实现可以有 JGit 来做,但是 JGit 的接口很脏,而且加上 JGit 以后就不能 Native Image 了导致分发产物体积显著变大。虽然都不是什么大事,但是工具类软件本来就是强调细节处的开发者体验,正好我逐渐掌握了 Rust 编码的技巧,就拿上来练练手。

今年 3 月重写 HawkEye 的时候,我发现了 Rust 标准库里 Path 结构缺一个接口:

1
2
3
4
let mut extension = doc.filepath.extension().unwrap_or_default().to_os_string();
extension.push(".formatted");
let copied = doc.filepath.with_extension(extension);
doc.save(Some(&copied))

我在写上面这段代码的时候,目的是给文件名加一个 .formatted 后缀。但是,Path 的 with_extension 接口是直接替换掉扩展名,也就是说,原本我想要的是 file.rs 变成 file.rs.formatted 的效果,如果用 with_extension 接口,就会变成 file.formatted 这样不符合预期的结果。

最终,只能用上面这样的写法手动绕过一下。显然代码变得啰嗦,而且这段代码是依赖 extension 的底层实现细节,某种程度上说是 brittle 的。

遇到这个问题的时候,我就想给标准库直接提一个新的 PR 了。不过当时 OnceCell 和 OnceLock 的 PR 还没合并,我拿不准 Rust 社群的调性,也不想开两个 PR 都长期 pending 下去。

好在上一个 PR 在几周后顺利合并了,我于是同时创建了 ACP 和 PR 以供社群其他成员评审。因为改动面很小,比起口述如何实现,不如直接在 ACP 里链接到 PR 讲起来清楚。

这次,提案很快得到 Rust 团队成员,也是我的前同事 @kennytm 的回应。不到两周,我就按照上次的动作把 PR 推到了一个可以合并的状态。

然后又是两个月的杳无音讯……

最后,我根据其他社群成员的指导,跑到 Rust 社群的 Zulip 平台上,在 libs-team 的频道里几次三番的问:“这个 ACP 啥时候上日程啊?”

终于,在今年 7 月初,libs-team 在会议上 appove 了 ACP 的提议,并在一周内再次由 @dtolnay 完成合并。

开发者的需求是开源贡献的源动力

可以看到,上面两个贡献的源动力,其实都是我在开发自己的软件的时候,遇到的 Rust 标准库缺失的接口,为了填补易用接口的缺失,我完成了代码开发,并了解社群流程以最终合并和发布。在最新的 Rust Nightly 版本上,你应该已经能够使用上这两个我开发的接口了。

在开源共同体当中,一个开发者应该能够发现所有的代码都是可以更改的。这是软件自由和开源定义都保证的事情,即开发者应该能够自由的更改代码,并且自由地使用自己修改后的版本。

那么,开发者在什么时候会需要修改代码呢?其实就是上面这样,自己的需求被 block 住的时候。小到 Rust 标准库里接口的缺失,大到我之前在 Apache Flink 社群里参与的三家公司合作实现 Application Mode 部署,甚至完全重新启动一个开源项目,说到底核心的源动力还是开发者真的对这样的软件有需要。

因此,开发者的需求是开源贡献的源动力。一旦一个开源软件不再产生新需求,那么它就会自然进入仅维护状态;而一旦一个开源软件不再能够满足开发者实际的需求,甚至开发者连直接修改代码的意愿也没有,那么它的生命也就到此为止了。

Logforth: 从需求中来的 Rust 日志功能实现

由此,我想介绍一个最近在开发 Rust 应用的时候,由自己需求出发所开发的基础软件库:

Logforth: A versatile and extensible logging implementation

Rust 生态的日志组件,由定义 Logging 接口的 log 库和实现 Logging 接口的各个实现库组成。其中,log 库可以类比 Java 日志生态里的 SLF4J 库,而日志实现则是 Logback 或 Apache Log4j 这样的库。实际上,Logforth 的名字就来自于对 Logback 的致敬。

编写 Logforth 的动机,是我在选择 Rust log 实现的时候,现有所有实现都不满足我的灵活定制需求。生态当中实际定位在完整 log 实现的库,只有 fern 和 log4rs 这两个。其中,fern 已经一年多没有新的发布了,一些显而易见的问题挂在那里,内部设计也有些叠床架屋。而 log4rs 则是对 Log4j 的直接翻译,也有许多非常诡异的接口设计(底层问题是 Java 的生态和设计模式到 Rust 不能直接照搬)。

于是,我花了大概两个小时的时间,梳理出 Rust 和 Java 日志生态里日志实现库的主要抽象:

  • Appender 定义日志写出到目标端的逻辑;
  • Filter 定义日志是否要打印的逻辑;
  • Layout 定义日志文本化的格式。

然后花了一天的时间,把 log4rs 的主要 Appender 即标准输出和(滚动)文件输出给实现了,同时完成了基本的基于日志级别的 Filter 和纯文本以及 JSON 格式的 Layout 实现,就此发布的 Logforth 库。

经过两周的完善,目前 Logforth 库已经具备了全部日志实现所需的基本能力,且可以自由扩展。接口都非常干净,文档也都补齐了。除我以外,一开始一起讨论需求的 @andylokandy 也极大地帮助了 API 的设计跟多种常用 Appender 的实现和优化。应该说,目前的 Logforth 已经是超越 fern 和 log4rs 的库了。

而回到本文的主题,之所以我能在一天时间内就写出第一个版本,我跟 @andylokandy 能目标明确、充满动力地实现 Logforth 的功能,就是因为看到了自己和整个 Rust 生态的需求,并且我们清楚应该怎么实现一个足够好的日志库。

Rust 生态的诸多潜在机会

最后,为坚持到这里的读者分享几个我看到的 Rust 生态潜在的机会。

总的来说,目前整个 Rust 生态接近 Java 1.5 到 1.7 时期的生态,即语言已经流行开来,核心语言特性和标准库有一些能用的东西,整体的调性也已经确定。但是,距离 Java 1.8 这样一个全面 API 革新的版本还有明显的距离,核心语言特性的易用性有很大的提升空间,标准库的 API 能用但算不上好用,开源生态里开始出现一些看起来不错的基础库,但是还远远没有达到 battle-tested 的状态。

第一个巨大的缺失点就是 Async Rust 的实用工具。我找一个 CountdownLatch 的实现,找了半天才发现去年底发布的 latches 库符合我的期待。要是去年我有这个需求,就真没人能搞定了。后来我要找一个 Async 版 WaitGroup 的实现,找来找去没有一个合适的,只能自己 fork waitgroup-rs 改改来用。

Crossbeam 是个很不错的高质量库,可惜它提供的是同步版本的并发原语,不能在 Async Rust 里使用。上面 latches 和 waitgroup-rs 单看实现得还可以。但是这种单文件库,真的很难长期维护,而且像 latches 这样一个结构硬整出各种 feature flags 的做法,其实是反模式的,没必要。

所以一个 async-crossbeam 可能是目前我最想看到的社群库,或许它可以是 futures-util 的扩展和优化。这些东西不进标准库或者事实标准库,各家整一个,真的有 C++ 人手一个 HashMap 实现的味道了。

第二个缺失点,顺着说下来就是 Async Runtime 的实现。Tokio 虽然够用,但是它出现的时间真的太早了,很多接口设计没有跟 Async Rust 同步走,带来了很多问题。前段时间 Rust Async Working Group 试图跟 Tokio 协商怎么设计标准库的 Async Runtime API 最终无疾而终,也是 Tokio 设计顽疾和社群摆烂的一个佐证。

async-std 基本已经似了,glommio 的作者跑路了,其他 xxx-io 的实现也有种说不出的违和感。这个真没办法,只能希望天降猛男搞一个类似 Java ExecutorService 这样的体系了。而且最好还要对 Send + Sync + 'static trait bound 做一些参数化,不用全部都强行要求……

IO 和 Schedule 虽然有关系,但还是不完全一样的概念。总的来说 Rust 生态里暂时没有出现跟 Netty 一样 battle-tested 的网络库,也没有能赶上 ExecutorService 生态的 battle-tested 的异步调度库。不过好的调度原语库还是有一些的,比如 crossbeam-deque 和 soml-rs 里的相关库,等待一个能合并起来做高质量 Runtime 的大佬!

第三个缺失点还是跟 Tokio 有关。应该说 Tokio 确实做得早而且够用,但是很多设计到今天看就不是很合适了,然而社群里用得还是很重,就变得更不好了。比如上面的 log 库,Tokio 生态里搞了个叫 Tracing 的跟官方 log 库不兼容的接口和实现,也是一大堆槽点。

这里想说的是 bytes 库。很多 Rust 软件闭眼睛就用 bytes 处理字节数组,这其实有很大的性能风险。例如,基于 bytes 搞的 PROST 就有一些莫名其妙的的多余拷贝,可参考《Rust 解码 Protobuf 数据比 Go 慢五倍?》。值得一提的是,PROST 也是 Tokio 生态的东西。

Apache OpenDAL 内部实现了一个 Buffer 抽象,用来支持不连续的字节缓冲区。这个其实也是 Netty 的 ByteBuf 天生支持的能力。如果现在 Rust 有一个 Netty 质量的网络库,有一个 ExecutorService 的接口跟一些基本可用而不像 tokio 一样全家桶的实现,再把文件系统操作搞好点,我想整个 Rust 生态的生产力还能再上一个台阶。

Rust 的 Async FileSystem API 实现现在没有一个能真正 Async 的,这个其实 Java 也半斤八两。不过 Rust 的 io::copy 不能充分利用 sendfile syscall 这个,就被 Java 完爆了。这点是 Apache Kafka 实现网络零拷贝的重要工程支撑。

最后一个,给国人项目做个宣传。Rust 的 Web 库也是一言难尽,同样是 Tokio 生态的 Axum 设计让人瞠目结舌,整个 Rust 生态对 Axum 的依赖和分发导致了一系列痛苦的下游升级体验,可参考 GreptimeDB 升级 Axum 0.7 的经历,至今仍然未能完成。

国内开发者油条哥搞的 Poem 是更符合 Web 开发习惯的一个框架,用起来非常舒服,也没有奇怪的类型挑战要突破。Poem 的最新版本号是 3.0.4 而不是像 Axum 的 0.x 系列,这非常好。

不过,Poem 的接口文档、回归测试跟一些细节的易用性问题,还需要更多开发者使用慢慢磨出问题跟修复。希望 Poem 能成为一个类似 Spring 的坚固 Web 框架,这样我写 Rust 应用的时候,也能省点心……

举个例子,我的一个 Poem 应用里有 query handle panic 的情况,就踩到了 Poem 一个并发对齐的设计没有处理 panic 的缺陷。当然,我顺手就给修了,所以新版本里应该没这个问题。

至于文档不全的例子,主要是错误处理的部分,应该需要更多最佳实践。

无论如何,目前 Rust 生态整体的生产力和生产热情,还是比 Java 要高出太多。长期我是看好 Rust 的发展的,短期我只能安慰自己 Netty 是一个在 Java 1.6 才出现,1.8 才开始逐渐为人熟知使用,5.x 胎死腹中,发展时间超过 15 年的项目,生态要发展并不容易了。

CommunityOverCode Asia 2024 参会纪实

CommunityOverCode Asia 2024 于 7 月 26 日到 28 日在杭州举行。其前身是 Apache 软件基金会一年一度的 ApacheCon 活动,通常会在多地分别举办。今年已经举行的是本次亚洲大会,以及 6 月在斯洛伐克举行的欧洲大会。10 月,北美大会将在丹佛举行,我也会在北美的会议上分享两个主题:

  • Mobilize Your Community Army: A Commercial OpenSource’s Perspective
  • Equip the Community with Test Suite: Best Practice from Apache OpenDAL and more

欢迎届时关注。

本文分享我在刚刚过去的 CommunityOverCode Asia 2024 大会上的见闻和体会。

会场体验

本次活动现场,主要分成以下四个部分:

  1. 主会场
  2. 各个分论坛的会场
  3. Workshop
  4. 开源集市等自由活动区域

Workshop 我没进去,不知道具体什么情况。

各个分会场的空间今年是有点狭小了,即使全部坐满,估计也就四十个人的水平。同时,狭小的空间里开猛烈的空调,相对来说更容易犯困或身体不适。我在各个分会场里都没有待太久的时间。

Community 分会场由于有几位海外讲师,并且确实到场的外国友人比较多,是唯一有文字同传的分会场。但是文字同传需要视线在讲师、PPT 和翻译软件上来回切换,且翻译有几秒钟的明显延迟,现场的外国友人普遍反应体验一般。

主会场是整个会场当中体验最好的一部分,屏幕比去年的屏幕看起来要大上很多。我想如果我在上面讲 Keynote 演讲,应该也不得不好好准备了(笑)。

巨大的主会场屏幕

最后,开源集市布置在一个狭小的过道上,整体体验上比较局促。不过参与展览的项目和组织倒是不错,我除了在 GreptimeDB 摊位上不定时刷新,也在 Apache 基金会摊位上客串大半个小时的 NPC 跟不少参会者做了交流。

Greptime 的文创广受好评

商业、开源以及出海

这次大会上我除了出品 Community 分论坛以外,主要参与分享了两个主题,分别是:

  • 第一天主会场的圆桌讨论《国际化的机遇和挑战》
  • 第二天 Community 分会场的主题分享《动员你的社群大军:商业开源视角下的开发者关系》

可以看到,两者围绕了商业、开源和出海等主题展开。

其中第一天的圆桌,本来我以为要讲一些比较 tough 的话题,比如开源社群国际化当中的挑战,商业开源出海如何取得成功,结果讨论的都是比较柔和的话题,包括应该什么时候做国际化,国内和海外参与者的不同,社群如何建设之类有定论的主题。整体气氛轻松欢乐。

社群成功的关键是及时响应;参与开源社群可以是轻松的

第二天的主题分享,由于事前我没有做好排练,外加分会场冷气 debuff 和我的演讲时间被压缩到了 15 分钟,整体分享效果应该说非常糟糕。我实际想说清楚的是:

  • 开源社群不同分类的动机及可持续的根基;
  • 商业开源的模式和商业与开源的共生关系;
  • 面向商业开源模式的开发者关系如何实施。

其中,我目前主要的观点是,持续的开源创造,要么是个人自己所需,自产自销;要么有其他收入来源,支付开源开发的成本。后者对于社群团队来说,就是募资和加入企业就职;对于商业团队来说,需要商业盈利,反哺上游开源软件,核心基础能力走开源孪生构建生态的同时帮助大众。大公司支持的企业开源,是特定的标准化工作能规避的巨大成本风险。

商业开源的理论蓝图

这个主题我会在 10 月北美的大会上再做一次英文演讲,应该到时候做好排练调整一下内容顺序,效果会更好一些。敬请期待到时的分享和专项说明文章。

此外,作为 Community 分论坛的出品人,本应由我担任的分论坛主持人一职,实际最后是 Ted Liu 和 Yu Liu 客串完成的。感谢两位的支持 :D

现场与场外的交流

虽然本次会场没有 Unconference 一类专门交流的节目,但是参会者在开源集市、会场休息处和其他场外地点做的交流可一点都不少。应该说,这样一场行业瞩目的开源峰会,来到现场的人在主题分享之外的交流,也是会议价值不可缺少的组成部分。甚至以其创造的价值总量而言,它是超过主题分享的。

夜话 ASF Culture

得益于来自中国的 ASF 董事姜宁老师亲自前往北美和欧洲的邀请,本次亚洲大会有很多外国友人参会和做分享。

由于我跟外国讲师恰巧都住在会场附近的酒店,在第二天晚上 Track Chair 聚会结束以后,我跟几位外国讲师回到酒店大堂边吃夜宵边聊起了在 ASF 的经历和各地文化的差异。

几位讲师都是游历各地的老江湖了,他们分享起各地的风俗和自己的人生经历,十分有趣。尤其是他们面对跟自己儿子乃至孙子同辈的我解释笑话里面的梗的时候,连比带划的介绍然后又想要跳过黄暴的部分,非常迫真。

线下交流会让每个人都发现,其实线上看起来一板一眼,只知道规则只关心编码的人,也是一个活生生的有自己生活的人。大家对生活的热爱和基本的品质是共通的,由此在线上产生矛盾的时候,也更容易站在对方的角度考虑其动机,而不是直接假定对方是故意搞破坏的恶人。

除了交流各地的文化和趣闻,作为资深的开发者和常年参与 ASF 事务的基金会成员,我们也交流了不少软件研发过程当中的经验、近期软件发展的趋势,以及对 ASF 文化、议事规则跟近期冲突争议事件的看法。我想,这就是 ASF 纪录片里提到的,ASF 最大的价值是其成员之间的共识,而这种共识是由每次具体的讨论反复达成和演进的吧。

在跟国内某开源软件基金会的朋友交流的时候,谈及 ASF 的流程和议事规则,我们都很惊讶 ASF 大量的步骤依赖于一个看起来很不可靠的“达成共识”。例如项目如何进入孵化器?只要孵化器的项目管理委员会(PMC)成员达成共识即可。进入孵化器的导师如何寻得?答案是只要项目足够有趣,不少 PMC 成员会志愿担任导师。孵化项目版本发布如何保证合规?几乎每时每刻都有导师志愿帮助检查,而且这种检查可能是非常严格的。

这种依赖志愿者的做法,并且数百名孵化器志愿者有着相似的共识,上万名 Committer 主观接受的理念,才是 ASF 有别于其他组织的核心竞争力。

虽然出于这次夜话 private 的性质,我没有留下合影。不过会场内单独的合影还是有的:

Craig L Russell

前几周去湾区的时候跟 Craig 吃了一顿泰国菜,听他吹牛在 IBM 和 Oracle 的工作经历,吹得太厉害忘记合影留念,这次可算补上了。

Justin Mclean

Justin 跟我介绍了几个他的拿手好菜。希望下次发版或者过提案的时候,别把我当成菜炒了 >~<

后起之秀的开源项目

大会现场有位中国开源的老玩家问我,是不是感觉每年参与开源大会的都是同一批人。我说或许主导国内开源方向,跟参与社群运营的主力军,这些年的中流砥柱都没有发生太大的变化,但是我作为 ASF 孵化器导师,不停地能看到新项目进来,还有新的开发者投身开源,从这个角度来讲,其实中国开源的新面孔还是非常多的。

今年让我印象深刻的后起之秀有这么三位。

第一位是在主会场做主题演讲的 Apache Fury 原作者杨朝坤(Shawn)。虽然他在分会场讲序列化框架的技术细节讲得那叫一个深入,但是在主会场上还是很好的把握了大众的需求,分享了一个 Side Project 如何从蚂蚁集团内部孵化,经历一次申请开源失败以后,通过一段时间的內源运营以后成功开源并进入 ASF 孵化的故事。

Apache Fury 的开源之旅

项目成功的故事是群众喜闻乐见的。Shawn 富有感染力的分享打动了现场的听众,Apache Groovy 的 PMC Chair Paul King 投喂了一个明知故问的技术问题,让 Shawn 有机会进一步展开作为跨语言 IDL-Free 的序列化框架 Fury 的设计优势。

Paul King 抛出“诱饵”问题

我客串了一把技术翻译

第二位是在主会场做闪电演讲,在 Community 分会场也做了分享的 Apache StreamPark Committer 张超(@VampireAchao)。作为一名 00 后程序员,他非常外向且思维活跃,编程技巧过关,假以时日应该会成为强大的开源开发者。

闪电演讲:Chrome 的 Debug 技巧

第三位是满会场宣传 Apache Amoro 的陈政羽。开源运营是一个富有挑战的领域,尤其是并非自己开发且独占主导权的项目,如何协调不同参与方的需求,跟核心开发者商量项目的定位和演讲方向,是非常不容易的。

经过本次会场讨论,Apache Amoro 当前的实质定位是增强 Apache Iceberg 的自动调优服务。一言以蔽之,Iceberg++ 是其核心价值点。不过这跟部分核心开发者希望兼容 Hudi 的负载,想开始超融合所有数据集成生态的目标有出入。我们可以拭目以待这个项目后续的发展,以及陈政羽在其中协调结果的成色🤣。

SelectDB 乱入 O_O

未来的开源峰会

CommunityOverCode Asia 2024 圆满结束。今年,国内还尚未开幕的开源峰会当首推开源社将于 11 月举办的中国开源年会(COSCon)。

定档 11.2-3,COSCon’24 第九届中国开源年会暨开源社十周年嘉年华正式启动!

巧合的是,今年是 Apache 软件基金会 25 周年,也是开源社成立的 10 周年。或许从未来回头看,今年会是中国开源继往开来的关键年😇。

此外,今年的 CommunityOverCode 大会还有 10 月的北美大会尚未举办。我将于 10 月到达丹佛会场做两个主题分享:

  • Mobilize Your Community Army: A Commercial OpenSource’s Perspective
  • Equip the Community with Test Suite: Best Practice from Apache OpenDAL and more

欢迎届时关注。

最后,去年和今年的 CommunityOverCode Asia 承办单位几乎都是贴钱办会,以至于活动组织者专门写了一篇文章“化缘”:

正在消亡的社区会议:呼吁更多伙伴持续支持阿帕奇亚洲大会的举办

我想,作为面向开发者的大会,最希望看到 CommunityOverCode Asia 顺利举办的,是每一位热爱开源的开发者。我向组织者建议,明年筹款的时候,其一可以实时公布筹款进度,让潜在的赞助者知道要想会议成功举办,还需要自己的多少支持;其二可以开通个人赞助的渠道,我想参与开源的开发者们众志成城,并不难凑齐这样一场年度盛会所需要的小几十万元人民币。

我在这里承诺,如果明年开通了个人赞助的渠道,我将以本人的名义或届时所在公司的名义,捐赠不少于十万元人民币以作支持。期待明年 CommunityOverCode Asia 越办越好!

Apache DataFusion 湾区线下聚会纪实

6 月 24 日,我在北美湾区参与了一场线下的 Apache DataFusion 聚会活动。

其实我是 6 月 21 日才到的旧金山,6 月 18 日才发现湾区有这样一场线下活动。不过或许得益于我在今年在 DataFusion 做过几次贡献,GreptimeDB 是 DataFusion 在行业顶级的应用标杆,会议组织方很干脆的就增加了一个 Speaker 席位,让我能够做在聚会上做题为《Boosting a Time-Series Database With Apache DataFusion》的演讲。

会议风格

本次 Meetup 一共报名 80 人,实际到场超过 50 人,绝对算是小型 Meetup 里的大型聚会了。现场是一位联合主办人的办公室,堪堪容纳所有人或站或坐待在室内。

我印象最深的是海外 Meetup 更加注重 Get Together 的风格,与国内 Meetup 基本是讲师从头讲到尾的风格的差异。

一般来说,国内的 Meetup 一个主题可以讲 30-40 分钟,4-6 名讲师讲完一个下午也就结束了。往往参与者也不怎么听完主题,而是逮到相关的到场专家,或者听到感性的主题以后,就拉着专家或讲师直接另开小会去了。

我刚报上名参加湾区 DataFusion Meetup 的时候,得到的消息是演讲时长 15-20 分钟,临了又改成 12-15 分钟。基本上一共六名讲师,从六点开讲,到七点半就完全讲完了。剩下的时间留给现场的观众自由交流,当然也有人拉着看对眼的与会者出去开小会,但是总体所有人都听完了所有主题,并且也还毫不疲倦。

这是国内不少活动最近有在参考的一个趋势。不管是年初 OSPO Summit 的 unconforence 环节,还是接下来几场 Apache 软件基金会的国内会议筹备的形式,都更加注重 Get Together 与会者自由交流,而不再是发布会形式的讲师从头讲到尾。

此外,海外的组织者对这样的小型聚会明显更有热情,他们有 lu.ma 和 meetup.com 等等丰富的宣传渠道,也乐于在湾区举办各种会议交流前沿进展。这点是国内近几年来声音比较弱的方面。

活动预告

在进入演讲内容回顾之前,先预告将在未来两周举办的两场 Apache 生态线下活动。

第一场是在 7 月 21 日下午杭州蚂蚁 A 空间举办的 Apache DataFusion Meetup 活动。GreptimeDB 核心工程师,同时也是 DataFusion 社群 PMC 成员夏锐航将会做深入的技术分享;蚂蚁集团工程师和 eBay 工程师也都将分享他们基于 DataFusion 构建出来的多样系统,参与 DataFusion 社群的故事,以及对 DataFusion 未来的看法。

欢迎点击链接( https://www.huodongxing.com/event/5761971909400?td=1965290734055 )报名,或扫描以下二维码报名。

第二场从 7 月 26 日开始持续 7 月 28 日。Apache 软件基金会年度活动 Community Over Code Asia 2024 今年也将在杭州举办。我将作为 Community 分会场的出品人参与活动并做演讲。GreptimeDB 的核心工程师,同时也是 OpenDAL 社群 Committer 的徐文康也将在 IoT 分会场上做技术分享。GreptimeDB 在现场另有展位介绍,我会全程在场(たぶん)。

欢迎通过这个链接( https://asia.communityovercode.org/zh/ )报名。

演讲内容

聚会上一共有八名讲师做了六个主题演讲,分别是:

  • LanceDB 的 CEO Chang She 分享了他们利用 DataFusion 实现向量计算的实践;
  • Cube.js 的 CTO Pavel Tiuvov 分享了基于 DataFusion 实现的查询缓存层;
  • DataFusion 的现任 PMC Chair Andrew Lamb 介绍了社群发展的近况和未来展望;
  • Denormalized 的创始人 Matt Green 分享了使用 DataFusion 实现流计算引擎的实践;
  • DataFusion 的原作者 Andy Grove 和他的同事分享了在 Apple 实现了 DataFusion Comet 做 Spark 查询加速实践;
  • 我介绍了 GreptimeDB 使用 DataFusion 和整个 FADOP 技术栈快速实现了一个高完成度的时序数据库的实践。

出于演讲时间限制,我的核心内容浓缩在了 GreptimeDB 如何使用和定制化 DataFusion 的实现上:

  • 每位分享嘉宾介绍的实践,都包含了在 DataFusion 这个执行引擎库之上实现的一个分布式计算框架。虽然 DataFusion 自己有一个子项目 Ballista 某种程度完成了这项任务,但是无论是成熟度还是它为大数据生态做的许多特化设计,都不适合上面这些使用场景的具体需求。

GreptimeDB 的分布式计算框架

  • GreptimeDB 从入口处直接重新实现的整套 PromQL 查询支持。DataFusion 主要支持的是 SQL 查询的解析、优化和执行,但是 GreptimeDB 为兼容 Prometheus 的生态,借助 DataFusion 封装的框架,将 PromQL 当做一种新的方言,通过实现 DataFusion 高度插件化的接口集成到了 DataFusion 的执行引擎当中。

值得一提的是,GreptimeDB 将 promql-parser 完全开源之后,已经成为所有想要实现 Prometheus 接口的 Rust 项目的统一选择。

GreptimeDB 实现了超过 80% 的 PromQL 接口

  • 另一项 GreptimeDB 突出的技术实现,是核心工程师钟镇炽主导开发的一个高度可扩展的索引框架。GreptimeDB 在此之上实现了 MinMax 索引和倒排索引的能力,并将在 0.9 版本里推出针对文本列的全文索引。我们计划将这一框架回馈给 DataFusion 上游,共同演进丰富查询引擎的共享代码。

GreptimeDB 强大的索引框架

  • 演讲之初,我介绍了 GreptimeDB 在时序数据领域上的关键理念创新:时序数据不仅仅是指标(Metrics),同样带有时间戳和上下文信息的数据,例如事件(Events)、日志(Logs)和追踪(Traces),都可以统一在一个时序数据库下进行处理。不仅能够同时优化负载和性能,降低成本,还能通过联合分析获得更加丰富的数据洞察。

统一 MELT 的时序数据处理

  • 最后,我实际超时了大概两分钟介绍了 GreptimeDB 得以快速实现一个高完成度时序数据库所依赖的技术栈:

FADOP Data Infra 技术栈

其中 FAD 都是原先 Apache Arrow 项目群的项目,分别是:

  • Apache Arrow
  • Apache Arrow Flight
  • Apache (Arrow) DataFusion

P 对应的是 Apache Parquet 项目,实际上大量的 Parquet 实现包括 Rust 实现,都包含在 Arrow 项目仓库里。

O 对应的是 Apache OpenDAL 项目。这是由来自 Databend 的工程师漩涡实现的统一数据访问层。接上它,你的项目就可以无痛对接上百个存储后端,包括几乎所有主流云厂商的对象存储服务,都可以用统一的 API 进行读写:用过的都说好。

社群发展

其他讲师大体也介绍了如何应用 DataFusion 的故事,而 DataFusion PMC Chair Andrew Lamb 则站在社群角度分享了 DataFusion 过去一年的进展,包括:

  • 超过 1000 个项目使用 DataFusion 构建自己的软件;
  • 正式从 Apache Arrow 的一个子项目,成为 Apache 软件基金会的顶级项目;
  • 遍布全球的用户会议,包括上面提到的杭州 Meetup 活动
  • 在 SIGMOD 2024 上发布了 DataFusion 的主题论文
  • 全球开发者合作实现的若干项功能,例如查询优化加速、索引加速和内存占用改进等等,其中不少功能是由中国开发者主导或参与实现的。

Andrew Lamb 确实是 10x 工程师

我在今年开始给 DataFusion 做一些实际的贡献,在此过程中深刻感受到了 Andrew Lamb 的热情和生产力。虽然他经常过分乐观的合并别人贡献的代码,导致一些回退(regression)跟接口需要重新适配,但是他 10x 甚至 100x 的生产力能够快速再次解决这些问题。我想这才是在开源社群协同当中真正 distinguished 的能力。正如 Linus 一样,他未必要独立写出 90% 的代码来推动项目前进,而是能每天合并来自全球各地的数十个 pull request 并保证它们相互之间没有实际冲突,也不会导致回退。

对于 DataFusion 这个项目,我的看法与其官方描述一致:

DataFusion is great for building projects such as domain specific query engines, new database platforms and data pipelines, query languages and more. It lets you start quickly from a fully working engine, and then customize those features specific to your use.

它有抽象层级非常高的 SQL 和 DataFrame 接口,能让你直接获得查询计算能力;对于数据库或其他查询系统而言,它也提供了高度可定制化的框架抽象。从纯粹技术理论上说,针对一个特定的数据系统,定制一个查询引擎总是能有最高的优化上限。但是很多时候,这未必是你的系统最核心的竞争力。

因此,就像 GreptimeDB 采用 DataFusion 的历史一样,我们发现这个软件库是一个很好的起点,扩展定制的旅程也很流畅。绝大部分情况下,我们对查询优化的投入程度和生产力是不如 DataFusion 上游的,我们会将这些时间和精力更多放在 GreptimeDB 作为一个统一时序数据库的业务逻辑实现,用户体验优化,以及从云到端的协同方案落地上。或许在未来的某一天,使用 DataFusion 的项目团队拥有足够多的开发者,能够针对自己的系统设计实现一个更加优秀的查询引擎;但是更有可能的是,终其软件一生,DataFusion 都是那个更好的框架和库实现。

改良 SQL Interval 语法:一次开源贡献的经历

本文是 GreptimeDB 首位独立 Committer Eugene Tolbakov 所作。

在上一篇文章《GreptimeDB 首位独立 Committer Eugene Tolbakov 是怎样炼成的?》当中,我从社群维护者的视角介绍了 Eugene 的参与和成长之路。这篇文章是在此之后 Eugene 受到激励,从自己的角度出发,结合最近为 GreptimeDB 改良 SQL Interval 语法的实际经历,分享他对于开源贡献的看法和体验。

以下原文。


开发者参与开源贡献的动机不尽相同。

开源贡献本身是一种利用自身技能和时间,回馈社群和造福更广大受众的方式。开源社群是一个绝佳的环境。参与贡献的人在其中能够与高水平开发者自由交流并建立联系,甚至可能找到可靠的导师或合作者。对于寻求职业发展的开发者,开源贡献可以作为个人技能和经验的展示。当然,也有许多开发者参与贡献只是出于个人的热爱。这很好!当你真正投入到一个开源项目当中后,你可能会发现软件的缺陷或缺失的功能。通过提交补丁修复问题或实现功能,不仅可以消除挫败感,还能让每个使用该软件的用户都受益。

成功参与开源贡献的秘诀不仅仅是编写代码。强烈的学习欲望才是內源动力,由此推动开发者才会主动了解本不熟悉的代码库,并应对其中出现的种种挑战。社群的及时响应和其他资深成员的支持,是这种学习欲望转换为真正参与贡献的重要保障。

社群的及时响应让开源开发者感到宾至如归;其他资深成员提供的指导和反馈,帮助新成员改进自己的贡献水平。理想情况下,你应该可以测试自己的补丁,并将修改版本应用到工作或个人项目当中。这种来自真实世界的需求,尤其是本人的需求,为开源贡献提供了重要的使用场景支撑,确保你的贡献是真的有用,而不是闭门造车的产物。只有如此,通过开源参与做出的贡献才能对整个社群产生持久的影响。

回到我本人的例子上来。虽然我已经花了不少时间锻炼自己的软件开发技能,但事 Rust 对我来说仍然颇有挑战。这种“新手”的感觉可能会让一些人不愿做出贡献,担心他们的代码不够好。然而,我把愚蠢的错误当作提高技能的垫脚石,而不是气馁的理由。

GreptimeDB 社群一年多的参与贡献经历,是一段不断学习并获得丰厚回报的旅程。今天,我会向你介绍其中的一次具体的贡献。让我们亲自动手吧!(或者我应该说,爪子?🦀)

动机和背景

这次贡献主要的目的是支持一个 SQL Interval 字面量的简化语法

1
select interval '1h';

SQL 标准定义了 Interval 的语法:

1
select interval '1 hour';

这个语法相对冗长,我们希望支持上面展示的简化语法,让 select interval '1 hour'select interval '1h' 返回相同的结果。

深入研究代码后,我发现处理转换的核心功能已经存在。为了实现上面的效果,只需针对 Interval 数据类型添加一条新规则:把简化语法格式的 Interval 将自动扩展为标准语法。让我们仔细看看代码中执行相关逻辑的部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn visit_expr(&self, expr: &mut Expr) -> ControlFlow<()> {
match expr {
Expr::Interval(interval) => match *interval.value.clone() {
Expr::Value(Value::SingleQuotedString(value)) => {
if let Some(data) = expand_interval_name(&value) {
*expr = create_interval_with_expanded_name(
interval,
single_quoted_string_expr(data),
);
}
}
Expr::BinaryOp { left, op, right } => match *left {
Expr::Value(Value::SingleQuotedString(value))=> {
if let Some(data) = expand_interval_name(&value) {
let new_value = Box::new(Expr::BinaryOp {
left: single_quoted_string_expr(data),
op,
right,
});
*expr = create_interval_with_expanded_name(interval, new_value);
}
// ...

代码评审

上面是我第一次尝试写成的代码。GreptimeDB 的资深 Rust 开发者 Dennis 很快发现了改进空间

Dennis 发现了不必要的复制

代码评审是一个优秀的学习渠道。

除了简单地接受建议(因为“减少复制”的理由很明确!),我决定深入研究。通过分析建议并尝试自己解释,我巩固了对 Rust 代码和最佳实践的理解。

避免不必要的 Clone 和所有权转移

起初,我直接在 interval.value 上调用 clone 来取得一个具有所有权的结构:

1
match *interval.value.clone() { ... }

这里的 clone 每次都会创建一个新的数据实例,如果数据结构很大或克隆成本很高,那么这可能是一个性能的影响因子。Dennis 建议通过使用引用来避免这种情况:

1
match &*interval.value { ... }

匹配引用(&*interval.value)避免了 clone 的开销。同样的手法可以用在对二元运算符 left 的匹配逻辑里:

1
match &**left { ... }

这个稍微复杂一些:它使用双重解引用来获取对 Box 内部值的引用。因为我们只需要读取数据结构中的部分信息,而不需要转移所有权,因此只获取引用是可行的。这也减少了 clone 的开销。

清晰的模式匹配

在模式匹配中使用引用可以强调仅读取数据而不是转移所有权的意图:

1
match &*interval.value { ... }

只在需要所有权的时候进行克隆

在第一版代码当中,opright 总是被复制一份:

1
2
3
4
5
let new_value = Box::new(Expr::BinaryOp {
left: single_quoted_string_expr(data),
op,
right,
});

但是,其实克隆只需发生在匹配左侧变量是 Expr::ValueSingleQuotedString 变体,同时 expand_interval_name 成功的情况下。Dennis 建议把 clone 调用移动到 if let 块内,从而只在需要所有权的时候进行克隆:

1
2
3
4
5
6
7
if let Some(data) = expand_interval_name(&value) {
let new_value = Box::new(Expr::BinaryOp {
left: single_quoted_string_expr(data),
op: op.clone(),
right: right.clone(),
});
// ...

直接引用

在第一版代码当中,我用 expand_interval_name(&value) 显式借用了 value 的值。

然而,valueString 类型的值,它实现了 AsRef<str> 特质,所以它可以被自动解引用成 &str 类型。修改后的版本直接写成 expand_interval_name(value) 不需要再手动 & 取引用。

译注:

这个说的不对。其实是因为 String 实现了 Deref<Target=str> 所以不 clone 以后 &String 可以被自动转成 &str 类型,但是之前 clone 的时候传过去的是带所有权的 String 类型结构,这个时候 &value 取引用而不是把带所有权的结构整个传过去是必要的。

Deref 的魔法可以查看这个页面

总结

在这次贡献当中,代码的“效率”体现在以下三个方面:

  • 避免不必要的克隆,减少运行时开销;
  • 让借用和所有权转移的模式更清晰和安全;
  • 提升整体的可读性和可维护性。

最终版本的 visit_expr 大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn visit_expr(&self, expr: &mut Expr) -> ControlFlow<()> {
match expr {
Expr::Interval(interval) => match &*interval.value {
Expr::Value(Value::SingleQuotedString(value))
| Expr::Value(Value::DoubleQuotedString(value)) => {
if let Some(normalized_name) = normalize_interval_name(value) {
*expr = update_existing_interval_with_value(
interval,
single_quoted_string_expr(normalized_name),
);
}
}
Expr::BinaryOp { left, op, right } => match &**left {
Expr::Value(Value::SingleQuotedString(value))
| Expr::Value(Value::DoubleQuotedString(value)) => {
if let Some(normalized_name) = normalize_interval_name(value) {
let new_expr_value = Box::new(Expr::BinaryOp {
left: single_quoted_string_expr(normalized_name),
op: op.clone(),
right: right.clone(),
});
*expr = update_existing_interval_with_value(interval, new_expr_value);
}
}
_ => {}
},
_ => {}
},
// ...

开源贡献是我找到的加速 Rust 学习的绝佳方式。参与贡献 GreptimeDB 只是一个例子,说明了我如何通过开源贡献获得知识。根据读者反馈,我很高兴在未来的帖子中分享更多这些学习经验!

非常感谢整个 Greptime 团队,特别是 Dennis 提供的帮助和指导,感谢他们在我贡献过程中的支持和指导。让我们继续贡献和学习!

Rust 程序库生态合作的例子

近期主要时间都在适应产品市场(Product Marketing)的新角色,不少想法还在酝酿和斟酌当中,于是文章输出没有太多时间来推敲和选题,只能保持每月发布相关的进展或一些零碎的思考。或许我可以恢复最早的模式,多做更新但是文章内容可能不会太过完整。

原本这一期想讨论的是 ASF 开源项目代码的所有权,以及开源软件变更协议的具体含义与操作方式。但是这个话题稍显枯燥,而且要把相关细节讲清楚,还需要继续斟酌。所以我改为采取把最近开始全职投入 Rust 开发,并接触相关生态发展和合作的经历做一点梳理,分享个人在其中的所见所闻。

我会从 Rust HTTP 库的生态切入,从最近一个大事件出发,即 Rust 采用范围最广的 HTTP 库 hyper 和 http 前后发布 1.0 版本,讲述其导致的整个 Rust 应用开发上下游牵一发而动全身的变化。

起因是我在整蛊 GreptimeDB HTTP 相关代码的时候,发现项目依赖的 axum 库是 0.6 版本,而上游是 0.7 版本。这天降的升级闲手,不升有点对不起自己了。通过解决升级过程中的问题,也能帮助摸清 GreptimeDB HTTP 模块的逻辑。

不过,我显然是小看了 Rust 生态荼毒甚广的 ZeroVer 文化的威力。

ZeroVer 是一个揶揄的说法,即在采用语义化版本(SemVer)的前提下,因为各种原因,项目迟迟不愿发布 1.0 版本。

0ver 的魅力时刻

在语义化版本中,0.x 版本是在项目正式发布或说进入稳定期前,一个相对动荡的快速迭代阶段。语义化版本的核心价值是告诉用户升级版本可能面临什么变化:

  • 升级补丁版本(Patch Version):应当只有缺陷修复和性能提升等,不会破坏用户程序的改动。
  • 升级小版本(Minor Version):可能包含新功能,应当向后兼容,用户应用应当可以顺滑升级。
  • 升级大版本(Major Version):可能包含破坏性变更,用户需要做好应对逻辑甚至数据迁移的准备。

当然,在实践当中,语义化版本并不那么严格执行。尤其对于大型项目的实验性功能,是可能有一个独立的可靠机制的。但是无论如何,进入 1.0 之后就意味着项目对用户做了一个向后兼容的保证,除非升级大版本,否则用户会假设软件升级是可以非常激进的。

ZeroVer 方案的反面极端是 Apache Arrow 和 Apache DataFusion 每次发版都升级大版本的做法,很难评价。

话说回来,axum 0.6 到 0.7 的版本升级是一个巨大的变更,基本把核心的类型设计做了一个颠覆,即 Body 不再是泛型了。

这其实也是一个槽点。国内开发者油条哥做的 Poem 就不搞这些花里胡哨的泛型,直接用胖指针抹掉底下的差别,提供更好的开发体验。你说我都 HTTP 了,搞应用层接口开发了,我是跟你抠这点性能的人吗?

一个屏幕都写不完的 breaking changes 列表

Rust HTTP 生态泛型的魅力时刻

于是我着手升级 axum 的版本,一上来就是好几个屏幕的编译错误。没事,Rust 开发者的日常而已。

第一个小问题,我们依赖了 axum-test-helper 这个库,它没跟上 axum 0.7 的版本。我先试着给上游提 PR 升级:

未果。

自己维护比较头疼,其实只有一个文件,最后我在 GreptimeDB 里 vendor 掉了:

这里我又要吐槽了。axum 是不是哪里有问题,居然不提供测试套件,还要下游自己 embedded 然后去掉 (crate) 修饰词,好玩吗?虽然其实也是可以用 reqwest 套一个 TestClient 解决,按照上游的说法:“这只是很薄的一层”,但是这么简单提升使用体验的事情,为何不做呢?

反观 Poem 就提供了 TestClient 工具,开发起来舒服多了。不管是不是我一个文件就能解决,这不是下游应该解决的事情。

紧接着,发现 axum 自己的 TestClient 有落后,以及一个 Rust Nightly toolchain 的兼容问题,提 PR 解决:

Rust Nightly toolchain 的兼容问题是一个很奇妙的问题。因为 Nightly 顾名思义就是最新的 Rust 开发版本,不提供语义化版本保证,只是在 Rust 1.x 的时间线上大体向后兼容。但是结合上 Rust Stabilize 的流程,以及打开 feature gate 如果找不到 feature flag 就会编译失败等等细碎的问题,经常会导致生态在跟进 Nightly 之前有一个无法编译的窗口。

这不算是绝对的坏事,甚至推着生态跟新版本是合理的。但某种程度上其实是 Rust Stabilize 太慢,导致系统开发比如 GreptimeDB 不得不用 Nightly 版本,而出现的新问题。Rust 开发很多用 Nightly 版本,跟 0ver 可能也有某种互相呼应的巧合。在这种环境下,开发公共库并保持多平台多版本兼容,其实是一件非常困难的事情,怪不得都 0ver 了。

然后我就遇到了本次升级最大的大魔王。Rust 生态的 gRPC 库 tonic 闪亮登场!

这里的依赖关系大概是这样的。

首先,axum 0.7 除了接口变化,还有一个关键的依赖变化是把 hyper 和 http 给升到了 1.0 上。因为 Rust HTTP 生态都依赖 hyper 和 http 这两个库,这就导致如果你的接口开始交互,那么所有的结构都要同步到同一个版本。

这个问题并不那么致命。因为如果你合理的 re-export 了依赖的接口,那么同一个 crate 的多个版本是可以共存的,就像我最近在升级 Apache OpenDAL 的时候连带需要升级 reqwest 到 0.12 版本,它依赖了 hyper 1.0 和 http 1.0 但是跟 axum 的 server 端代码关系较小,所以我可以切割开:

注意以上 PR 里修改 use 语句以选择正确的 re-export 符号的变更。

不过,tonic 的情况就比较幽默了。tonic 依赖了 axum 0.6 版本,而且 tonic 和 axum 都用了 tower 作为中间件的第三方库,而且都没有 re-export 而是标榜自己能无缝接入 tower 丰富的中间件生态。

由于 tonic 尚未完成 axum 0.7 和 hyper 1.0 的升级,这下就连环爆炸了:你找不到一个合适的 tower 版本,或者说你找不到一个合适的 axum 版本,来作为 GreptimeDB 被传递关联起来的版本约束。

于是,直到今天,GreptimeDB 的升级还是未完成的状态:

不过,在三月底,tonic 上游就出现了一位大英雄开了一个升级的 PR 完成了主要的工作:

这个 PR 我看到的时候还需要两个 hyper-util 的改动,我给帮忙推着合并了:

这里又岔开一下。虽然 tonic 在 hyperium 组织下,跟 hyper 和 http 一样,但是 hyper 和 http 的作者,Rust 生态真正负责人的英雄 @seanmonstar 并不怎么看 tonic 这个库。tonic 主要是 tokio 的作者 @LucioFranco 和另外两位志愿者 @tottoto 和 @djc 维护的。他们都有自己的本职工作要做,所以并没有太多时间 Review tonic 的变动。实际上,很多项目就用着 tonic 0.11 和 axum 0.6 万年不动也还行的(有没有发现 tonic 也是 ZeroVer 流派)。

不过,经过两个月当中的空闲时间累积,这个 tonic 的升级 PR 终于看着要走进尾声,希望能顺利。开源项目能有这个效率,小几个月跟进一个大型重构,虽然比不上 @seanmonstar 和其他活跃维护项目的响应速度,也绝对算是能及时更新的了。

这个 tonic PR-3610 有很多经典的开源贡献者跟维护者之间的交流和争论,我这里就不展开了。但是我非常建议各位关心开源的人去看看,了解真实的开源世界,未来或许参与进去做出自己的贡献,而不是自己想象或者冷眼旁观。

最后,贴两张图说明 Rust 生态 ZeroVer 的严重程度:

GreptimeDB 的依赖 ZeroVer 有 782 个

GreptimeDB 的依赖非 ZeroVer 有 278 个

在 GreptimeDB 的依赖里,ZeroVer 的占比约莫七成。

其实,GreptimeDB 很多升级都很有工程上的说法,欢迎各位关注发现。我可能也会在今年的某些 Rust 会议上分享相关的经验。

文末放两个我在 Reddit 上跟这次 HTTP 生态大更新相关的讨论链接,在 Rust channel 上还算有一些有趣的讨论。

应当在 ASF 孵化器中帮助项目

由于 Apache 软件基金会(ASF)过去十年在国内的文化传播,许多开源软件的创作者都有一个将自己的软件捐赠到 ASF 并最终成为顶级项目的梦想。我所接触到的 Apache Fury / Apache OpenDAL / Apache StreamPark 都有这样的背景。

按照目前 ASF 的章程和惯例,开源项目要想成为 ASF 顶级项目,绝大多数情况下需要经过 ASF 孵化器孵化。

这个 ASF 孵化器本身是一个 ASF 的顶级项目,与其他项目一样由项目管理委员会指导,通常称为 IPMC 即孵化器(Incubator)项目管理委员会(Project Management Committee)。IPMC 成员规模应当是所有 ASF 项目中最多的,截至今天 IPMC 共有 285 人,项目 Committer 有 3927 人(所有新的孵化项目,孵化期间产生的 Committer 都会自动成为 Incubator Committer)。

本文主要分享我最近在孵化器邮件列表上发布的一封倡议,以及近期帮助孵化项目所做的一些工作。

首先放一下倡议原文,可以从 https://lists.apache.org/thread/h7tblpghtyhlsfolt14jnpylj1cygmxf 公开访问和进行回复。

应当帮助孵化项目的倡议

这封倡议主要是面向 IPMC 成员也就是孵化器导师们的。但是,我认为这样的讨论应该公开进行,因为不涉及安全问题或个人评价,而是孵化器的共识和行为准则。

一言以蔽之,我建议在孵化项目积极回应或解决 IPMC 成员提出的问题或挑战之后,IPMC 成员应当给予积极反馈,认可孵化项目做出的努力,并鼓励项目主动了解和践行 ASF 文化和规则。

一个基本的前提是,在 ASF 的社群模型和定义里,所有成员都是志愿者。这意味着他们不是被雇佣参与开源社群贡献和创造的,也没有上下级关系,而是因志同道合聚集在一起的平等的同侪。

平心而论,大部分孵化器导师不是孵化项目的第一作者,也很少参与到孵化项目的开发贡献当中,甚至对项目的历史和当前社群成员组成都不甚了解。在孵化器导师看来,某个孵化项目出现显而易见的问题,大概率存在其历史原因,或是项目团队其实正在想办法解决。

我对隔着网线能造成的误解深有体会,因此总是尝试假设对方是一个正常人而不是一个弱智,做某件事情肯定有其原因而不是故意破坏,只要能把事情说明白总能取得进展而不是对面就是个恶棍。这些听起来都很平常,但是如果你仔细观察网络空间当中的文字交流,你会发现但凡冲突出现且无法收敛的地方,很多是一方或双方都有前面提到的预设立场。

目前孵化器当中由于沟通能力产生的问题并不鲜见。其中直接触动我的,是一个孵化失败的项目退出孵化器时写下的一段话:

The incubator spends more energy on failing us than helping us.

它说:在孵化过程中,孵化器像是在努力阻止我们成功毕业,而不是帮助我们融入到 ASF 的文化和规则当中。

这很像开发者在一开始 Review PR 的时候,总是执着于找到 PR 当中的错处,把理论上协同开发、合作贡献的流程搞成了大家来找茬。IPMC 成员在审查孵化项目的问题时还有一个额外的问题,就是前面提到的 IPMC 成员往往很少参与项目本身的发展,因此甚至从《大教堂与集市》中提到的 Loken 所有权理论出发,都不那么有权利来评判孵化项目的好坏。

诚然,IPMC 成员大多是经验丰富的老 ASF 玩家。我相信并且从看到的情况看也确实是,每个 IPMC 成员提出挑战的时候,其动机都是为了保证孵化项目符合 ASF 的文化和各种政策要求。但是,我也确实看到 IPMC 成员由于种种原因,草率地给出挑战,或是以一种上级对下级的口吻颐指气使。当挑战的内容本身是个误报,甚至从开发者的角度来说就是无厘头的折腾的时候,IPMC 成员和孵化项目之间的摩擦就会急剧上升。在《全票通过?同侪社群无须整齐划一》里我列举过几个具体的例子。

这些提出挑战的 IPMC 成员往往不是被挑战的孵化项目的导师。因此,他们对这个项目而言更是一个志愿者。我非常尊重这些成员有如此多的时间,来检查大量孵化项目可能存在的问题,而且现在的我也能看清楚他们志愿者的身份,心平气和地按照事情本身的是非曲直来沟通。

不过,对于很多孵化项目来说,遇到直觉上应当经验老到的 IPMC 成员的挑战,还是会很有心里压力。尤其是当孵化项目的导师不能及时处理摩擦(项目导师也是志愿者,时常出现不能及时响应的情况),项目成员很容易感受到孵化器在阻止自己成功,而不是帮助自己更好地成长。

因此,我鼓励 IPMC 成员 follow 其他项目已经广泛验证的经验,在孵化项目做出改进时积极给予肯定,如有可能,自己上手解决发现的问题,或者提供解决问题的具体解决办法。这样,孵化项目能够认识到 IPMC 是帮助自己在孵化器中成长,最终成为 ASF 顶级项目,我相信大部分有志于经历 ASF 孵化的项目,都是有动力配合 IPMC 的指导改进的,只要这些说辞站得住脚。

两个典型的例子。

第一个,@sebb 最近狂盯孵化项目下载页面的合规问题,包括发版投票时候邮件的格式和链接是否指向可靠的地址等等。他提出问题都是非常具体可以解决的,而且在项目解决之后往往会感谢孵化项目及时处理。同时,他还维护了用于 ASF 项目管理的 Whimsy 工具,把这些检查和常见的管理操作尽可能自动化发现和自动化处理。这种从根本上改进流程,又针对每个具体问题提出可以操作的解决方案的做法,为他赢得了相当的声誉。

不过 @sebb 上次跟我扯 ASF 网站模板组织形式的时候,因为被 GitHub UI 显示误导,跟我 battle 了一周,最后发现是他看错了,搞得我也心很累。

第二个,我把自己发现的 StreamPark 距离毕业最大的阻碍,文档的正确、合规和流畅等问题,提出来之后,不仅是 StreamPark 社群的活跃成员积极响应开始改进,还有一位身为专业文字工作者的 IPMC 成员 Andrew Wetmore 伸出援手,仔细改进文档不符合英文表述习惯的地方。这无疑为他赢得了项目成员的尊敬。如果他再对项目的内容提出挑战和改进意见,项目成员也更容易接受。

最后这点我想再强调出来,它代表的是一种同情心或同理心。很多 IPMC 成员对自己辅导的项目都是非常宽容的,经常能够找到通融、网开一面的制度支撑,从根本上是了解体谅项目团队遇到的问题跟当前阶段的困难。相反,当审视其他项目时,往往由于缺乏同情心,容易光是挑战项目的各方各面,而不愿意花时间好好沟通。

比如,孵化器主席 Justin Mclean 在过去几个月里就让我几度血压飙升。

在 HoraeDB 进入孵化器评审的时候,因为原创 CeresDB 团队用新名称捐赠核心代码,Justin 就怀疑人家图谋不轨,搞到最后原来是说代码里很多 CeresDB 的文本还没改过来。如果他一开始就说这些遗留的 CeresDB 文本是问题,马上项目团队就可以开始改,然后我作为 champion 把提案改改,误会就说清了。

在 OpenDAL 进行毕业投票的时候,项目成员没有顺着他的意去处理几个很小的品牌问题,而是就合理性做了一些反问,他就从根本上质疑团队在品牌保护问题上的严谨性,拿放大镜挑各种各样的刺(《Apache OpenDAL 毕业随感》)。最后我找到 ASF 品牌办公室的事务官帮忙评估,才收敛到若干个可实操的改进措施。而即使 OpenDAL 项目成员解决了一系列此前没有细究的品牌问题,在 API Docs 上的品牌合规水平秒杀 90% 的 ASF 项目,Justin 还是不依不挠,沿着预设立场认为你人品有问题。这真是没话说。

在 Fury 刚刚进行的上一轮投票,Justin 稍微表现的正常一点,虽然还是出现了以下经典画面,但是我再三追问之下还是把自己使用的检查工具说出来了,一下子项目成员就能自检出……全都是误报。其中有两个误报只要稍微看一眼就知道是误报,但是 Justin 有功夫回邮件 battle 也不乐意去验证报告的成色。这我只能说是傲慢作祟。至于另外两个,作者能不能多次捐赠代码,Java ArrayAsList Wrapper 这种朴素代码有没有抄袭可能,这搞不清楚只能说是人水平不行。

无言以对

应当说,Justin 自认作为孵化器主席,有责任充当守门员来保证毕业项目质量,同时也会在前几天某个毕业项目出现品牌问题时,被其他人追责孵化期间怎么没有发现,而上火着急自我辩解,肯定还是对 ASF 的角色有认同。而且 Justin 乐意花这么多时间检查这些确实重要的合规问题,客观来说是非常不容易的。

但是他的表达、语气和措辞真的很容易产生摩擦。@sebb 挑战项目的次数跟他不相上下,孵化项目体感上完全不同。而且 Justin 真的特别闲,所以过往孵化器里想争论道理的人,也没有那么多时间跟他吵这些说白了就是官僚主义的东西。加上 Justin 是 ASF 董事会成员,而董事会成员只有他全力关注孵化器,可能再加上姜宁老师和 Craig 偶尔看看,就导致 Justin 实质上成为孵化器里几乎唯一强势的声音。

中国人是最务实的,现在国内项目要进孵化器,第一时间就是找 Justin 来当导师,然后你总不能自己输出自己,把这个“难关”直接自己消解掉。对于我来说,现在我发现我之前也落入到情绪主导思考的坑里,只要自己别着急上火,冷静按照事情本身的是非曲直来讨论,无理的意见就忽略掉,不自己把任何一个社群成员特殊化,沟通还是可以进行下去的。

回到项目孵化的话题上。

虽然 ASF 一直强调社群重于代码(Community Over Code),但是我的理解一直都是活跃的社群才能一直演进代码,死代码不会自己向前走,而不是说“社群优于代码”,或者“社群才重要,代码不重要”。

因为 ASF 的组织目标明确写了:

The Apache Software Foundation (ASF) exists to provide software for the public good.

然后才是:

We believe in the power of community over code, known as The Apache Way.

所以 ASF 存在的意义是向公众提供好的开源软件,Community Over Code 是手段。

而在孵化器当中,IPMC 成员和项目导师往往不怎么参与到软件研发本身去,大多是帮助项目了解 ASF 文化和制度,成为社群形式上合格的 ASF 顶级项目。实际上,项目的价值还是由软件解决的问题本身所定义的。如今 ASF 代码仓库最热门的六个项目,都是充分解决了某个领域的问题,而不是因为它们合规。

所以,作为孵化器导师,我个人做得多的是参与到项目发展中去,这样才能真正站在项目成员的立场上看待问题,而不至于缺少关键评判视角。对于每位 IPMC 成员来说,至少应该对努力创造软件价值的人保持同情心或同理心,认识到他们是 ASF 长期存在且保持影响力的重要贡献者。这样,才能基于平等的同侪对话共同创造价值。

如何处理 Good First Issue

我在《GreptimeDB 社群观察报告》当中提过,GreptimeDB 的 good-first-issue 流转速度极快,大部分容易上手的工作往往在一周甚至两三天内就会有人认领,并且完成的情况也还不错。这个体验很难得。

在最近一些 Good First Issue 的流转过程中,我重新发现了一些典型的模式。正好同大家分享一下我对于如何处理 Good First Issue 这个问题的看法。

还是先看几个案例。

第一个案例算是标杆:

example

example-pr

可以看到这里有两个要点。

  1. Issue 上仔细说明了问题是什么,为什么要解决这个问题,相关代码在哪里,可能的实现思路是什么。
  2. Pull Request 里作者积极的提问,并且说明他做了什么改动,有哪些 alternative 是考虑过但是不可行的。
饱和沟通是开源协同的一个秘诀。

这位 contributor 后来也开始参与到其他 issue 的解决中。

第二个案例介绍了一种处理 stale assign 的方法:

stale

注意这里我并没有直接 assign 给这位新出现的开发者,而是在他提交 PR 之后才 assign 给他表示这个 issue 已经有人开始工作。当然,如果他能在 issue 中表达任意有效信息推动 issue 前进,我也会 assign 给他。

第三个案例是我相对担心的:

concern

因为 issue 的描述我读完以后都觉得,可能需要耗费相当时间梳理清楚相关逻辑。这个时候一个没有过任何开源项目 contribution 经验的新人贸然说要开始接手这个工作,且不说明他的计划,也没有任何问题要追问。在我的记忆里,这种 assignee 很容易回头发现搞不定。如果能主动说自己搞不定,unassign 还好,但是更多人是默默离开,一言不发。

于是我追加问了一下他的计划:

concern-follow

嗯……虽然有点抽象,但是至少多产生了一些有用的信息,暂且信一回。

关于这个问题,我有一段相关的论述:

Assign contributor 的时候可以做个背调,或者直接问一下 TA 的实现计划,除非 issue 确实是闭着眼睛都能做的。另一方面,这也跟 good first issue 有没有相关代码链接,有没有写清楚需求有关。比如上面的典范写清楚了,就容易让 contributor 上手做起来。

Contribution 不只是上来写代码,能经过交流把 issue 往前推进一步,对项目都是有帮助的。因此 issue 也不一定每个都要写得那么详细,毕竟大佬可以自己脑补很多内容。只是很多新手,他不知道可以问问题,或者就模糊地问一句“你有什么可以帮我的吗”。

不用害怕劝退潜在的参与者,如果正常交流都不能回答,大概率不能解决问题,或者做出 PR 来很抽象,review 起来想死。当然,我也见过不用太多交流也能做得很好的,那种大佬会一个 PR 拍你脸上,也不会被劝退。

最近比较像的比如这位:

demo-scene

demo-scene-pr

不过无论如何都有可能出现 assignee 出于各种原因无法完成 issue 的情况。从维护者的角度说,兜底可以问一下进度然后 unassign 掉。这个是可以自动化的。我在 GreptimeDB 上记了一个 Good First Issue 来实现这个自动化:

其他相关的话题这里就不展开了,如果各位有兴趣听,可以回复我来讲。

  • 不同文化背景的 Assignee 的倾向;比如这位是比较典型的。
  • Good First Issue 怎么设置比较合适?
  • 企业开源项目如何在 Issue 上与跨越组织边界合作?

对于关注 GreptimeDB 想找机会参与的读者,现在主仓库还有一些未解决的 Good First Issue 可以上手,即使有人 Assign 了,如果过去数周没有什么进展,也可以由你来接手推进。

contribute

如果你不是 Rust 开发者,GreptimeDB 也有各个语言的客户端正在开发。对于 DevOps 开发者,GreptimeDB 的命令行工具 gtctl 正在准备发布第一个版本,我和项目作者都会及时处理上面的信息。对于前端开发者,GreptimeDB 的 Web UI 也是一个独立的开源项目。

总的来说,GreptimeDB 是一个值得参与的开源项目。其软件产品目标是成为一个高效、实惠,且可以处理分析大规模时序数据的云数据库。目前已经可以替换 InfluxDB 和作为 Prometheus API 的存储后端。

GreptimeDB 社群观察报告

GreptimeDB 是格睿科技(Greptime)公司研发的一款开源时序数据库,其源代码在 GitHub 平台公开发布。

https://github.com/greptimeteam/greptimedb

我从 2022 年开始知道有 GreptimeDB 这个项目。2023 年,我注意到他们的 Community Program 是有认真写的,不是无脑复制所谓成功项目的大段规则,于是开始跟相关成员探讨开源治理和社群运营的话题。后来,我读过 GreptimeDB 的源代码,发现他们的工程能力很不错,于是就开始参与贡献

经过这几个月的参与,我对 GreptimeDB 的社群有了初步的了解。我认为,这是一个值得参与的拥有巨大潜力的开源社群。于是写作这份社群观察报告做一个简单介绍和畅想。

GreptimeDB 的社群量化情况

两年前,曾有人半开玩笑地说 Rust 和时序数据库都快成开源世界的一个梗了,因为当时有大量的 Rust 语言写作开源项目和定位在时序数据库的开源项目出现。GreptimeDB 也算其中一员,它同样是用 Rust 语言写成的。

不过两年过去,回过头看能够坚持下来不断发展的项目,GreptimeDB 就是为数不多硕果仅存的一员。哪怕跟主流时序数据库项目社群相比,GreptimeDB 的活力也可圈可点。

上面两张图展示了 2023 年,即 GreptimeDB 开源运营第一年,从 OpenDigger 数据集生成的每月 OpenRank 和活跃指数(Activity)数据折线图。

可以看到,从 OpenRank 的维度看,GreptimeDB 显著超越了近年来发展乏力的 InfluxDB 项目,跟 TimescaleDB 和 Prometheus 不分伯仲,相比起战斗民族出品的商业开源产品 VictoriaMetrics 仍有差距。从项目活跃指数的维度看,GreptimeDB 则与 InfluxDB 之外的主流项目同属第一梯队。

OpenRank 是同济大学赵生宇博士定义的一个开源价值流分析指标。相比于容易受先发优势影响的 Star 数和 DB Engines 分数等指标,上面展示的每月 OpenRank 和 Activity 变化情况更能体现出项目当前的发展情况和未来趋势。

GreptimeDB 的社群运营情况

前面提到,我真正开始关注 GreptimeDB 社群的契机是发现他们的 Community Program 并非船货崇拜,而是明显经过思考,有一定可行性的。事实证明,确实如此。2023 年 GreptimeDB 按照 Community Program 的设计发展了两名公司之外的 Committer 新成员:

这两位 Committer 都是通过代码贡献被提名的,提名前都提交了大约二十个代码补丁,且质量被项目团队所认可。此外,这两位 Committer 从开始参与项目发展到成为 Committer 都经过了若干个月的持续投入。可以看到,这个标准下筛选和认可的两位 Committer 在上个月仍然有活跃参与。

应该说,目前 GreptimeDB 的项目功能已经初具规模,能够达到线上交付的标准。这也意味着开荒阶段的大量初创工作已经完成,新加入的社群成员可以在一个坚实的工程基础上发挥自己的创造力。同时,GreptimeDB 在实现优化上还有很大的进步空间,倒排索引、WAL 和存储引擎等技术方向上还有很多未解决的设计实现问题。现在仍然是参与 GreptimeDB 成为 Committer 的机遇期。

不过,GreptimeDB 的 Community Program 距离成为一个商业开源标杆还有不少可以改进的地方。

例如目前邀请新的 Committer 只在 Biweekly 上简单提及。Community Program 设计的结构上并没有即时体现出它正常运转,以及社群存在公司之外的 Committer 的事实。对于活跃参与者和 Committer 的介绍和成功经验分享,也尚有欠缺。目前社群基本处于给代码写得好的人一份权限的朴素运营阶段。

此外,Community Program 虽然已经相比其他船货崇拜的同行删减了许多内容,以保证它能够务实地运作,但是仍然存在一些空洞的组织结构。例如设计出的 Steering Committee 做技术和社群发展决策,但是实际上当前阶段大部分工作就是公司团队商议决定后公开;例如还是定义了 SIG 乃至 OSPO 的组织,但是根本没有人力填充运营这些机构。

我认为,Community Program 应该继续依托当前社群实际运行的状态,结合期望达成且有能力达成的下一个状态,来做修订。例如,提高成为 Committer 的标准和路径的透明性,积极分享案例和邀请 Committer 说出自己的故事。例如,精简冗余和虚假的组织架构的同时,保留在社群征求意见和决策结果向社群公开的关键动作。例如,强调社群成员参与渠道的多样性,鼓励在不同渠道帮助他人使用 GreptimeDB 和参与贡献。这部分是 Ambassador/Advocate 的核心。

除了这个堪称开拓性探索工作的 Community Program 之外,GreptimeDB 社群还有两件事情让我印象深刻。

第一个是 GreptimeDB 社群积极参与开源之夏这样的务实的开源活动,今年释放的三个挑战项目都实现了不错的开源导师传帮带效果:

第二个是 GreptimeDB 的 good-first-time issue 流转速度极快,大部分容易上手的工作往往在一周甚至两三天内就会有人认领,并且完成的情况也还不错。实际认领实现过程中,只要你能够主动更新进展和提问,项目团队成员大多能及时回复。这个体验还是很难得的。

GreptimeDB 的未来发展期望

前面介绍 GreptimeDB 的时候,提到了开源、Rust、分布式、云原生、时序数据库等关键词。虽然这些 buzzword 确实也是 GreptimeDB 能力或特点的一部分,但是从注重实效的程序员(The Pragmatic Programmer)的角度来说,我们还可以做一个具体的展开。

即使当初市面上已经有“恒河沙数”的时序数据库存在,GreptimeDB 的创始团队还是发现了这些现存解决方案没能处理好的问题。对于时序数据当中重要的三个分类:指标(Metrics)、事件(Events)和日志(Logs),大多数时序数据库都只能最优化其中一到两种分类的存储和访问。

GreptimeDB 的创始团队认为,这三类数据可以共用同一套查询层和对象存储层能力,只需要针对各自的数据特性实现各自的存储引擎即可。其中大部分 DB 的架构和能力,例如数据分片、分布式路由,以及查询、索引和压缩等都可以共享。这样,GreptimeDB 最终能够成为同时提供所有时序数据最优化的存储和访问体验的单一系统。

开源应用监控项目 Apache SkyWalking 自研数据库 BanyanDB 也是基于相似的挑战和思考,不过它作为一个监控项目的子项目,更多是以相当特化的方式在实现。但是这反应了时序数据可以统一存储逐渐成为业内共识,所有的通用主流产品都将朝这个方向发展。

在仰望星辰大海的期许之外,GreptimeDB 也有脚踏实地的挑战。

例如,虽然我前面夸赞 GreptimeDB 的工程化水平不错,工程师做功能扩展和代码重构都能找到一个相对整洁的切面,但是软件工程是一个即使知道了原理和最佳实践,真正做出来还是有相当长的必要劳动时间的领域。在快速原型迭代的过程中,GreptimeDB 对内存和抽象的使用是相对奔放的。随着线上应用逐渐增多,GreptimeDB 团队也能收到用户上报的各种性能问题。这就要求重新关注到在快速开发过程里被刻意忽略的细节,精打细算关键路径上的内存使用,针对性能修改抽象以充分利用机器资源。这部分工作都是细致工作,讲究一个 benchmark 发现性能瓶颈并逐个优化。目前的 GreptimeDB 应该有相当多这样的优化机会。

例如,之所以过去时序数据库没能同时服务前面提到的三种不同数据,除了数据建模上的差异,更主要还是因为在数据量暴涨之后,特定数据类型的特定访问形式的读写性能会骤然降低。目前针对此类问题,业界提出了一系列索引方案进行改良。GreptimeDB 目前正在实现其中倒排索引的方案,也将探索结合倒排索引、基于 Cost 的查询优化器和 MPP 查询引擎的自适应方案。这些工作存在许多参与机会,目前 GreptimeDB 团队成员也有不少精力投入在此。

例如,系统层面数据一致性和性能之间的取舍依赖 WAL 模块的实现。目前,GreptimeDB 仅提供了本地的 RaftEngine 实现和 Kafka Remote 的实现,其中 Kafka Remote 的实现发布还不足三个月。这部分工作现在跟进来,参与到现有实现的完善和优化,以及可能的自研 WAL 设计实现过程当中,对任何数据系统开发者而言都将是一段宝贵的经历。

例如,GreptimeDB 在部署形式上支持云端同构部署,时序数据从设备端到云端都是同一套技术栈在处理。这时,如果 GreptimeDB 能够支持一些高级的分析能力,那么时序数据分析的成本将大大降低,体验也将进一步提高。目前,GreptimeDB 已经支持通过 SQL / PromQL / Python 脚本等形式执行分析,正在设计实现基于 Dataflow 技术的分析功能。分析的需求无穷无尽,这一部分对于熟悉数据分析的开发者来说,是一个很好的切入点。

核心数据库系统代码之外,GreptimeDB 还开源了完整的 Dashboard 方案和多语言客户端。再加上本身 GreptimeDB 就支持 SQL 和 PromQL 等业内通用接口,从 GreptimeDB 与生态集成的角度入手参与到 GreptimeDB 的发展,也是一条不错的道路。就在几天之前,我还看到有位同时使用 EMQX 和 GreptimeDB 的开发者向 GreptimeDB 的 Erlang 客户端提交补丁

软件开发参与之外,Greptime 社群维护的两个重要渠道:GitHub Discussions 主题讨论平台Slack 即时通信工作空间都欢迎任何对 Greptime 开源和商业产品感兴趣的人加入。

GreptimeDB 的商业与可持续

我曾经表达过一个观点:商业化不是开源项目可持续的必要条件。因为许多开源软件是个人开发者兴趣所为,这些个人开发者可以有其他经济收入。即使不基于其创造的开源软件做商业变现,也不影响这些开源项目持续维护和发展。

不过,GreptimeDB 是 Greptime 公司研发的开源软件,而公司要想存续下去,就必须以某种形式取得盈利。Greptime 公司投入了不少资本和人力在 GreptimeDB 的研发上,那么 GreptimeDB 总要为 Greptime 的商业成功创造价值。

Greptime 公司目前有两条商业产品线:

  • GreptimeCloud 提供了全托管的云上时序数据库服务,其内核是 GreptimeDB 系统。这个服务可以免费试用,其 Playground 和 Dashboard 做的技术品味都很好。

Greptime Playgorund

Greptime Dashboard

  • GreptimeAI 是为 AI 应用提供可观测性的服务。不同于其他数据库在赶上 AI 浪潮时采用的 PoweredBy AI 增强自身产品的思路,GreptimeAI 是 For AI 增强 AI 产品的思路。其实本轮语言大模型带动的 AI 浪潮对 Database 服务本身的提升还十分有限,反而是这些 AI 应用自身产生的数据需要 Database 来存储和管理。

这两个产品的底层都是 GreptimeDB 的开源代码提供的核心能力,而云控制面、企业管理、安全集成等功能,则是商业代码实现的。

至于为什么要开源 GreptimeDB 数据库核心代码,而不是干脆全部都是私有的商业代码,前几天 Meta 的财报上介绍的 Llama 开源的理由帮我省去了很多口水:

LLaMa 开源的理由

应用在 GreptimeDB 的情况,在 Greptime 团队决心做这个产品的时候,先发的主流时序数据库已经取得极大的优势,且它们几乎全是开源的。这种情况下,就算 GreptimeDB 存在没有历史包袱的优势,直接朝着正确的方向飞奔,但是软件工程的固有复杂度和必要劳动时间并不能无限减少,所以开源是追赶现有主流产品和赢得用户信赖的必选项。

当然,开源软件允许任何用户免费使用,因此构建商业价值不能直接基于开源软件本身。关于 Greptime 如何设计开源模型,或许我会另写一篇文章做对比介绍。目前而言,其开源模型接近 Databricks 的策略。虽然 GreptimeDB 是从头开始写的,不像 Databricks 直接基于开源的 Apache Spark 构造解决方案,但是其核心功能实现重度复用了已有的开源软件:

  • Apache Arrow DataFusion
  • Apache OpenDAL
  • TiKV RaftEngine
  • Apache Kafka

而且,Greptime 团队对于什么功能应该开源是谨慎的,而不是 by default 开源。只有存在这样一个踌躇推敲的过程,才有可能做商业可持续的战略开源。

DISCLAIMER

在社群参与过程中,我跟 GreptimeDB 的核心社群成员有深入的交流,并于近期加入了 Greptime 团队,因此我的观察和评价可能存在一定的主观误差。欢迎各位留言或私信交流意见。

开发者关系的指标与价值

随着软件行业持续发展,企业构建软件系统的复杂度日益上升,系统不同层次和不同方面的分工日益精细。许多公司不再完全自己生产所有需要的软件,而是转向大量采购技术产品来满足自己的软件需求。

除了核心业务逻辑需要独立实现以外,支持业务逻辑的软件平台和服务都可以甚至应该采购,开发业务逻辑本身也能够藉由采购开发工具和平台来进行加速。前者的例子包括传统商业软件和云服务等,后者的例子有 Copilot 和 Retool 等。

这个潮流当中,开发者已经成为公司购买技术产品决策过程中的重要参与者。他们既影响了技术的发展,也是技术产品的使用者和创造者。于是,开发者经济蓬勃发展,开发者本身成为重要市场客户,企业面向开发者的一系列工作应运而生。这就是 Developer Relationship (DevRel) 即开发者关系发展的背景。

关于开发者关系的定义和详细论述不是本文要涵盖的内容,可以参考我此前的文章《开发者关系简明指南》和《开发者体验的基础设施》,以及 Richard 翻译的《开发者关系:方法与实践》

本文讨论的是开发者关系工作,作为商业公司的一个职位,可以采取的工作成果衡量指标。

虚荣指标

在循序渐进的讨论可行的衡量指标之前,我先介绍一下最常见的错误:虚荣指标。

虚荣指标是《精益创业》提出的概念,指的是反馈表面数据的指标。这些指标往往数据量级很大,看起来效果很好,但是唯独不能告诉企业指标对应的具体价值。

典型的虚荣指标包括点击量和下载量,放在如今开源运动盛行的开发者关系工作上,还有软件代码仓库的 star 数等等。

这些指标共同的实际问题在于信息量太少。例如要做 star 数的指标,我们做过去几年中反复看到,被分配此项任务的运营人员用小礼物在各式活动现场以扫街地推的方式引诱开发者点击 star 按钮。对于单纯的下载量指标,我很清楚自动化流水线会对此产生多大的噪音,以至于使用这一指标的团队完全无法从一个每月下载几万到几十万的数据当中得到任何有用的信息。

信息量太少的原因是行为太简单或者说成本太低。任何一个路人,即使不是开发者,也可能为了小礼物而点击 star 按钮,或许他点完 star 拿了礼物,还会顺手再按一次取消。不加区分的页面点击量和下载量也是如此,除了作为某种谈资,很难指导开发者关系工作的开展。

Star 数这个指标没什么额外的变化空间,唯一能想到的价值是在做广告宣传时跟同类产品做比较,给到一个虚假的直观印象。但是,页面点击和下载行为是可以通过一些精细化的分析来增强的。

针对页面点击行为,简单的有 Google Analytics 分析点击来源的不同地区、不同源网站,分析各个页面的跳入跳出率。复杂一点的有 ReadMe 做的访客全路途分析,甚至集成到 API 页面调用和结果反馈。在数字指标以外,类似 Vercel 和 GitHub 的官方网站尤其是文档,都会添加交互反馈的小组件。这些指标或组件的目的都是优化网站内容的组织呈现,改善用户访问体验。

vercel-docs-widget

github-rest-api-widget

针对下载行为,主要增加信息量的途径是区分下载的来源和目的,尤其是:

  • 具体下载了什么构件?
  • 下载来自什么地区?哪些公司?
  • 手动下载还是自动化流水线下载?

Scarf.sh 试图提供基于下载量的数据分析,不过这个工作还在探索当中,并没有被证明是实际有效的。同时,Scarf.sh 增强下载量数据的方式,需要引导用户通过 Scarf.sh 提供的 Gateway 下载构件,这一点并不容易做到。

实际上,Google Analytics 能够采集到丰富的信息,依靠的是页面访问 URL 中带上 utm 系列参数等。这个方向往下做,总会涉及到需要用户配合提供信息的种种问题,例如用户拒绝提供、信息伪造等等,甚至可能触犯某些地区的信息安全法规。

市场声量

虽然上一节的后半段我讨论了改造“虚荣指标”使得它们产生某种价值的基本方法,但是这些简单行为组成的指标仍然不适合作为开发者关系工作开展的北极星指标。北极星指标也叫唯一关键指标,应当牵引整个开发者关系工作的开展方向。

开发者关系工作含义广泛,某种程度上是目前面向开发者应当开展的工作尚未进入分工而产生的一个笼统的指代。它可以涉及开发者营销、开发者布道、开发者技术推广、开发者技术支持、开发者体验、开发者培训、开发者成功、开发者社群运营和开发者关系项目管理等等。

这其中很大一部分工作跟市场工作相关。例如,公司推出的软件服务或开发工具应当被开发者所认知,应当促进开发者的使用。这就需要有足够多的人谈论公司推出的技术产品,从而引出技术产品的市场声量这一衡量指标。

然而,市场声量指标的一大难点是缺乏统一的定义,如果把它又定义成为单纯的点击量或阅读量,就落入了虚荣指标的陷阱当中,而且容易牵引出变形的运营动作。

最简单的一个市场声量数据就是 Google 指数,但是在如今的自媒体多媒体传媒时代,单纯看 Google 指数很容易掉进坑里,尤其是当项目刚刚起步的时候,很少有开发者是通过 Google 进入到你的范围的。

某些细分领域有成熟的市场声量定义,例如数据库领域的 DB Engines 排行榜。它详细地说明了分数的计算因子,同时提供了细分领域的排名。重要的是,数据库领域内部对比和用户选型时,真的会把 DB Engines 作为一个参考指标。对于一个新兴的数据库软件来说,可以先确定自己所处的细分领域,主要的竞争对手,在多长的时间内要超越哪些对手或者进入到前几名的位置。

其他领域如果没有类似的市场声量指标,可以参考上面提到的计算因子列表自己定制和对比主要竞争对手。这其中我认为最有价值和能够牵引其他工作的渠道,是 Twitter 的提及次数或 HashTag 引用次数。因为这个指标的生成方式对应到了具体人的具体行为,从而具有产生化学反应的可能。这一点在介绍另一个指标模型时会展开。

除了跟竞争对手横向比较,市场声量指标还可以在私域做纵向比较。

目前,市面上有着 OrbitCommonRoom 等社群工具能够收集技术产品相关社群的私域行为数据,拿到数据以后可以自定义市场声量算式并做不同时间点的纵向比较。同样,这些指标背后的每一个行为在这个模型下是可以溯源的,根据聚合指标的变化情况,可以开展影响对应原始行为的开发者关系工作。

最后提醒两点,市场声量衡量的目标,不一定只是技术产品本身。很多时候,市场声量的评估要跟具体的市场营销动作相结合,尤其是跟今年主推的市场概念相关联的声量。进行横向对比时,也不一定是只在竞争对手产品这个粒度上做全域比较,进一步细分领域和平台能够更精确地衡量工作效果。

成员数量

开发者关系工作本身是面向开发者的,其核心工作方式是帮助开发者利用技术产品取得成功,从而实现技术产品自身的成功。这项工作围绕开发者开展,自然也应该是以人为本的。于是,定义好技术产品相关社群后,社群成员数量就是一个很好的北极星指标。

这个指标在几年前就被 Apache SkyWalking 的作者吴晟使用。他当时在不同场合使用 SkyWalking Contributor 的数量来替代 Star 数介绍项目的健康情况和发展情况。

当然,对于商业公司当中的开发者关系工作来说,开源软件代码仓库上的 Contributor 很可能不是工作的重心。这主要是因为公司技术产品的核心代码未必开源,或者开源核心后主要开发工作实际由公司员工负责,在企业软件工程的模型上套一个开源协同的模型,且不说实现起来并不容易,实际上对公司商业成功带来的价值也很难周期性地衡量。

当然,这并不是说商业开源公司都应该完全放弃开源协同的模型。实际上,良好运作的协同模式和生态发展,长期看来是公司产品技术增长和用户增长的巨大杠杆。但是作为开发者关系工作的北极星指标,它并不合适。直白点说,一旦 Code Contributor 数量或参与度成为北极星指标,很容易发生揠苗助长的运营行为,并且很容易跟企业软件工程模型发生摩擦,最后两败俱伤。

商业公司当中的开发者关系工作,作为其北极星指标的社群成员数量,应该是更广阔的相关社群当中的参与者。这里的参考模型仍然可以从 Orbit 和 CommomRoom 这样的社群工具当中获取灵感。

  • Slack
  • Discord
  • Discourse
  • Redit
  • Stack Overflow
  • YouTube
  • Twitter
  • LinkedIn
  • GitHub

当然,这里一定不是说技术产品的社群建设要覆盖上面所有这些渠道。实际上,对于一个刚起步做开发者关系的产品来说,先在少数几个关键渠道上取得突破,再把经验和内容传播到其他渠道上,尤其是配套的扩大开发者关系队伍来应对渠道增加后的工作量,才是一个比较健康的模式。

所谓关键的渠道,首先是即时通讯工具选定一个,论坛看人力选择一个或放弃。Twitter 必须运营,LinkedIn 和 Stack Overflow 看情况覆盖。其他单方面发布内容的渠道,视目标开发者主要获取信息的渠道和团队的内容生产能力决定。

不同类型的渠道有不同的参与者。例如 Slack Workspace 和 Discord 是加入的成员,Discourse 是注册会员,各种社交平台是关注的人和参与讨论创作内容的人。在定义北极星指标时,可以先把一些低成本的行为排除出去,计算一个笼统的社群总体人数。然后再看各个渠道进来的人具体的行为,划定一个基准定义活跃社群成员。

在一刀切的统计全渠道社群成员数量之外,进一步改良北极星指标的方式就是从这些社群成员当中发现真正可以促进公司商业成功的人。这就是下一节要讨论的指标。

DevRel Qualified Leads

我没有为 DevRel Qualified Leads (DQL) 找到一个合适的翻译,直译下来这是“开发者关系认证的潜在客户”,但是不如英文直观地表达它的意思。

DQL 的概念大致是 DevRel Qualified Leads: Repurposing A Common Business Metric To Prove Value 博文提出的。下面讨论的时候我会大量引用这篇文章的内容。

首先说明一点,在技术产品的社群刚起步的时候,实际上社群成员的人数非常少,如果你在这时使用 DQL 指标,就会进入跟使用 Code Contributor 作为指标类似的问题。不是说他们不好,而是在社群起步阶段,这些人可遇不可求。早期能够成为某种“潜在客户”的人,一定有某种特殊性或巧合性,而不是某个策略带来规模化效果的一部分。

直白点说,社群规模小的时候,定这个目标可能完不成,或者为了完成牵强附会。即使在开发者关系工作顺利开展的情况下,往往几个季度也只有个位数 DQL 出现,而且其出现往往很不规律,把它作为北极星指标会打乱开发者关系工作的方向。只有社群初具规模以后,DQL 才能通过一定的内容生产和运营动作来产生。

当然,即使在社群规模小的时候,也可以把 DQL 作为某种补充指标来引导社群人数增长时具体关注哪些人,实行哪些后续动作。

我们先阐明 DevRel Qualified Leads 的含义。

所谓的 Leads 在市场营销领域内是被广泛理解的。在一次市场营销活动中,目标对象往往会被邀请填写某种表单或者登记自己的相关信息。一旦你在这种场合登记了自己的信息,甚至有一些具体的期望或反馈,那么你就是这家公司的市场合格潜在客户。换句话说,市场团队制作了有意思的内容,吸引潜在客户登记信息。然后,他们审核这些信息,确保潜在客户符合公司的标准或期望,然后将这些信息交给销售团队。销售团队会在未来与这些潜在客户联系。市场团队由此完成了他们填充销售流水线的工作。他们不负责确保潜在客户真的成为客户,这是销售团队的责任。

把这个概念移动到 DevRel 工作当中来。

当然,这不是说 DevRel 团队要有销售指标。前面提到过 DevRel 的核心工作方式是促进开发者成功,这是一种真诚的人际关系。如果直接跟销售指标挂钩,那么 DevRel 的工作会一下变得非常混乱难以开展。因为已经有市场营销团队定位在填充销售流水线上,这不是 DevRel 的工作,而将金钱关系直接带入到开发者关系当中是不持久的:某些公司在过去几年中实际上是在期待用几个小礼品换取开发者的技术选型偏好或大量时间投入,这是非常荒唐的。

Leads 这个概念在 DQL 指标模型当中指的是能够以某种方式为公司做出贡献的人,而不一定是潜在客户。

再次回到 DevRel 的核心工作方式定义上来:帮助开发者利用技术产品取得成功,从而实现技术产品自身的成功。商业公司要从这个模式当中获益,需要有人从开发者角度考虑问题,理解什么是开发者成功,达成这个目的需要如何动员公司各个职能部门的力量。而在现有商业公司架构当中,通常没有一个部门会以这种方式考虑问题。

这就是 DQL 引入的契机:DevRel 团队从开发者角度考虑问题,告诉各个职能部门如何与产品社群当中的开发者合作,最终“以某种方式”为公司带来利益。

下面举几个具体的例子。

市场部门

面向市场部门的常见 DQL 是案例或用户创造的内容。

华为云是 Kafka on Pulsar (KoP) 的早期使用者和合作者。KoP 是 StreamNative 推出的一款基于 Apache Pulsar 系统的兼容 Apache Kafka API 的技术产品。在华为云成功使用 KoP 实现自身需求之后,StreamNative 与华为云开发者共同创作并发布了 From Kafka to Pulsar: Creating A Comprehensive Middleware Platform to Power HUAWEI Mobile Services 博文。此文向大量观望 KoP 技术的开发者证明了这项技术确实是可行的,为该产品的市场营销做出了直接的贡献。

类似地,StreamNative 在 FlipkartDiscord 顺利上线之后,也分别推出了专题分享,证明公司的技术产品在电商领域和在线学习技术方向上是可行的(Discord 的场景是泛化的在线学习,而不是核心业务逻辑)。

如果缺少与开发者的深入沟通,这些用户故事很可能沦为一个简单的证言,即“我们用了这个产品挺好的”,但是说不出具体的技术挑战和应用场景。开发者们看到“相互认证”式的市场营销,很难对技术产品本身产生信心。

产品部门

面向产品部门的常见 DQL 是使用反馈和 Beta 测试。

TiDB 的用户论坛 AskTUG 上每天都会有很多用户使用 TiDB 相关产品的反馈。当然很多反馈本身是使用姿势问题,论坛在发展出良好运作的版主制度以后成为一个志愿的 TiDB 支持前线。但是也确实有一些产品反馈是产品本身的设计缺陷或者存在优化空间的地方。TiDB 相关产品大多有从用户论坛上反馈出来的问题。

PingCAP 的社群团队建立起了版主制度,让 AskTUG 成为一个能自服务解决问题的论坛,而不用大幅占用内核研发的时间解答用户问题。这是用户能够提供足够使用反馈的前提。否则一个论坛问问题没人回,那么根本就不会再有用户参与,遑论从中得到有价值的产品反馈。进一步地,该团队可以建立起用户反馈问题的报表,以及确认是产品部门待解决问题的清单,推动用户开发者实际参与到改良产品的流程中来。

当然,PingCAP 的社群团队也不会忘记可以引导开发者参与 Beta 测试,例如 TiDB v7.1.0 荣誉体验官活动就是一个例子。

同样有送礼品的环节,PingCAP 送出的奖品比起换赞的量级所能承受的小礼品要高级许多。与之相对应,作为这个“荣誉体验官”所要做的工作也比点个 Star 要复杂,但是确有指导手册和技术讲解的支持。PingCAP 没有期待参与测试体验的开发者此后就一定会选型 TiDB 到线上环境,也没有期待他们会投入额外的时间做测试指导手册要求以外的工作。因此,奖品和交换而来的产出是相对应的。而这个过程当中参与的开发者总之是花了时间了解 TiDB 等产品,那么从长期主义的角度来说,他们会投入更多时间就是有可能的,同时选型时也至少对 TiDB 有更全面的了解。

tidb-beta-test

研发部门

面向研发部门的常见 DQL 是报告缺陷和修复问题。

如果是开源软件,这个问题可能有 Contributor 会直接解决。否则至少用户能够提供出现问题时的环境配置和复现步骤,这些对于研发部门来说都是重要的输入。

一般而言,软件产品刚开始开发的时候,研发部门的开发者们自己就能够处理外部开发者进来的输入。此时往往也不会有太多的报告缺陷和志愿修复问题的人。但是随着软件产品走向销售轨道,研发部门需要花更多的时间解决客户问题,这种来自于社群开发者的声音就很容易被忽略。而且早期由研发部门经验丰富的开发者亲自指导问题掩盖的信息沟通问题会快速显现,尤其是随着软件变得日益复杂,所有想要报告缺陷和修复问题的人都缺乏相关的知识正确推进事情解决。

DevRel 团队能够改善这个状况。我帮助了来自丹麦的开发者完成 Apache Pulsar C# 客户端的自动发布,完善了 API 文档和发布文档。我帮助了 pulsar-rs 的开发者取得 StreamNative 维护者的代码评审,并最终合并和发布他们提交的补丁。

这个过程当中主要的挑战是 DevRel 团队需要有建立起信赖的研发团队合作者,或者本身就了解一些具体的研发知识。否则只是充当一个传声筒,像个监工一样催促研发部门的开发者“处理”这些输入,而不能说明这些输入具体做了什么,有什么价值,需要他们提供什么帮助,那么他们就会像忽略开发者的邮件提醒一样忽略掉 DevRel 团队传来的同样没有额外附加值的信息。

devrel-notify-challenge

商业拓展

面向商业拓展的常见 DQL 是集成和伙伴关系。

TiDB 的 Flink CDC Connector 维护在 Ververica 的仓库上,StreamNative 维护的 pulsar-spark 集成得到了 Databricks 开发者的参与。这些合作伙伴开发集成的主要动力,是他们的用户希望能够将这两个应用结合起来用。

Airbyte 的成功高度依赖第三方开发者创造的集成。一个 DevRel 团队能够跟集成开发者保持沟通,确保软件提供的扩展点是易于实现的,相关文档是齐全的,甚至有开箱即用的测试套件来支持集成开发。所有这些事情并不都需要 DevRel 团队的成员实施,但是 DevRel 团队能够将这些工作的必要性和优先级说清楚,以推动它们得到公司的投资并最终能够交付,从而帮助集成开发者和依赖集成的应用开发者成功。

airbyte-develop-connector

人才招聘

这个不用多说了。尤其常见于商业开源公司,从开源社群当中接触到的完全懂这一行的开发者,如果他认可这项技术,那么就很有可能会加入公司一起创造。许多有名的商业开源公司都是由此聚集起来最早的一批员工。

潜在客户

回到 Leads 在市场领域当中的含义,DevRel 团队当然也能从开发者关系工作中发现潜在的销售线索。实际上,我工作过的某公司的相当部分订单,就是开始于甲方开发者从某个社群渠道接触到公司的技术产品。

结语

上面介绍了几个可用于开发者关系工作的衡量指标。应该说,目前 DevRel 的工作缺少一个行业范围内通用的单一指标。从事这一工作的人要么过分强调长期主义,以至于表现出来的就是在任意期限内都没有可交付的成果;要么模糊的说 DevRel 的指标要看情况,实际是做了什么就说是什么。这都对 DevRel 工作取得广泛认可和推广产生了阻碍。

上面除了作为反面教材的虚荣指标,我所罗列的也不是某个单一指标。但是这些指标之间是有联系的。从 DevRel 工作的独特价值来说,DevRel Qualified Leads 是可以作为最终的北极星指标的。但是从我实际开展 DevRel 的经验来看,如果社群刚开始建设,为了保证 DQL 准确定义了为公司做出贡献的阈值,使用社群成员作为北极星指标是一个比较好的替代。而市场声量是特定在开发者营销方向上一个能够用于横向比较的指标,如果当前公司的优先事项是提高开发者的认知度,提高公司技术产品和理念的触达,那么可以使用这个指标来作为牵引。

相比较而言,DQL 着重体现了 DevRel 工作者在催化公司各个部门与开发者的连接上能够为公司带来收益的独特价值。DevRel 的工作经常与社群工作联系在一起,因为帮助开发者成功,催化开发者之间、开发者与公司部门之间的链接,本身就是在建立一个开发者社群。

关于这项工作开展的具体细节,除了上面提到的《开发者关系:方法与实践》,还可以参阅 Jono Bacon 的《People Powered》,以及关注 Jono Bacon 的 Youtube 频道。如果有可能,我也会在接下来的时间里详细介绍上述 DevRel Qualified Leads 指标的具体实现方式和其中的细节。

Apache OpenDAL 毕业随感

Apache OpenDAL 简介

Apache OpenDAL 是一个以软件库形式提供的数据访问层。它允许用户通过统一的 API 简单且高效地访问不同存储服务上的数据。你可以把它当作是一个更好的 S3 SDK 实现,也可以通过统一的 OpenDAL API 来简化配置访问不同的数据存储服务的工作(例如 S3 / HDFS / GCS / AliyunOSS 等)。

OpenDAL 以库形式提供,因此使用 OpenDAL 无需部署额外的服务。OpenDAL 的核心代码用 Rust 写成,因此它原生的是一个 Rust 软件库。在项目孵化和成长的过程中,社群也开发出了 Java / Python / Node.js / C 等语言的绑定,以支持在其他语言程序中方便地集成 OpenDAL 的能力。

下图列举了 Apache OpenDAL 多语言实现的线上用户:

real-users

OpenDAL 核心的统一 API 设计,其使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async fn do_business() -> Result<()> {
let mut builder = services::S3::default();
builder.bucket("test");

let op = Operator::new(builder)?
.layer(LoggingLayer::default())
.finish();

// Write Data
op.write("hello.txt", "Hello, World!").await?;
// Read Data
let bytes = op.read("hello.txt").await?;
// Fetch Metadata
let meta = op.stat("hello.txt").await?;
// Delete Data
op.delete("hello.txt").await?;

Ok(())
}

可以看到,实际读写数据的 API 是经过精心设计的。用户想要访问存储在不同服务上的数据,只需修改 Operator 的配置构造,所有实际读写操作的代码都不用改动。

Apache OpenDAL 孵化

OpenDAL 起初是 @Xuanwo 在 DatafuseLabs 为 Databend 项目开发数据访问层时创造的软件库。再往前追溯,Xuanwo 在青云工作时就开发过 BeyondStorage 这一目标相近的软件。不过,由于种种原因,BeyondStorage 最终夭折了

不同于 BeyondStorage 遇到的挫折和经历的歧路,OpenDAL 在一个明确目标的指引下快速成长:

  • 2021 年 12 月,Xuanwo 在 Databend 的代码库中开始开发后来作为 OpenDAL 核心代码的数据访问层逻辑
  • 2021 年 12 月,Xuanwo 同步开始起草 OpenDAL 的定位和目标
  • 2022 年 2 月 14 日情人节,OpenDAL 的核心代码从 Databend 代码库中抽离,开始作为一个独立开源项目运作。

2022 年 8 月,Xuanwo 找到我讨论进入 ASF Incubator 孵化的可能性。彼时项目发展才大半年,几乎所有代码都是 Xuanwo 一个人开发的,也没有 Databend 之外的用户。我发给 Xuanwo 一份 ASF 孵化项目提案的模板并给出项目发展的一些建议,并告诉他如果能在用户增长上做一些工作,主动集成其他知名软件和打造示例场景,过程中发觉合作开发契机招徕开发者,年底应该项目就能成长到进孵化器标准的水平。

今天再次看到 OpenDAL 的提交历史,实际上这个时候 GreptimeDB 应该已经开始调研采用 OpenDAL 的方案。可以看到在 v0.11.0v0.11.4 两个版本的发布里都有 GreptimeDB 的主创人员的参与贡献。

2023 年 1 月,春节前我正好跟 Apache Kvrocks 的成员讨论年后开始准备孵化毕业的事情,想起来之前跟 Xuanwo 交流过 OpenDAL 进入孵化器的意向,于是拉着 Xuanwo 一起起草了孵化提案,并在 2 月初开始孵化讨论

由于项目定位清楚,并且潜在地寄托了替代行将就木的 Apache jclouds 项目的希望,孵化提案顺利地“全票通过”

这段时间的经历可以补充阅读这两篇博文:

接下来的一年,Apache OpenDAL 孵化项目社群高速发展,在功能开发、版本发布和社群成长等各个方面全面取得可观的成绩:

incubation-status

上图所显示的是,孵化期间 OpenDAL 发展了 10 名新 Committer 和 3 名新 PPMC 成员,并有 8 名不同的 Release Manager 发布了 11 个符合 Apache 标准的版本。

上图未显示的是,孵化期间 OpenDAL 发起并实现了 23 个技术提案,处理了超过 1000 个问题报告,合并了 2000 多个代码补丁。

早在 2023 年 8 月,我就判断 OpenDAL 已经接近孵化毕业的标准。2023 年 10 月,在进行过一些沟通之后,项目导师之一吴晟将他发现的毕业前需要完成的工作列了一份清单,正式开始推进毕业工作:

graduation-todos

这份清单上的工作对于 OpenDAL PPMC 来说并不全是简单易懂的,甚至有很多项内容颇有挑战。后面展开讨论 OpenDAL 毕业面临的挑战时,你将看到一些挑战解决起来是很有难度的。于是,在部分简单易懂的工作完成后,清单上的项目有四五周的时间没有任何推进。

2023 年 11 月底,我在准备发起两个新的孵化提案的同时,也决定同时推进 OpenDAL 毕业的工作,免得这些理论上可以一次性完成的工作越拖越久,反而日后需要彻底重新做一遍。

事后,我在跟 Xuanwo 的讨论中得知,这些事务性工作对于开发者来说还是有一定的门槛,看到很多个一眼不知道如何开始的工作项,下意识搁置是第一反应(俗称摆烂)。我作为项目导师,把这些颇具官僚主义色彩的事务性工作用通俗的语言向 PPMC 成员解释,并拆解成一些实际可执行的工作,才能推动项目往毕业的方向前进。

2023 年 12 月,OpenDAL 项目社群内部达成了毕业共识。随后,毕业提案提交到 ASF Incubator 列表上讨论。经过一个月的激烈探讨和继续处理项目存在的问题,2024 年 1 月,项目成功通过毕业投票并在 Board Meeting 上由董事会审核通过:Apache OpenDAL 正式成为 ASF 的顶级项目

PMC Member 的标准

ASF 词汇体系下,PMC Member 即项目管理委员会成员,大致上相当于开源项目的维护者。所有 PMC Member 都是 Committer 并且额外具有对项目发展议题投有效票(binding vote)的权力。

项目在 ASF Incubator 孵化期间也有项目管理委员会,称为 Podling 项目管理委员会(PPMC),其中 Podling 意即孵化中的项目。最初的 PPMC 通常由孵化提案中的 Initial Committers 组成。随后就是上文图中显式的走提名投票流程邀请新的 PPMC 成员。

项目毕业时,毕业提案需要说明最终形成的顶级项目的 PMC 由哪些人组成。通常,原先 PPMC 的成员包括项目导师会加入到顶级项目 PMC 中。此外,孵化阶段邀请的 Committer 也是潜在成为顶级项目 PMC 成员的候选。

万事皆有例外。比如 Apache Doris 毕业时,原先 PPMC 成员部分加入了现在的 StarRocks 公司,并且在孵化期间持续损害 Doris 的品牌。这些成员在毕业时就没有被包含在 PMC 中,甚至不是顶级项目的 Committer 了。

如同我在《Maintainer 的标准》中提到的,我倾向于给予做出贡献的社群成员更高的权限以减少他们参与的门槛,因此在发起 OpenDAL PMC 成员讨论时,我先抛出了一个极端的所有 PPMC 成员和 Committer 都加入 PMC 的提案。

这个提案遭到了 Xuanwo 和其他 PPMC 成员的挑战。他们认为因为“项目毕业”这个契机将 Committers 加入到 PMC 当中,这个说法是行不通的。

随后,项目导师吴晟回应说到 ASF 文化崇尚积极引入 Committers 和项目维护者,Committer 和 PMC Member 在代码权限上是相同的,只是 PMC Member 具有额外关注项目管理的责任,例如处理安全问题、响应 Board 的提问和要求、参与版本发布和投票决议等等。

在后续的讨论中,OpenDAL PPMC 成员表现出把 PMC Member 和 Committer 差别对待,以至于类似等级制度的表述。不过这更多的是一个表达和语境的差异,我在推特上提到

在国内的开源宣传和讨论语境下,确实经常会有一个升级甚至权限交易的 mindset 在。甚至有人就是说开源参与像打游戏一样打怪升级。不同流派和认识存在是很正常的,只是这确实不是 ASF 倡导的方式。

最后,OpenDAL 采取的做法是在包括 PPMC 的成员和项目导师的基础上,询问所有 Committers 参与项目管理成为 PMC Member 的意愿。如果 Committer 都不看邮件列表不回这个邮件,显然跟有意愿参与项目管理事务还是有一定距离的。最终有两位 Committer 回应了轮询,他们也在近期积极参与了项目发展的讨论和组织版本发布。

另一方面,在轮询以外,直到完成毕业流程的两个月间,OpenDAL 也按照以往的流程和标准提名了一位 PPMC 成员。最终毕业成为顶级项目的 Apache OpenDAL 共有 14 名 PMC 成员:初始 4 位,孵化期间提名 3 位,轮询增补 2 位,以及 5 名孵化导师。

从我个人的标准来看,至少愿意花时间做 Release Manager 或是作为项目某个模块的 CODEOWNER 的社群成员,都应该是 PMC 成员。按照这个标准看,PyO3 的核心开发者和 OpenDAL Python 绑定的原始作者 @messense 还不是 PMC 成员,这点应该再 Review 一下。

官方网站和文档

homepage

OpenDAL 的官方网站并不算非常“惊艳”。这一方面是由于核心开发者大多缺乏前端开发技能,另一方面也是作为一个被应用集成的软件库,OpenDAL 不需要独立部署服务,自然也就没有一个独立服务配套的管控页面相关的需求可以展示。大部分情况下,OpenDAL 的使用方式是在软件当中以代码的形式被调用。

在上面的首页信息展示中,可以看到 OpenDAL 主要设计了三个扩展点。

  1. OpenDAL 的核心代码是 Rust 库,但是提供了多语言的绑定,从而可以在诸多语言写成的程序中调用。提供新语言的绑定是一个扩展点。
  2. OpenDAL 的核心价值是屏蔽不同存储服务后端,从而使得用户可以用统一的 API 访问不同位置的数据。提供新的存储后端集成是一个扩展点。
  3. OpenDAL 设计了 Layer 抽象,以在统一 API 的访问链路上提供不同切面的增强,包括重试、日志、监控、超时等功能。

docs

紧接着文档导航页几乎就展示了所有文档内容:OpenDAL 的设计理念,以及跳转到 QuickStart 页面的如何配置四个已经正式发布的语言的软件库。侧边栏的 Services 与其说是文档,不如说是已经支持的部分存储后端的参考手册。

community

相反,关于如何参与 OpenDAL 开发和作为 Committer 或 PMC Member 如何处理事务性工作的文档,由于有实际需要,是相对完整的。

其余页面,博客截至目前只发了四篇,且已经有小半年没有新发布。API 页面除了 Rust 文档,其他语言的 API 文档主要是参考手册性质的。Downloads 和 ASF 相关的页面主要是为了符合 ASF 的要求,对于项目本身基本没什么价值。

项目导师吴晟在毕业自检清单中提到了文档的问题,主要关注的是文档的版本化和避免露出未正式发布的软件库的临时文档。大体上,这是在以 Apache SkyWalking 多语言集成和多模块功能文档的标准给 OpenDAL 提建议。

OpenDAL PMC 成员之一 @suyanhanx 初步完成了文档版本化的调研工作,但是没有彻底完成,也没有更新开发和发布文档以包括相关操作。

我认为,在文档上,OpenDAL 还是有很大的提升空间的。不过在做毕业检查时,我采用了以下的标准:

  • 官网大致能用起来,谈到项目概念时引用链接要齐备。
  • 至少需要让想用 OpenDAL 的人知道如何用起来,整个内容阅读路径是清楚的,就还算可以了。至于版本化的问题,OpenDAL 还没到 1.0 版本,可以先只提供 nightly 版本的文档,这也是目前用户实际的用法。

其实,关于 OpenDAL 的内核设计和各个服务后端的使用方式,Rust 核心实现中已经包括了详细的版本化文档

我认为,OpenDAL 接下来的文档优化方向,除了继续完成所有语言的版本化发布以外,应该注重阐明概念的定义和常见的设计、使用模式,以及不同语言之间的翻译定式。在这一前提下,把实际的文档内容用引用链接导向 Rust 核心实现当中伴随代码的活文档,就可以把存在于 Rust API 文档中的完整文档给利用上。

rust-docs

用户实际的阅读路径,首先从设计、使用模式的文档中确定自己要到 Rust API 文档中查看哪部分模块的具体文档,了解相应的接口契约之后,对应自己使用的语言,查看不同语言直接接口翻译的定式,完成逻辑开发。

如果能做到这个程度,从软件产品角度说,OpenDAL 的产品力才算堪堪能打。

多语言软件库的开发与合规

如前所述,OpenDAL 的一大特色就是在 Rust 核心软件库的基础上,提供了不同语言的绑定,以支持在各种语言写成的程序中利用 OpenDAL 的能力。这也是为什么 OpenDAL 能被寄托替代 Java 写成的 jclouds 库的原因。

目前正式发布的四种语言的 OpenDAL 库的绑定方式如下:

其余开发中的语言绑定包括:

这其中 C Binding 已经有线上用户直接拿去用了,而其他语言的绑定则还未发布或者甚至就只有一个占位符。

在开发多语言绑定的过程中,OpenDAL 总结了一套最佳实践:

  1. 暴力开发出 Hello World 示例;
  2. 重构完成基本的工程化编译和测试流程;
  3. 重构完成基本的 API 映射设计;
  4. 跑通语言对应的发布形式。

这其中最麻烦的其实是工程化的部分和搞清楚最终要怎么发布到目标平台。目前 C Binding 的设计开发已经相对完善,但是由于 C 生态没有一个发布的定式,因此导致了 C Binding 迟迟未能正式发布。

又或者说其实 C 生态就是直接拷贝源文件,所以实际上也已经“发布”。

相反,Rust / Python / Node.js 这样有官方背书的发布平台的语言生态,OpenDAL 可以很轻松的创建对应的 GitHub Actions 工作流来完成自动发布。

值得一提的是,虽然 Java 软件库大多发布到 Maven Central 上,但是 ASF 软件对应的 Repository 不是其他项目常用的 Sonatype 资源库,而是 ASF 自己的资源库。考虑到 Maven 也是一个 ASF 项目,这一点并不奇怪。不过,这就导致支持 OpenDAL Java Binding 自动发布需要 ASF INFRA 介入。OpenDAL Java Binding 是 ASF 第二个支持自动发布的 Java 库,也是第一个自动发布 JNI 原生共享库的 Java 库。相关的工作如下:

此外,OpenDAL PMC 积极与 ASF 的品牌官员合作,探讨在 @apache scope 下发布 OpenDAL NPM 包的方案:

Apache Airflow 的 Jarek Potiuk 正在与 PyPI 团队合作探讨创建一个 ASF 账号的方案。OpenDAL PMC 密切关注进展并随时准备集成 OpenDAL Python Binding 到这个账号下:

可以看到,OpenDAL 认真对待软件发布工作,通过平台提供的机制,以及与 ASF INFRA 密切合作,切实提高了所发布软件包的可靠性。

最后,ASF 在技术合规方面还非常看重所发布的软件的依赖项采用的软件协议是否符合 ASF 对软件协议的政策。OpenDAL 为每个发布的制品都提供了 DEPENDENCIES 文件来披露这一消息。同时,由于大部分其他语言的绑定都是 Rust 核心库的一个翻译层,OpenDAL 开发者们尽可能减少不必要的第三方依赖,以降低下游使用时的合规负担。

技术上,由于需要对接多个存储服务后端,并且存在提供不同语言绑定的愿景,OpenDAL 高度重视代码工程化。

查看 OpenDAL 的 GitHub Actions Workflows 就可以发现,OpenDAL 开发了一套可重用的测试框架,任何新语言绑定或新存储服务后端都能快速具备现有的测试覆盖范围。不过这也不算新奇,同样提供多语言支持和多模块分散开发的 SkyWalking 也研发了适用于自身情况的 SkyWalking Infra E2E 测试框架。

就语言绑定技术而言,Rust 本身支持 C FFI 决定了 C Binding 的实现是非常流畅的。大部分语言也会提供访问 C API 的集成方式,于是通过 C Binding 可以产生其他语言的绑定。这也是 OpenDAL Haskell / Lua / Zig 等一众绑定的实现方式。

在这种大量利用现有技术的方案之外,上面提到的 jni-rs 和 napi-rs 等技术,则是在已有的 C API 集成方式之上,封装了一层符合 Rust 习惯的接口,从而在开发层面只需要涉及 Rust 语言和绑定目标语言。PyO3 更进一步,为这个开发过程研发了一套脚手架,中间打包和配置对接的工作也全部简化了。应该说,这是 Rust 生态主动向绑定目标语言靠拢。底层技术上,两边仍然是基于 C ABI 在通信。

于是这些技术统统可以归类到 FFI 的框架下,跨语言通信的主要成本就产生于数据拷贝和线程模型同步上。可以阅读我的另一篇技术博客《Rust 与 Java 程序的异步接口互操作》了解 OpenDAL 做过的实践。我想 OpenDAL 应该会活跃在 Rust 与其他语言深度集成的前沿。如果生态中有人想要改进 Rust 与某个目标语言的互操作体验,不妨在 OpenDAL 上实践你的想法。

ASF 的政策、官僚主义与基金会发展

上文提到,在毕业讨论进入孵化器邮件列表后,截至顺利毕业前经过了一个月的激烈探讨和继续处理项目存在的问题。

绝大部分毕业前需要处理的问题,其实都包括的项目导师吴晟罗列的清单当中。在处理清单列表的过程中,文档版本问题、依赖合规问题和最终 PMC 人选问题花了一些时间研究讨论,剩下的基本都是按部就班顺利完成。

不过,在清单当中忽略了一个重要问题,那就是 ASF 项目的 PMC 要遵守品牌政策,保护项目品牌和 ASF 的品牌。这其中主要且基础的一条就是以 Apache Xxx 正式名称来引用项目。

OpenDAL 的捐赠没有经历过改名,所以大部分材料和项目核心成员在捐赠过后仍然沿用原来的称呼习惯用 OpenDAL 来指代项目,并认为既然已经捐赠到 ASF 了,那么项目归属 ASF 的事实会随着时间推进被不断强化,因此也没有特别在意。

实际上,ASF 当中明确违反品牌政策的行为主要是 DorisDB 这样直接占用品牌宣传竞争产品,或者在商业公司中用某某项目商业版来称呼自己的产品等等。OpenDAL 虽然脱胎于 DatafuseLabs 公司,但是跟商业化可以说是一点关系也没有。其核心开发者也大多是个人身份参与,所以我认为只要大家没有损害 ASF 品牌的行为也就差不多了。

但是 IPMC Chair Justin Mclean 不这么认为,他在 OpenDAL 毕业提案的讨论里抛出了品牌问题的挑战。

现在回头看,其实一开始 Justin 的表达是 “I found a few minor issues where some name and branding work needs to be done.” 并不十分强烈。但是在 Xuanwo 首次回复没有做到 Justin 期望的完美符合 ASF 政策之后,他表示 PMC 应该要“好好学习相关政策”。

随后,在 PMC 成员完全一头雾水不知道 Justin 所指的到底是什么问题的情况下,项目导师吴晟表达了不同看法,大致跟我上面说的类似,即 OpenDAL 项目成员没有损害 ASF 品牌的动机,实际指出的问题也并不是什么明显的问题,只是没有达到完美主义的标准而已。

这个回复让 Justin 彻底破防,认为 PMC 目无政策,在主观遵守规定的意愿上有严重的缺陷,于是对毕业提案投了 -1 票,并在接下来的一个月时间里充分发挥轴的特性不停地全方位质问。

这个过程给 OpenDAL 项目成员带来了非常不好的体验。不是说不能投 -1 票,而是在主观认定 OpenDAL PMC 不配合、不愿意解决问题之后,连续的挑战都不是奔着具体解决问题去的,而是为了证实 OpenDAL 的项目成员就是一群坏人,且就算 OpenDAL PMC 成员读过品牌政策做过一些 nice to have 的改进以后,得到的也不是认同和进一步改进的建议,而是“你做得还不够好”的持续 -1 批评。

我于是专门写了一篇文章《全票通过?同侪社群无须整齐划一》批评这种行为。实际上,最后 OpenDAL 的毕业提案确实没有“全票通过”

opendal-graduation-result

不过,Justin 本人是 Board Member 之一,哪怕孵化项目在 IPMC 中如上图决策通过,最终是否可以建立顶级项目,还需要 Board 核准。

所以从 Justin 开始挑战,直到最终 Board 一致通过了 OpenDAL 成为顶级项目这一个月间,我跟 ASF 的商标品牌官员 Mark Thomas 以及其他 Board Member 就这个问题进行了全面的讨论。最终我们发现,实际上很多顶级项目都没有严格按照品牌政策来落实自己发布的内容,甚至一些 ASF 基金会层面官方渠道发布的内容,严格按照品牌政策来“审查”,也会有做得不够完美的地方。

不过,这并不是大家可以一起摆烂的原因。相反,这揭示了 ASF 项目在品牌保护上孱弱的现实。我做这些相关内容的讨论,也从来不是为了争个对错,而是带着相关人员重新审视一下目前 ASF 品牌政策的执行情况,从而能够用建设性的目光来评判 OpenDAL PMC 在过去和近一个月来处理品牌政策问题的行为到底做得怎么样。

在 ASF 孵化器里,一般而言顶级项目是不能作为参考的。从务实的角度出发,这是因为很多顶级项目都并不完全合规,也就是这里提到的问题。

但是我仍然坚持应该在孵化器中讨论顶级项目的做法,至少对于做得好的地方应该予以传播和认可。对于做得不好的地方,也不应该局限于孵化器的范畴,而是站在基金会的角度统一协调解决。

这是因为我清楚地知道大多数开源项目进入孵化器,或多或少受到了其他顶级项目的影响,而且进入孵化器明面上的目的就是毕业成为顶级项目。如果顶级项目都在摆烂,都没有按照 ASF 政策行事,孵化项目如何能理解它们被要求符合的政策规定?

探讨的过程中,我们发现了 OpenDAL 和其他顶级项目存在的各种品牌问题,所有已知被发现的问题都被积极解决了。这些工作被总结发送到上面提到的毕业提案结果讨论串上。

Justin 仍然认为 OpenDAL PMC 做得不像他为另一个有志于捐赠到 ASF 孵化的项目做的那样“完美”。但是在我通过对话将他的挑战彻底转化为主观担忧以后,由于我本人问心无愧,说 OpenDAL PMC “不愿意解决问题,只是被动反应,并且习惯性向外甩锅”更是无稽之谈,所以这些挑战在我列举事实的回应之下也就烟消云散了。

在交涉探讨的过程中,我认为有以下几个片段是值得注意的。

第一个是关于孵化器当中指出问题的方式

我用了两组对比。第一个是某个孵化项目对 ASF 执行政策时体现出的官僚主义失望,从而主动退出孵化器时,提到孵化器的维护者们并不是以帮助它们的姿态出现,而是表现得像是要通过项目的失败来证实自己的权威。如前所述,Justin 在 OpenDAL 的案例上最后完全是走向“我对你错”的模式,而不是我如何帮助你一起变得更好。

这未必是 ASF 结构性的问题。对于某个孵化项目来说,主要给予它们这个印象的就是孵化器主席 Justin 本人,有一个“权威”不断地否定你,这种挫败是非常明显的。

第二个是作为一个开源社群,指出问题最好的方式是提交补丁来修复,并在此过程中传达自己的理念,再不济也是提供一个可复现的问题报告,而不是说我认为你有点问题,你要自己发现问题并解决。我用的一个类比,是现在如果有个从未参与过项目开发,也不实际使用项目软件的人,跑过来说我感觉你代码写的方式会出现一些性能问题,你最好自己测一测改过来,这种莫名其妙的报告是无法得到项目维护者的注意的。

这两组对比用英文表达会更加对仗:

  • Helping us rather than failing us
  • Correcting with contributions rather than instructions

第二个是关于政策的文档和实践的问题

ASF 一个做得非常好的地方是它的社群规则和工作方式都有相应的文档记录:

  • 基金会官网 apache.org 记录了基金会的目标,核心定义和各个角色的职责,以及发布、品牌和投票等相关政策。
  • 社群发展网站 community.apache.org 是政策实际执行时的最佳实践参考。
  • 孵化器官网 incubator.apache.org 包括了孵化全过程的指南。
  • 基础设施官网 infra.apache.org 说明了主要资源的位置和使用方式。

但是,这些网站上的内容很是年久失修。

基金会官网的内容东一块西一块,除非很有经验的老成员,否则大多很难快速找到相应的材料。

社群发展网站的最佳实践基本都是十几年前的实践。号称目前开源世界最泛用的成熟度模型,对本次毕业讨论时被挑战的品牌问题只字未提。

孵化器网站内容也是极其杂糅,且有部分“指南”实际是 Justin 个人的偏好,虽然更新时流程上也是经过讨论的,但是大部分审阅的人也很久没有参与孵化,很难对指南落实的时候实际产生的问题有直观的感受。

基础设施官网也是内容稀碎,除非很有经验的老成员,否则大多很难快速找到相应的材料。而且,ASF 在过去二十几年里,基本都在 Maven Central 上发布 Java 代码库,在 SVN 仓库上发布源代码压缩包,对于新时代的不同语言不同软件的发布形式有很大的落差。

Apache OpenDAL 一方面遇上了 Justin 近期跟品牌问题较劲的点子上,另一方面由于它多语言多平台想做自动化发布的工作直接挑战了 ASF INFRA 常年的舒适区,所以在孵化和毕业时相比起其他项目,跟 ASF 的各个机构打交道的次数和时间都要多得多。

这也算是一件好事。毕竟只有新鲜血液的加入,才能促进基金会不断向前发展。只要挑战被正确引导、合作解决,那么遇上问题就不是一件可怕的事情。

第三个是关于基金会本身发展的问题

从另一个角度考虑,为什么 Justin 总是不断地在孵化器当中投 -1 票呢?其实这也反映出孵化器人才梯队建设的问题。由于太多人并不在乎 ASF 政策和 The Apache Way 到底要以什么形式建设出一个什么样的社群和发布什么样的开源软件,所以这些违反政策和文化冲突的问题才会不停挤压到 Justin 这里处理。

久而久之,比起费尽心力去了解项目社群发展的来龙去脉,到底是什么人出于什么动机做了这些事情,人类懒惰地天性就会促使有严格要求的人先直接一个 -1 拍脸,你自己反省。对我来说,我是有足够的动力来处理合规和文化问题的,所以这种处理方式对我来说很冒犯,我会认为原本好好谈合作解决就能行。但是对于某些项目来说,确实你不 -1 我就不管你了,也不是没有这样的案例。

一个更高抽象层次的问题是,ASF 的社群发展和孵化项目的形式,迄今为止仍然是某种“作坊式”的做法。ASF 起源于几个志同道合的开发者聚在一起成立的 Apache Group 并延续了它“一小部分人掌握一系列部落知识运作起一个开源社群”的模式。

在我直接指出作为孵化毕业标准之一的成熟度模型中根本没有关于品牌的论述之前,Justin 宁愿地低效逐个讨论他发现的品牌问题,不断评价 PMC 到底是听话还是不听话,主动还是不主动,也没想到其实这个可以在孵化的必经之路上做提示,最好能做成现在官网基本合规的检查器来提升整个社群的合规水平。

The ASF is well past the point where a small number of folks who have huge “tribal knowledge” can guide the number of projects and podlings that we now have.

我在探讨这些问题的时候,推动和主动修复了一系列文档的缺失,促进了最佳实践的产生和归纳,并且思考到底我们怎么把这些政策、理念和文化传播给更多的人,让他们主动的承担起裂变传播的责任。我想这是 ASF 在走过 25 年之后,面对新的开源社群形势和软件开发方法,应该要考虑和改进的问题。实际上,这也是从参与 ASF 项目接触 Apache 社群理念和方法论的人,成长为基金会成员的一条康庄大道。

全票通过?同侪社群无须整齐划一

近几年,国内开源项目捐赠到 Apache 软件基金会(ASF)的案例很有一些。几乎每个在进入孵化器和从孵化器当中毕业时发通稿的项目,都会选择在标题中加入“全票通过”的字样。

诚然,大部分项目在 ASF 孵化器中茁壮成长,实际上投票结果也是没有反对票,使用这一标题无可非议。然而,对于把同侪社群(Community of Peers)作为社群核心价值之一的 ASF 来说,追求全票通过并不是必须的。

在 ASF 孵化器当中,近些年来由于孵化器主席 Justin Mclean 个人风格的原因,许多项目遭受了无端的审查压力。我认为有必要在国内营造出人人都可以,甚至都应该“全票通过”的氛围时,阐明 ASF 同侪社群的理念和工作方式,以减少项目在面临不合理的挑战时遭受的挫败,尤其是当它来自于某个看起来权威的成员时。

理念与制度支撑

The Apache Way 当中即包括同侪社群的理念:

  • ASF 的参与者是个人,而不是组织。ASF 的扁平结构决定了无论职位如何,角色都是平等的,投票权重相同,贡献是基于志愿的(即使有人因为 Apache 代码的工作而获得报酬)。Apache 社区期望成员之间相互尊重,遵守我们的行为准则。我们欢迎领域专业知识,但不允许有终身仁慈独裁者。

也就是说,ASF 当中所有人在原则上都是平等的,所有的 PMC 成员在投票表决议案时具有相同的权重。

进一步地,ASF 关于投票的专门文档中写到:

  • 对于流程问题的投票,遵循多数原则的常见格式:如果赞成票多于反对票,该问题被认为已通过,而不考虑赞同或反对的具体票数,即反对票不构成否决。
  • 对于代码补丁的投票,反对票构成否决。为了避免否决权被滥用,投票人必须在行使否决权时提供技术理由,没有理由的否决是无效的,没有影响力。
  • 对于版本发布的投票,要通过至少需要三票有效赞同票,且有效赞同票多于有效反对票。反对票不构成否决。

实际操作中,行使技术否决时,如果其他 PMC member 不认同否决者提出的理由,否决也不成立。

因此,全票通过当然是一件值得开心的事情,但是 ASF 的运作方式并不要求需要全票通过。

面对反对意见

我在指导 ASF 孵化项目的过程中遇见过多次反对意见。

先看一个压力没那么大的。StreamPark 的孵化提案在提交表决时,最终是以 8 票有效赞成票,12 票其他赞成票,两票其他反对票通过的。

两票反对票来自 Apache StreamPipes 的项目成员,他们没来由地觉得 StreamPark 跟他们的项目“很像”,所以不应该进入孵化器。

且不说 ASF 并不禁止定位相似的项目进入孵化器,例如复数个消息队列和功能相似的大数据软件,StreamPipes 定位是物联网的工具箱,而 StreamPark 是为流计算系统 Flink 打造的作业管理平台(现在也部分支持管理 Spark 作业)。

所以,这种反对意见,既不是有效票,更没有什么可靠的理由,忽略即可。

再看一个比较搞笑的。Doris 的毕业提案在 2022 年 4 月 27 日以 12 票有效赞成票,13 票其他赞成票“全票通过”。但是孵化器主席 Justin Mclean 在 5 月 15 日找了一下存在感发了一个反对意见

显然,时间已经过去了,而且赞成票远多于反对票,因此 Doris 毕业是既定事实。

面对傲慢的审查

既然是同侪社群,那么允许不同的意见存在就是合理甚至必要的。有人提出反对意见,有人行使投票权投有效反对票,这都是正常的。我在本文开篇所反对的,是通过投反对票带给项目无端压力的傲慢的审查。

上面 Justin 给到 Doris 连续的负面意见,虽然对毕业结果没有影响,但是实际上作为 Doris PMC 整体处理起来的负担并不小。Justin 不停地抛出各种链接,要求 PMC 对此做出解释,其中各种无厘头或者过分的要求层出不穷。

例如,他提到,搜索 Baidu Doris 或者 DorisDB 会出现可能模糊 Apache Doris 品牌的内容,这些内容都需要 Doris PMC 去处理解决。

这根本就是扯淡的。

今天,你主动搜索 Baidu Doris 或者 DorisDB 还是会有各种导向非 Apache 品牌的内容,难道 PMC 整天啥正事儿不干,就陪你做因特网警察?这还是在 Doris PMC 对当时的品牌侵占大户,如今的 StarRocks 有较大影响力,且 Doris PMC 中不少成员受雇投入时间解决这些问题的情况下。

另一个例子来自于几乎全员志愿者的 OpenDAL 项目。

OpenDAL 自进入孵化器以来已近一年,在这段时间里,OpenDAL 提名了 9 位新 Committer 和 3 位新 PPMC 成员,发布了十几个版本,且分别由近十位 Release Manager 主导,不同语言的版本被多个下游软件所依赖。以任何开源社群的标准来看,这都是一个蓬勃发展且做出成绩的项目社群。OpenDAL 的作者 Xuanwo 信任 ASF 的社群发展理念,把 OpenDAL 捐赠到 ASF 当中,其本身就是对 ASF 品牌的认同。

那么好了,在上面链接对应的孵化毕业讨论中,OpenDAL 遭受了怎样的审查呢?

第一次回复,Justin 表示 OpenDAL 的一些引用最好改成 Apache OpenDAL 并带上商标标记,一些第三方的网站提到 OpenDAL 的时候也没有 Apache 的品牌。Xuanwo 看到以后及时的处理,甚至到第三方项目中提交 PR 将 OpenDAL 改成 Apache OpenDAL 的字样。

一般来说,到这里我们就可以认为 OpenDAL PMC 认真对待商标问题,尽力展现 Apache 商标,这已经很足够了。

足够吗?Justin 认为还不够呢。

Justin 进一步提出,按照 ASF 品牌政策的字面意思,所有 OpenDAL 网站的页面,都要用 “Apache OpenDAL” 来指称项目,而且都要带商标名称。最为离谱的是,这个要求连带要执行到 API 文档的每个页面上。

这个真的是保护 ASF 品牌吗?我要打个大大的问号。且不论 OpenDAL 的网站明晃晃的是在 opendal.apache.org 域名下的,根本就没有任何一个 ASF 项目,能够做到在所有网页和材料里都用 Apache ProjectName 指称项目,还要带上商标名称。还是那句话,PMC 整天啥正事儿不干,就陪你搞这些?

说到“任何一个 ASF 项目”,就不得不提 ASF 孵化器讨论里某些人的 360° 立体防御体系。其运作方式如下:

  1. 顶级项目不能作为参考,原因不明反正就是不行。你说某个顶级项目也是如此,他们不会解释为什么顶级项目那么做是有问题的,甚至为什么很多顶级项目都没管这些破事,只会说顶级项目不能作为参考,其回答模式就像低水平 AI 一样。难道孵化器项目毕业,不是为了成为顶级项目?怎么顶级项目反而没这么多破事,到你这就有了?
  2. 其他孵化项目不能作为参考,因为它们反正也没毕业,有问题是正常的。
  3. 基金会以外的项目不能作为参考,因为我们是 ASF 孵化器,别人爱咋咋地。

你发现了吗?这样一套操作下来,一个孵化项目要 argue 自己的做法的时候,不能援引任何其他项目做参考,建设性讨论几乎无法进行。

不能参考其他项目,那怎么界定合理性呢?那就要回到 ASF Policy 及其解释了。

例如,Justin 援引 ASF 品牌政策和自己写的 Incubator Distribution Guideline 说,政策规定项目正式名称是 Apache ProjectName,所以你的 NPM 包名应该是 apache-projectname,PyPI 包名应该是 apache-projectname。下面一众项目发出问号:

哦对了,其他项目不能被引用论述。这下无敌了。

哦,也不一定。比如 Justin 自己要证明说这个包名用 apache- 前缀是合理的时候,他就可以说

This is no different to any project that comes to the ASF via the incubator. Many of them need to change names, often before joining the incubator, and all need to change their name to be in the form “Apache Foo”.

这又可以了。

双标。

当然,没有 ASF Policy 支持,Justin 也可以创造出一些村规来审查你。

例如,Justin 表示 opendal.databend.rs 被重定向到 opendal.apache.org 上,那么 OpenDAL PMC 就要能控制 opendal.databend.rs 这个域名。

哈?所幸 databend.rs 是捐赠 OpenDAL 的企业 DatafuseLabs 控制的,这件事情可能还没那么离谱。换个思路,任何人今天就可以搞定 opendal.vercel.app 重定向到 opendal.apache.org 上,其他服务只要想找肯定能找到,是不是 OpenDAL PMC 还得买下 Vercel 啊?

不过我依稀记得 Justin 自己 mentor 的项目 Answer 也有过 answer.dev 的旧域名吧?这个怎么说呢?

Answer 域名的问题还是我提出来的,我也是 Answer 的导师之一。在这里,Justin 明确说:

redirection would be best

这又可以了。

双标。

再来看另一个莫名其妙的审查。

上面说到要用 Apache OpenDAL™ 来指称项目的事情,OpenDAL PMC 觉得也不无道理,一些显著的引用改改也行的。于是 Python API 文档的首页就用 Apache OpenDAL™ 来指称了:

opendal-python-apidocs

Justin 说这不行,你第一个 opendal 是包名,没有 Apache 字样。所幸我强忍恶心,耐心问了下商标团队的成员这个问题。商标团队的成员是个正常人,曰:“如果工具限制就是这样的,那也没事”。我补了一刀,说你非要说那 PMC 高低得自己做个 API 文档工具来解决合规问题。

当然,只有这个怎么够呢?这首页行了,没说其他页不行啊。pdoc 生成页面是按 Python 模块生成的,Justin 找来一个模块的文档页,指着说:你看,没有 Apache,不行。

opendal-python-apidocs-layer

真要较真,合着以后大家搞网站全别分页了,塞成一个大单页,就像 Kafka 这样

kafka-single-page

合规只要做一次,岂不美哉?哦,Kafka 这个大单页也不符合 Policy 呢。

这种想要做事的人反而莫名其妙多了很多繁文缛节要搞,可不就是官僚主义么?

小结

开源社群存在的首要目的,包括 ASF 自己写的第一愿景,是支持开源开发者生产开源软件。

所谓的政策、指南、规则,其目的应该是保护社群成员免收意外风险侵扰。本身它们是一种非强制性的指引,有道理不遵循也是可以的,更不要说违反了就等同于违法。

我在 ASF 当中得到过很多人的帮助。OpenDAL 作为一个支持多语言的库,为 ASF 在很多发布方面的共识提供了讨论的基础。例如,在我和 Mark Thomas 以及 Drew Foulks 等人的合作下,OpenDAL 搞定了所有 ASF 流程以支持包括 Maven Central 在内多平台自动发布。

Justin 本人愿意花费大量的时间检查孵化项目的发版和提案,我个人对这一点本身是尊敬的。他实际上也指出过很多项目实际存在的合规或品牌问题,而且确实应该被合理的解决,包括上面一开始点出 OpenDAL 的品牌问题,OpenDAL PMC 是有可以改进的地方,也确实改进了。

但是,Justin 把 Policy 苛刻成一种对内进攻项目的武器,用一种非常令人头疼的语气攻击项目,实际上是对 ASF 品牌和孵化器更大的伤害。

此前,这种苛刻又傲慢的审查已经逼退了只有一个核心开发者的 ZipKin 项目:

OpenZipKin 本是监控领域的明星项目,它愿意进入 ASF 并宣传 The Apache Way 是对 ASF 品牌的巨大帮助。然而,在这封令人伤心的退出提案中,ZipKin 的主创 Adrian Cole 无不失望的写到:

Process and policy ambiguity has been ever present and cost us a lot of time and energy.
The incubator spends more energy on failing us than helping us.

这其实是一个早该被提起更高优先级的反馈,ASF 的孵化项目居然感觉到孵化器在促使它们失败而不是帮助它们成功。有了本文前面的介绍,你应该知道这是怎么一回事。

关于流程和政策的争论,我想引用 Rust 作者 Graydon Hoare 的博文 Batten Down Fix Later

这里所谓的不专业或不健康的处理方式,我至少见过四种具体的形式:

  1. 通过花费时间争论消耗对手的精力。

离开阶段常常显得有点突然,因为其原因不透明,并且也有几种不同的形式,通常对应到上述处理的模式:

  1. 由于精疲力竭或倦怠而退出。

毫无疑问,Justin 苛刻且傲慢的审查,就走在这个模式上。

OpenDAL 跟 ZipKin 相似,有一位明确的主创 Xuanwo。如果没有几位导师支持,就像 ZipKin 的 Adrian 一样独自面对这些东西,很难想象如何能够坚持下来。不止一次 OpenDAL PPMC 成员和项目导师对 Justin 的雷人言语表示“麻了”。Justin 本人近五年没怎么正式写过代码也让他的很多“意见”显得非常业余。

例如,要求更改发布平台上 OpenDAL 的 README 包括 Apache 商标,PMC 改完以后说下次发版就会更新。Justin 来了一句能不能不发版就更新 … 你说呢?

当然,如标题所言,ASF 是一个同侪社群,孵化器和基金会并不会因为有一个特别苛刻而傲慢的人就不工作。但是 Justin 是孵化器主席,还是 ASF 董事会九人组的成员,身在基金会中的我即使知道这是同侪社群都会感觉到不可避免的压力,更不用说对此了解较少的其他开发者了。

真要说起来,Justin 的表达真像他自己那样较真的理解,并没有这么大的压力。

例如,在我挑战 NPM 和 PyPI 的包名到底要不要非得用 apache- 前缀后,他改口说 Guideline 都是 SHOULD 不是 MUST 所以有理由的话不用也行。但是又不死心的加了一条临时村规说这个要改也得在毕业提案前,提前跟 IPMC 商量。争论村规毫无意义,但我确实有心情,就说 IPMC 在每次发布的时候都会检查,这些内容都是公开的。毕业提案前,导师组都觉得没问题,怎么你不在导师组里,就得跟 IPMC 商量了?我看你在导师组的项目都不怎么商量啊。

再有一个例子是蚂蚁集团捐赠 CeresDB 核心代码的时候,出于保留商标的商业动机,用新名字 HoraeDB 捐赠核心代码。另一个 ASF 老玩家 Roman 都说这种 Dual Branding 很正常了,Justin 觉得不行。

“Daul branding” is nothing new, but recently, some entities have taken unfair advantage of this (including one you mentioned), and I feel the Incubator should take care that others do not also do this.

诛心言论,死了也证明不了自己只吃一碗粉。我就觉得你未来要 taken unfair advantage of this 了,你说你不是,我觉得你是。

Why a company would be unwilling to give up that brand or trademark just because it may be convenient in the future is a concern.

为什么呢?商业行为,甚至都找不到 ASF Policy 来说这不行了,但我不喜欢,我觉得是个 concern,你就要给我解释。

HoraeDB 的提案,最后我就说不剩什么正经问题了,你的这些意见我都听到了,该说的都说了,我们投票表决。最终 HoraeDB 以 13 票有效赞成票,1 票其他赞成票“全票通过”。Justin 没有投票。蚂蚁集团的运营也好险能继续沿用“全票通过!”的标题。

最后复述一遍,我写这篇文章是为了阐明 ASF 同侪社群的理念和工作方式,以减少项目在面临不合理的挑战时遭受的挫败,尤其是当它来自于某个看起来权威的成员时。

如何撰写一个 ASF 孵化器提案?

随着 Apache 软件基金会(ASF)在国内的深入发展,越来越多的项目希望藉由进入 ASF 孵化器孵化,来建设开源社群。在进入孵化器之前,项目发起人或其核心团队必须撰写一份孵化器提案来介绍项目的基本情况,以供孵化器项目管理委员会(Incubator Project Management Committee, IPMC)评估是否适合孵化。

ASF 孵化器成立于 2002 年,至今已有超过 20 年历史。截至本文写作时,孵化器一共孵化了 347 个项目,其中 241 个项目已毕业,28 个项目正在孵化中。

ASF 孵化器的官网有丰富的文档介绍如何进入孵化器以及按照 The Apache Way 建设开源社群。对于孵化项目而言,最核心的两份文档是:

此外,进入孵化器前需要撰写的提案,孵化器也提供了相应的模板,过往的孵化提案都是公开可查的,这也是撰写孵化提案时的重要参考。ALC Beijing 整理翻译了这份材料,可以阅读其发布文章《ASF 新孵化项目提案指导》

本文从我至今指导孵化五六个项目的经历出发,讨论项目在进入孵化器前撰写提案时经常遇到的问题以及应对方法。

找到一位领路人

提交孵化器提案时,需要一位 IPMC 成员担任该提案的领路人(Champion),其职责主要是作为项目和 ASF 的沟通桥梁,帮助项目进行一些基本的自我评估,并完成孵化提案的评审工作。同样,ALC Beijing 整理翻译了一份材料《Apache 孵化器领路人与导师的职责》做详细介绍。

除了我指导孵化的第一个项目 Kvrocks 是陈亮担任领路人,以及思否捐赠的 Answer 项目是姜宁担任领路人,其他我指导孵化的四个项目都是由我担任这个角色,并帮助项目完成提案撰写和通过孵化器评审。

虽然从流程上说,领路人的职责在项目顺利进入孵化器后就结束,但是许多项目的领路人都会稍后仍然担任项目的导师(Mentor)。毕竟愿意在项目成员还很不了解 ASF 的文化和工作模式的时候,予以指导并帮助项目顺利加入孵化器,需要付出不小的努力。如果不是对项目本身的认同,是很难做这样的投入的。既然如此,那么参与后续孵化,帮助项目进一步建设发展社群,从孵化器中毕业,也就顺理成章。

因此,找到一位领路人不仅是项目进入孵化器时流程上必要的条件,实际上也是找到一位当前在任的 ASF 孵化器导师,愿意支持项目整个孵化过程的发展。

随着 ASF 在国内蓬勃发展,这几年来,进入 ASF 孵化器的源自中国的项目数量可观,我也能看到有许多富有经验的导师和新锐进取的导师在帮助项目孵化。不过,以我现在指导的项目数量来说,我应该很难再支持一个新的项目进入孵化。或许等到现在指导孵化的四五个项目(Kvrocks 已经毕业,OpenDAL 接近毕业)减少到一两个,我还能有多一些时间。国内有作为孵化项目导师意愿的 IPMC 成员我知道一些,但是不合适在这里罗列。如果有想要寻找领路人加入 ASF 孵化的项目,可以跟我联系或者找到 ALC BeijingALC Shenzhen 等组织询问。

第一大原则:真诚

我所指导孵化的六个项目,它们撰写的提案没有第一次就能通过导师自检的。其中最主要的两个问题,一个是英语技能不熟练,另一个就是不够真诚。

原本我想在这写的原则是“诚实”,因为有部分提案编造项目现状尤其是成员多样性和活跃度。这种问题项目导师和后续参与审议的 IPMC 成员稍微查证一下就会暴露,如果在提案正式讨论时才被公开挑战,那么提案通过的概率就非常渺茫了。

不过,“诚实”到底是一个相对客观的标准。很多情况下,项目团队还不至于直接撒谎。然而,如果用“真诚”这样一个相对主观的标准来界定,那么刻意隐瞒、夸大、春秋笔法之类的问题就能囊括其中了。实际评审过程中,不够真诚往往也是很大的挑战。

所谓真诚,意味着明确阐述项目定位,如实披露项目及其社群目前的状况,以及说明加入孵化器的目的和计划。在此之上,知道评议项目提案的人大都是 IPMC 成员,即潜在的孵化器导师,知道 ASF 孵化器的目的是以 The Apache Way 帮助项目发展开源社群,就能够比较好的确定如何写好这个提案。

例如,一个典型的问题是在提案中填充市场材料。这尤其常见于企业团队捐赠的项目,毕竟企业团队在做内部报告和外部宣传时,标配就是一份 PPT 和一系列营销材料。

某项目的孵化提案草案中,几次三番提到捐赠项目的企业建设了中国最大的某品类平台。诚然,首次提及时补充相关信息无可非议,但是原本就不长的的提案里每屏都会出现一到两处提及此事的长难句,属实有点难绷。

某项目的孵化提案草案中,罗列了项目荣获各种开源奖项,希望以此说明项目如何的好。可惜这些开源奖项本身的认可度极低,很多时候是拿着奖项找人领奖。同时其知名度也极低,放在特定环境下或许还有人关注,但是到了 IPMC 成员的审议里,只会认为项目团队是否只是注重虚名。实际上,在提案中需要回答的问题有一条,就是项目需要解释自己的捐赠行为是否只是“对 Apache 品牌的过度迷恋”。

在我的认知中,往往是对社群本身的发展和软件的价值没什么可说的项目,才会期望以填充市场材料的方式来蒙混过关。相反,真正值得展示已有社群规模的项目,通常寥寥数语就能勾勒其形状,IPMC 成员也知道如何求证。

例如,Seata 是阿里巴巴集团和蚂蚁集团共同开发的开源项目,多年以来独立发展得也很好。由于一些原因两家公司希望将 Seata 捐赠到 ASF 当中以在中立的平台和品牌下继续对 Seata 项目做投入。Seata 项目是这样描述其社群的:

Seata is being developed by the development team inside Alibaba who’s responsible for building internal distributed system too. Since Seata was open-sourced on GitHub, it has gained significant traction, receiving up to 24k stars, being forked over 8k times, and having more than 40 versions released. Besides being widely adopted inside Alibaba and Ant Group, Seata is also widely adopted by hundreds of other companies, including … For more information, please click here. We aim to expand the contributor by inviting all those who make valuable contributions and excel in adhering to The Apache Way. The Seata project and its side projects always accept contributions from individuals outside of Alibaba.

因为它确实有人用,所以展示自己的时候才有的放矢。

当然,并非所有项目都像 Seata 一样经过多年的运作。Fury 是蚂蚁集团今年七月份开源的另一个项目,起初项目团队也犯了难,为了让项目看起来勃勃生机万物竞发,把钉钉群的人数也写进去,还把一些未来可能可以做的事情当做已经做完的事情来表述,显得项目似乎得到了广泛的采用。

我首先要求把提及群聊人数的内容给删掉,因为这个大家都知道是怎么回事,背靠大公司或者经过一定的宣传,拉出几个五百人微信群或几千人钉钉群并不是难事,然而其中到底有多少价值,至少不是按群聊人数来衡量的。

此外,我告诉项目团队,Fury 项目的优点是定位清晰,社群用户反馈积极,可以着重突出这一部分。对于未来能做的事情,只要定位说清楚了,再辅以几个确实做过的集成,认真审议的人是能 Get 到的。社群用户反馈积极,则是项目作者杨朝坤在开源的小半年时间里,积极在 Reddit 和 HackerNews 上宣传项目,由于 Fury 作为序列化方案的定位简单易懂,用于替换 Kyro 和 Protobuf 的一些场景下优势明显,所以有不少用户确实公开表示了对项目的兴趣。

最终,我们把 Fury 实际的几个用例做了一点展开来体现项目的价值和其合作的前景:

Currently, Fury has a group of individual users, and organizations such as Alibaba/Vipshop are Fury users, too. Here are some of Fury’s use cases:

  • Ant Ray uses Fury for serialization at 100W+ CPU cores every day at Ant Group.
  • Ant Mars replaced its serialization with Fury, which sped up task scheduling TPS by 2.5X and 4X for data transfer.
  • Ant real-time-graph computing systems replaced Kryo with Fury to speed graph serialization.
  • A few companies replaced Kryo with Fury in Flink jobs for faster data transfer and state persistence.
  • Lindorm at Aliyun uses Fury for serialization between clients and servers.
  • Taobao Android app, which has 880 million monthly active users, is considering using Fury for IPC serialization between Android processes.
  • Vipshop replaced Protobuf with Fury, reducing the end-to-end latency by 30ms.

可以看到,由于开源时间不长,大部分的使用场景仍然在蚂蚁集团内,但这就是实情,而且本身技术场景是多样的。我们没必要编造说因为钉钉群有好几千人,所以或许大概可能这里面对应的几百家企业也在尝试将 Fury 用在生产环境。也许这个逻辑听起来很离谱,但是真的有孵化草案一开始就像这样写的。

这里引出第二个问题,真诚意味着对项目当前的风险如实披露。

例如,Fury 项目开源不久,核心开发者确实只有两个人,而且都是在蚂蚁集团的员工。虽然有过提交的人有几十人,但是显然大家一看 commit 数,时间和内容就清楚怎么回事了。好在前期讨论捐赠的时候,项目引起了 Kvrocks PMC 成员刘明阳(@PragmaTwice)的注意,在提案审议时,项目有三个开发者做出了 non-trivial 的贡献。此外,我为项目找到的另一位导师 PJ Fanning 是 Jackson 的开发者,他也看好 Fury 并改进了 Scala 实现的一些小问题。我也看好 Fury 项目并做了一些微小的改进。最终 Fury 提案是这样表述其开发者团队的:

Currently, Fury has only three core developers, but they are not homogenous: although Chaokun and Weipeng work at the same company, they know each other only due to their common interest in Fury. Mingyang Liu joined the Fury community recently, and he mainly contributed to C++ part of Fury.
We don’t have enough diversity for now. It’s a risk, although we’re optimistic about future developer diversity. Since Fury is open-source, we have attracted more than 20 developers to contribute. We will keep building community diversity following The Apache Way.

在早期商议的时候,其他导师对这一点也提出了担忧。我想到了 OpenDAL 捐赠时也只有 4 个初始成员,且他们都来自 DatafuseLabs 公司,所以我补充了一个评论来自己挑明这个风险和我的判断:

tison’s comment: Although only three initial committers are listed above, PJ (who contributes to Jackson also) and I, as mentors, would participate in the development. Also, another podling that I mentored, named OpenDAL, has four initial committers but so far invited nine (days before its tenth) committers and two PPMC members, done eight (now during its ninth) releases. From my experience with Fury’s initial committers, I saw several shared characteristics with OpenDAL’s members. So, I’d invest efforts to help this project grow within the ASF Incubator.

Hudi 项目到今天也有相当多样的参与者了。然而它起初也只是 Uber 内部的一个方案,在几个合作伙伴间有共享。这就类似蚂蚁集团开发的 Fury 在阿里巴巴集团和唯品会内也有一些采用。Hudi 是这样写关于同质化开发者的风险的:

Currently, the lead developers for Hudi are from Uber. However, we have an active set of early contributors/collaborators from Shopify, DoubleVerify and Vungle, that we hope will increase the diversity going forward. Once again, a primary motivation for incubation is to facilitate this in the Apache way.

这里非常有趣的一句话是,加入 ASF 孵化器的动机之一就是为了发展一个多样的社群。

这体现了撰写孵化器提案的时候另一个常被误解的问题,那就是你并不是来证明说你的项目已经非常好,解决了所有的问题。如果这样,那还要孵化啥呢,直接作为顶级项目运作就好了(这并不是玩笑,实际上已有先例)。或许由于我们总是从一个成功走向另一个成功,我们很少能够坦率的展示项目目前面临的挑战,要么是好的,要么正在往好的方向发展。但是其实 IPMC 并不介意项目有些地方目前还并不跟 ASF 的文化理念和顶级项目的标准对齐,不真诚导致 IPMC 对项目产生信任危机,才是更大的风险。

两个主要段落:现状与风险

我第一次作为孵化项目的领路人,是在 OpenDAL 项目上。当时我误以为孵化提案应该是领路人负责起草的,只是需要项目团队支持补充一些信息。但是,当我实际开始试着写提案的时候,我就发现这个提案的定位就是项目团队的一个自我介绍和自我剖析的。

整个提案大致可以分为两到三个部分:

  1. Abstract 和 Rationale 等前几部分是对项目本身的介绍,项目到底是干嘛的,用在什么场景,相比其他方案有什么差异。这个问题只有核心开发团队才能回答。Initial Goals 是项目捐赠的动机和捐赠后的初步工作,这个也只有核心团队心里才有答案。这部分可以算是简答题。
  2. Current Status 和 Known Risks 也即现状和风险,是几个具体的问题,需要项目披露当前的社群状况和回答一些常见风险的挑战。这部分可以算是问答题。
  3. 此外的内容可以算是填空题,包括源代码的地址,文档的地址,依赖项及其软件协议,捐赠后所需要的资源和初始团队的成员等等。

填空题写起来是比较标准的,简答题除了大规模加塞市场材料突显自己多么 NB 的问题以外通常也不会遇到什么问题,主要出现夸大、掩盖的地方,就是关于现状与风险的问答题。所以我会说它们是一份提案的主要段落。

现状披露分为以下几个方面:

  • Meritocracy
  • Community
  • Core Developers
  • Alignment

Alignment 比较简单,就是说一下跟其他 ASF 项目的联系,没有就不说了,通常没什么问题。是的,提案模板只是一个模板,如果没什么好说的,可以不写。

主要出问题的就是前三项,为了证明社群不只是捐赠方一家在参与,有很多人在用,草案里 Community 的 Core Developers 里凑数夸大的情况非常严重。关于 Meritocracy 就更整蛊了,很多时候项目开源不久或者就是一两家公司的员工在维护,雇佣关系决定提交权限,非要编出多么的开放,甚至为此把一些新来的 Contributor 临时提成 Committer 指着说这就是 Meritocracy 了。

实际上 Meritocracy 并不是多样性的意思,而是任人唯贤,对比的是仅由雇佣关系授予和回收权限的方式。把一个没做什么贡献的人提名成 Committer 反而是违反这一条的。

对 Meritocracy 的回答,其实只要表示出理解它是什么含义就行。当然如果确实已经按照这样在运行了,举出例子是最好的。

风险评估分为以下几个问题:

  • Project Name
  • Orphaned Products
  • Inexperience with Open Source
  • Length of Incubation
  • Homogenous Developers
  • Reliance on Salaried Developers
  • Relationships with Other Apache Products
  • A Excessive Fascination with the Apache Brand

如前所述,下意识的规避项目存在的问题,是不真诚的主要诱因。

Project Name 通常没什么问题,只要做好自检就行。如果要改名或者发现跟其他项目名称冲突,则最好在提案前就完成改名,避免提案对名称产生疑问。Ignite 和 Seatunnel 在捐赠前都改过名字,HoraeDB 也有相似的经历。这里如果没有说清楚改名的缘由,IPMC 评估时发现存在品牌问题,是会有比较大的挑战的。品牌和合规问题是 ASF 当中的红线。

展开说一下,Seatunnel 的原名 Waterdrop 已经被其他组织注册,为了避免品牌问题不得不改名。Ignite 原本是 GridGain 公司的一个项目,其名称 GridGain In-Memory Computing Platform 跟公司名相同,同时作为独立项目也过长。因此实际上 GridGain 可以认为是把 GridGain In-Memory Computing Platform 的核心代码,以 Ignite 的名字捐赠出来。

HoraeDB 情况和 Ignite 类型,是蚂蚁集团的 CeresDB 产品核心代码,用 HoraeDB 的名字捐赠给 ASF 孵化器。然而,由于技术上需要修改代码中对 ceres 文本做替换,我在指导项目撰写提案的时候使用了 rename 一次来表达这个操作。实际评议提案时,技术上的名称替换做了一小部分,rename 的用词导致某些 IPMC 成员对 CeresDB 和 HoraeDB 的关系产生了误会,由此引发了不必要的一系列争论。

Orphaned Products 主要可能的风险有两种,一个是个人开发者捐赠的项目,如果人数很少,一旦开发者转移兴趣,项目可能就死了。另一个是公司捐赠的项目,如果没有 solid 的用例,可能公司一转向,项目也就死了。

如果项目开发者足够多样,有很多用户从中获益,这自然没问题。但是如果人并不多,IPMC 就需要评估项目的定位和未来发展的可能性。例如 Fury 未来长出一个社群的风险并不高,替换逻辑也清晰,不会因为项目没有用而废弃。相反的例子是 Tuweni 作为 Java 作成的区块链工具,虽然也进入了孵化,但是很快就因为没什么人感兴趣,作者自己也觉得没指望而放弃。

如果是公司项目,很多时候公司项目总要打包票说未来可期大大的投入,所以在 IPMC 这关通常也不好挑战。但是对于导师们来说,就要评估这个项目到底有没有足够的高层支持和内外用例。因为公司改主意经常比个人开发者转移兴趣或 burnout 来的快,除非有什么外力牵引,否则项目组换方向或者原地解散,这项目就死定了。对于导师来说,投入帮助项目孵化和成长的努力也就打了水漂。

类似的例子比如 Hotonworks 的 Ambari 项目,在公司被 Cloudera 收购后,项目跟 CDH 冲突,也就停摆了。但是换个角度看,由于 Ambari 仍然有其价值,所以在归档一年后,又有人出来复活这个项目继续运作。可惜的是由于一些其他原因,这次复活后的运作也不顺利。

另一个例子是同样来自 Hotonworks 的 Livy 项目,由于类似的原因停摆。然而在其从孵化器中退休前,有的用户已经上船并在生产环境使用,所以他们自告奋勇来维护这个项目,至少保持能够修复 BUG 和安全漏洞的情况。

Inexperience with Open Source 这个也是经常容易不真诚的地方。

从我的经验来看,大部分想要进入孵化器的项目,往往在社群建设上还有很大的欠缺。如果一个项目已经建立起了完善的社群,例如 ApolloConfig 和 Nacos 这样的,它们很少会想到要捐赠给 ASF 谋求进一步发展。因此,如实披露项目成员在开源方面的经验即可。

反面例子是一些草案在披露是添油加醋故意夸大,说是有某个成员四五年前在某个知名不知名的开源项目里提交过补丁,这就算是不会 Inexperience with Open Source 了。这就像是在建立上放一个 fix typo 的 PR 链接,然后说自己深入参与开源项目开发一样。

Length of Incubation 标准答案是 2 个月进入孵化器,2 年内孵化毕业:

Expect to enter incubation in two months and graduate in about two years.

这个在提案时基本不会被挑战,不过背后是孵化器运作的部分逻辑,即孵化项目是要奔着毕业去的,如果长时间不能毕业,可能会面临被要求退休的风险。历史上退休的孵化项目一共有 78 个,除了项目自己放弃以外,IPMC 会定期评估孵化时间过长的项目,并需要项目对风险做出回应。如果没有回应或确实状况很不健康,可能由 IPMC 出面将其退休。

Homogenous Developers 同质化开发者,即项目核心团队的多样性。这个在前面讨论真诚的时候已经介绍过很多。人的兴趣会转移,精力会消磨,公司的注意力转移得更快。如果项目本身不能凭借自己的价值吸引到足够多样的开发者,那么其失败的风险是极高的。

Reliance on Salaried Developers 这点跟同质化开发者有相似之处,不过其实并不一定是坏事。Flink 的开发如果不是拿钱,一个纯粹的志愿者是很难撑起社群的需要的。所以关于这个问题,我认为要么是不依赖,表达出项目核心团队对技术本身的追求和认同,要么是确实就是一直有钱雇佣开发者做这个项目,都没有问题。同样这里容易产生一些不真诚的地方,明明就是一群拿钱办事的人,编造出自己没钱也会做的谎言就显得很可笑。

HoraeDB 的提案里并没有回避这个问题:

We acknowledge that most developers are supported by their employers to contribute to HoraeDB, which poses a significant risk. However, HoraeDB has already been extensively deployed within Ant Group, with no internal forked versions. The version available on GitHub is the actual production version used in practice. As a result, Ant Group can ensure long-term commitment. We believe that within this timeframe, we can attract more maintainers and developers from diverse backgrounds to address this risk.

作为对比,Fury 的作者独立开发这个项目两年多,他有底气说自己就是会做这项技术,而不只是因为被雇佣做这件事:

Although Fury is created at work time in Ant Group, Chaokun and Weipeng contribute to Fury in their spare time. They love the process of building such a versatile framework and the value it brings to all users and organizations. They will continue to work on Fury even if they leave their current cooperation, and Mingyang Liu also contributes to Fury in his spare time. We plan to attract more committers to address this risk.

Relationships with Other Apache Products 如实回复即可。

A Excessive Fascination with the Apache Brand 这个问题是整个提案模板里最容易被误会的一条,反应了我一开始说的撰写提案时最主要的两个问题,第一个是英语技能不熟练。

这个问题的意思是,项目是否只是“对 Apache 品牌的过度迷恋”而捐赠,而不是孵化器关注的按照 The Apache Way 建设社群。换句话说,是不是只想借 Apache 的品牌做营销。不少草案写作时不知为何理解成要表达对 Apache 品牌的认可,洋洋洒洒写了一堆说 Apache 品牌是如何如何的好,完全是背道而驰。

当然,你说想要捐赠到 ASF 的项目团队不认同 Apache 品牌,这也不可能。可以参考 Kvrocks 提案的写法:

Although we expect that the Apache brand may help attract more contributors, our interest in starting this project is based on the factors mentioned in the fundamentals section. We are interested in joining ASF to increase our connections in the open-source world. Based on extensive collaboration, it is possible to build a community of developers and committers that live longer than the founder.

其他琐碎的问题

第一个,前面已经说过,这里再提出来强调,孵化器提供的模板只是个模板,不一定要完全按照它的格式来写,不知道写啥的可以留空或者询问领路人。

第二个,填空题的标准答案可以看 Fury 提案。一般来说需要三个邮件列表:

  • private 用于讨论不适合公开的内容,例如安全问题的处理,涉及具体人的讨论,例如提名新 Committer 或某人行为不端等。
  • dev 用于项目开发的讨论。以往还会有一个 users 来回答用户问题,但是现在大部分用户习惯开 Issue 或者 Discussion 聊,除非有明确需求,否则复用 dev 已经足够。
  • commits 用于归档项目开发产生的信息。这个主要是 The Apache Way 约定所有开发活动都要在邮件列表上留痕,ASF 会维护邮件服务器和保存所有邮件,这保证 ASF 项目的所有信息资产不会依赖外部系统。

Git 仓库现在基本都是 GitHub 上的迁移,不过 ASF 实际上有自己的 GitBox 做同步来确保不依赖外部系统。

Issue Tracker 大部分新项目都用了 GitHub 的 Issue 功能,老项目很多还使用 ASF 自建的 JIRA 平台。同样这些活动会被同步到邮件列表上,确保信息保存不依赖外部系统。

Initial Committers 的人选要仔细,这个也是经常被挑战的问题之一。在这个问题上,倒是可以多考虑 Meritocracy 的问题,同时不要设置过高的门槛但也不要为了凑人数而把没有什么实质性参与的 Contributor 强行提上来。最后,小心不要把没有参加项目实际工作的公司老板安排进来,这个如果被公开挑战会很难回应,因为跟 ASF 的文化完全格格不入。

Sponsoring Entity 捐赠给孵化器就写孵化器,Sponsors 需要注意领路人(Champion)只负责项目进入孵化器的过程,所以如果他想要继续担任项目导师,在导师一栏里要重复提名。

最后一个,依赖项的协议要认真看,从别处抄来的源代码要保留原先的文件协议头信息。合规和品牌问题是 ASF 的红线,IPMC 里有些人会特别仔细的看这些问题的。关于 ASF 项目如何看待依赖项协议是否合规,可以参考这份材料

注意有不符合 ASF 合规要求的依赖,只要写清楚了而且有后续修复方案,并不需要在进入孵化器前就一定解决。ASF 对孵化项目的 endorsement 其实是有限的,它会要求孵化项目发布时必须带 incuabting 字样和带有一份 DISCLAIMER 文件,来表示孵化项目并不一定完全符合 ASF 承诺的软件标准。

参考案例

再谈 Rust 项目社群治理

上一篇文章《Rust 社群何以走到今天?》发布之后,得益于 Rust 社群的繁荣和成员的分享惯例,我收到了不少关于 Rust 现状的评论。

大部分评论集中在 Rust 项目社群的治理上,即 BDFL 和基金会会对 Rust 项目社群产生什么影响,以及 Rust 项目社群是否需要 Leadership 组织。

我在上一篇文章中引用了 Graydon Hoare 和 @withoutboats 等人的博文,展示了“遗老”们对 Rust 项目社群目前的一些看法,这不免让人理解成赞同他们的观点,希望 Rust 社群出现一个强有力的领导人,甚至希望 Rust 语言像 Graydon 设计的那样演进。

其实,上一篇文章主要想说明的只有两个观点:

  1. Rust 项目社群治理的核心特点是没有一个人(BDFL)或一个一致行动的团体(Apache Group)说了算。
  2. Rust 项目社群未来的发展会极大受到各大公司组成的基金会的影响。

我并不认为有 BDFL 就一定是好事或是必须,至少我所在的每个 Apache 项目社群中都不存在 BDFL 角色,它们也发展得很好。

而且,以 Rust 项目社群如今的状况而言,几乎不可能出现一个新的 BDFL 或者 Graydon “复辟”的情况。因为拥有《大教堂与集市》中提到的,由“开垦”新项目带来的自然所有权的人,绝大多数已经长期淡出 Rust 项目社群,其他少数几个人也无意于社群管理。对于新人来说,由于没有明确地所有权转让程序,想要赢得 BDFL 的地位,就需要依靠自己的贡献。而以 Rust 项目社群目前的领域范畴来说,这对个人几乎是一个不可能完成的任务。所以,Rust 没有 BDFL 将会是一个长期的现状。

进一步地,作为 Rust 项目最初的赞助者,Mozilla 甚至不在 Rust 基金会的白金或黄金赞助商名单上。这就意味着在公司影响力上,哪怕是 Rust 基金会的创始会员也没有天生的主导权。

从机制上来说,Rust 基金会目前并不参与 Rust 项目的开发,基金会的目的是提供 Rust 项目存续的资金支持,以及相应的人才培养和品牌宣传。

上一篇文章中提到的 Leadership Council 并不隶属于基金会,而是 Rust 项目社群的一个治理机构,其成员由各个 Rust 项目顶级团队组成,基本职责是协调跨团队合作以及针对 Rust 项目社群治理本身的一些问题。

在这之前,Rust 项目社群在基金会和 Leadership Council 的领域内发生过几起争议事件:

  1. Ashley Williams 曾经是 Rust Core Team 的成员,同时是 Rust 基金会筹建时的执行董事,但是后来前后从这两个组织中出局
  2. Rust Moderation Team 因为跟 Core Team 的冲突,以集体辞职表达不满。如今,Core Team 本身被解散,而 Moderation Team 则由两位深度参与 Rust 核心开发的成员支撑。

Rust 社群的这些故事,除去缺少 Linus 这样的 BDFL 维持项目本身的有序运转之外,跟 Rust 社群总是期待一个两全的解法,以及过度期望所谓“专业人士”来解决问题的惯例是有关系的。

前者在 @withoutboats 的博文中有所提及:

我担心 Rust 项目从这个经验中得出了错误的教训。正如 Graydon 在这篇文章中提到的,Rust 开发团队坚持只要有足够的构思和头脑风暴,就能找到每个争议的双赢解决方案,而不是接受有时必须做出艰难决策的事实。Rust 开发团队提出的解决这种无休止的争议所带来的 burn out 的方式,是转向内部讨论。设计决策现在主要以 Zulip 线程和 HackMD 文档等未索引的格式进行记录。在公开表达设计方面,主要是在相关开发者的个人博客上发布。如果你是一个开发团队以外的人,那么你几乎不可能理解 Rust 开发团队认为什么是优先事项,以及这些事项的当前状态如何。

而后者则是一种社会高度分工以后常见的认识误区:其他人比我更专业,只要相信他们就好了;有其他人承担这项分工,我只要给予信任或者进一步给予金钱等利益,就可以完全把这些工作委托出去。

实际上,其他人未必有身临其境的人了解事情的原委沿革,不一定站在项目的立场考虑问题,甚至未必专业。对项目负责的人只能是深度参与的核心成员自己,这也是 Leadership Council 在成立动机中所提到的:

根据核心团队(Core Team)的经验,发现问题和开展实际工作本身,对于一个团队来说都太过繁重。这导致了不理想的结果,在某些情况下,还导致了倦怠。虽然有少量工作需要立即采取紧急行动,但绝大多数工作都需要由专门的管理机构进行跟踪和处理。

此前的 Core Team 和 Moderation Team 不仅相互之间缺乏沟通,甚至本身成员也相对独立于其他顶级团队,这导致本应作为协同社群反馈和各个顶级团队之间合作的两个独立团队,跟其他顶级团队之间也可能缺乏互信基础和合作机制。新的 Leadership Council 成立并以九个顶级团队代表为成员,期望在维持先前 Core Team 所要解决的问题的基础上,提高团队之间的协同效率。

对于基金会,可以明显的看到在机制上它相当于 Linux Foundation 之于 Linux 或是 Python Foundation 之于 Python 的形式,前两者在运行过程中间并未出现如同 Rust 社群这样的戏剧性发展。其原因部分在于它们有一锤定音的 BDFL 主导技术开发和项目团队合作,另一方面也是在这样长期的运作方式下,项目团队成员发展出一套自己解决问题的办法,而不是被不满意的现状困扰,转而祈求外部力量奇迹般的解决问题。

Rust Foundation 的性质是 501(c)6 行业联盟,这跟 Linux Foundation 是一致的,它们存在的目的除了写下来的维持 Rust 的存续发展,从性质上说是要为商业联盟中的成员企业服务的。相反,Python Foundation 和 Perl Foundation 的性质是与 Apache Software Foundation 相同的 501(c)3 慈善组织,纯粹是为项目本身的发展和公众利益服务。与此相匹配地,就是慈善机构型基金会往往在募集资金上不如商业联盟型基金会。

如前所述,由于 Mozilla 无法或不愿入局,如今 Rust Foundation 的四个创始会员分别是 AWS、谷歌、华为和微软。它们并没有像 Oracle 在 JCP 执行委员会中那样继承自 Sum 并再次发展得到的垄断权,甚至 Rust Foundation 本身也没有 JCP 那样直接指导语言项目技术发展方向的权限。但是,既然 Core Team 可以改组,Rust 社群整体对于基金会干涉项目发展存有期望,那么在未来社群治理的发展或者至少每次讨论当中,基金会就有可能发挥出自己的影响力。

当然,现在的主要问题恐怕不是担心基金会如何“窃取” Rust 项目的领导权,而是期望基金会的创始会员和其他赞助商先加大投入,哪怕就是奔着主导权来的,至少先把 Rust 的产业应用和语言发展,投入资源搞起来。但这是另一个话题了。

在基金会之外的另一个角度,也有人在呼唤唯一有可能成为 BDFL 的 Graydon 加入战局“拯救” Rust 项目社群。如前所述,由于 Graydon 本人已经淡出 Rust 社群很久,这实际上是几乎不可能的。

Graydon 本人对此的认识倒是比较清晰,他先后写下两篇博文对此做出回应:

其中第二篇已经在我的上一篇文章里展开介绍过,下面我会翻译第一篇。

译文之前说一点题外话。

Graydon 在第二篇博文里提出的对 Rust 的期待,在我上一篇文章发布后收到了推特上网友的讽刺。从 Graydon 本人列举出来的设计方向看,除了错误处理他更喜欢 Swift 的做法,其他的诸如绿色线程、语言级别集成的容器和迭代器、结构化类型等等,确实都是 Golang 的招牌特性。可以说,Graydon “在位”的话,Rust 恐怕就会发展成弱化的 Golang 而不是今天的 Rust 了。

另外,虽然试图“拥立” Graydon 重回 Rust 项目社群的呼声是不理智的,但是这些呼声的存在,侧面印证了前文提到的 Rust 项目社群总是期待两全解法,并总是寄希望于未知的专业人士神奇的解决问题的惯例。

Rust 项目如何走出当前的境况,我参与得不多无法给出意见。不过,我倒是乐意从开源社群发展的研究角度,来分析 Graydon 这样在卸任之后还持续思考和发生的例子:这确实是不可多得的考察对象。

在不能持续维护项目的作者里,Graydon 的案例非常有参考性。他思考了很多,也分享了很多。但是他没有“成功”。这比那些被“成功”掩盖了问题的例子,还有简单一句 burn out 解释所有问题的例子要有意思的多。

以下 Batten Down Fix Later 译文,“我”指的是 Graydon Hoare 本人。

在社交网站上,有人问我:“你是否希望自己是 Rust 项目的 BDFL?如果 Rust 项目有一个 BDFL,现在这些戏剧性事件会不会少一些?”

这是一个很难回答的问题。如果只看它表面的提问,我的答案是“不希望”和“不会”。但我认为它还间接地问了另一个问题,即 Rust 项目社群治理中是否存在某种原罪,或是致命缺陷,例如缺少 BDFL 的角色,而这正是其频繁发生内部政治问题的罪魁祸首?

关于这个问题,我可以分享一些我的观点,希望能简明扼要,或者至少具体一点。我认为有几个根本原因和一个主要的模式。

主要模式

我已经有十年没有参与这个项目了,所以我认为我说的任何话都需要谨慎对待。但我确实时不时地关注 Rust 项目的进展:这些年来,有不少人离开了这个项目,而且其中许多人的离开是不愉快的。很多人对参与这个项目感到后悔和怨恨。这令人悲伤。

我认为这些事情通常是这样发展的:

  1. 项目内部存在冲突。
  2. 这个冲突没有得到很好的管理或解决,至少没有以专业或健康的方式处理。
  3. 冲突并没有消失;相反,它以某种不专业或不健康的方式被表现出来:有时是一次性爆发,但更常见的是长期存在。
  4. 可能在经历了第三阶段的一段时间后,冲突的一方就离开了项目,而所有人都试图假装冲突从未发生过。

这里所谓的不专业或不健康的处理方式,我至少见过四种具体的形式:

  1. 利用非常规的力量:压力、社交影响力、小圈子。
  2. 利用外部正式权力:找某人的上司。
  3. 利用内部正式权力:与管理员进行交涉。
  4. 通过花费时间争论消耗对手的精力。

离开阶段常常显得有点突然,因为其原因不透明,并且也有几种不同的形式,通常对应到上述处理的模式:

  1. 因厌恶甚至为表抗议而辞职。
  2. 上司处理或解雇。
  3. 管理员处理或封禁。
  4. 由于精疲力竭或倦怠而退出。

理解模式

所有这些阶段都有可以理解的缘由。我不是说它们是“好”的,但可以理解。我理解它们是如何发生的,甚至可能对形成这些模式起过推波助澜的作用。

以下是一些背景事实,有助于理解为什么会出现上面这些模式:

第一,很多人只是习惯性地避免冲突。这可能是由于成长背景、个人创伤经历、固有的天性或其他原因。我自己就是避免冲突的人!应对冲突是困难且疲惫的,尤其对于没有报酬的志愿者来说。避免冲突通常看起来更容易。冲突回避是一种为了当下舒服一点,将问题推迟至未来应对的方式。

第二,Rust 如今形成了一种非常高风险的品牌。它似乎非常不可思议,经常吸引大量的互联网关注。它一度似乎被普遍崇拜,又被普遍憎恨。它感觉随时可能失败,但又感觉总是处于胜利的边缘。这在许多人中产生了一种自卫心理,我相信助长了这种心态的蔓延(我是一个焦虑的偏执狂),我确信这种心态是让许多人全身心地投入其中的原因之一。这可能是有趣和令人满足的,但对于许多人来说,它变得更重要。这些人为了参与 Rust 相关的事业付出了全部,将自己的职业或目标感、身份投入其中。自己的工作和未来往往岌岌可危!而且很多人也因过度工作、过度努力而真正精疲力竭。那些没有精疲力竭的人往往保留着一种内在的承诺,这使得缓和冲突变得困难,妥协变得困难,甚至公开承认问题也变得困难。

第三,Rust 最奇怪的文化规范之一(我可能有助于形成它,如果是这样,我向你道歉),是许多人都相信所有的冲突都是“虚假的权衡需求”,即冲突显示需要权衡的地方,都可以像 Rust 声称解决速度与安全之间的权衡问题那样,不用权衡地取得双赢。所有事情都可以实现双赢,如果你找不到这样的解决方案,那就是你没有努力尝试。我的意思是,如果真的能双赢,那当然很好,但是有时事情只能在合理的权衡甚至一定的冲突中形成决定!有些事情是零和博弈的。

第四,Rust 项目社群在管理或解决冲突方面没什么正式机构,考虑到 Rust 项目社群的规模,这个问题尤为突出。Rust 项目社群是互联网上的志愿者构建的,并在一个自身没有强大的正式架构的组织中孵化(Mozilla)。我推荐你阅读《无架构的暴政》这篇文章。Rust 项目社群中只存在着非正式的架构,或者执行严格的最终手段的正式架构,例如 moderators 或者外部干涉。

最后,这些非正式架构当中,有一种普遍存在于 Rust 和许多开源项目当中,这就是时间承诺的规范。《无架构的暴政》中也提到了这一点。热情的贡献者通常愿意并且有能力在项目上投入大量时间。然而,并不是所有人都能够遵守这种时间承诺。付出时间往往被明确激励:“真正做事的人有权做决策”。但是,这些决策往往会影响到许多其他的利益相关者,而“拥有最多时间的人”可能无法很好地代表这些利益相关者,或者可能缺乏完成这项任务所需的技能或知识。而“比其他人花更多时间”的做法也是一种相当不健康的处理冲突的方式:与其直接面对冲突,不如跟对方比拼时间投入,互相消耗精力。即使在正式架构内部,如果有“每单位时间贡献多少输入”的余地,这种做法也可以被采用。有些人会出现在每个会议上,推动相同的议程。这种做法行之有效,你能够按照自己的方式推进事情,但从社群角度上看,这并不是最好的方案。

修复问题

我真的不知道如何解决这些问题。如果我知道,我肯定会提出建议。我之前说过,我对助长一些模式感到相当负责。不过当然,过去的已经过去了。对项目来说,最重要的是未来。

我想我的主要建议是一个“不要听我的建议”的建议:“雇佣并听取那些在相关领域接受过培训的专业人士”的建议,其中“相关领域”涵盖了“一群编译器极客们”通常不擅长的一切。从项目管理到政治科学、金融、传媒、调解、人事管理。Rust 项目现在是一个相当大,而且非常分散的组织,人们长期以来一直在研究如何运行这类组织,甚至不同的专家研究其中不同的细分领域。应当听取他们的意见,而不是试图从某个第一性原则开始解决每个问题,也不要假装因为你们是一群互联网上的编译器极客就可以回避一个正常组织的所有机制。

我不知道新的治理体系在多大程度上具有这些专业人士的痕迹,也不知道它是否会在很大程度上解决上面提到的问题模式,但我可能会对结果感到惊讶。我在这些领域没有专业技能!如果一定要说,我喜欢“权力明确划分”、“任期限制”和“角色轮换以避免倦怠”这样的概念,以及透明决策;我喜欢设置某种利益相关者的代表和限制时间投入的机制。

我不知道社群是否承认现实中存在冲突,以及是否必须明确解决冲突。我不知道当前的治理模式和治理机构,在使项目中的权力职位,包括非正式的权力职位,对其行为负责方面,或在公开宣传以树立信心方面,是否做得足够。最主要的是:我不知道现在的治理模式,是否能够让那些不想全身心投入项目的人过上舒适的生活。

就我个人而言,我根本没有足够的精力,这一切对我来说都太沉重了。对于那些参与其中的人,我祝你们一切顺利。祝好运。

追加讨论:基金会

我想明确一点:据我所知,Rust 项目社群中的治理问题并不是 Rust 基金会导致的。每当社群中发生一些争议,总有一群人会把矛头指向基金会,这通常是错误的。

再次强调,我只是一个从旁观者的角度观察和倾听的外人。基金会通常似乎是房间里的成年人,当它做出一些表面上看起来奇怪的举动时,通常是因为它试图解决项目交给它的一些不可能解决的困境。

我认为基金会实际上只是想支持这个项目的存续,而 Rust 项目一直不是一个很容易支持的对象。

追加讨论:企业赞助商

此外,虽然 Rust 项目在一定程度上是由志愿者推动的,但它也在一定程度上是由企业赞助的,并且不仅是基金会成员的赞助。尽管有时我认为企业赞助会对维护者产生不良影响,导致维护者没有动力做那些琐碎的维护工作,但我认为 Rust 不会出现人们最担心的情况:企业在冲突或不受欢迎的决策中“购买影响力”,或者以其他方式操纵这门语言的发展。

这是一个可能存在的问题,但基金会的架构在设计之初,就花费了很大的精力来最小化相关风险。到目前为止,我认为我们没有看到这种情况。

追加讨论:最初的问题(BDFL)

为了更加具体地解释本文开头的“不希望”和“不会”的回答:我不喜欢受到关注或压力,当我在 2009 年至 2013 年担任项目技术负责人时,我接近极限运作,我离开的一部分原因是达到了这些极限并且有些崩溃了(以及公司对我燃尽的事实的反应并不好:参见“每个人都是人”)。我在本文中描述的人性缺陷,在我身上同样存在!

此外,我没有理由相信我会建立起强大或健康的正式决策机制、冲突管理机制或委派和扩展机制。我没有受过有关这些主题的任何培训,我完全是在 Mozilla 的角色中凭直觉行事。Mozilla 本身似乎对这些主题也没什么经验。我曾经试图在 Rust 的设计上进行一次“正式决策”,我试图用投票结果确定关键词命名的选择,但是结果非常糟糕:每个人都讨厌结果。

追加讨论:Moderation

我不了解 Rust Moderation Team 的故事。我从 2013 年其就不在 Rust 项目社群中了,因此,我不希望对他们过去的行为做出任何的评价或暗示,尤其是今天的 Moderation Team 已经不是过去的那一个了。在我看来,这是一个需要知晓内幕的人在其他地方讨论的主题。我想说两件事:

  1. 我写了最初的行为准则,且当时的行为准则更简短、更简单。我同意明确的社群规范和强制执行的能力通常是互联网社群所必须的。我不认为有管理员是一件坏事,也不认为管理员在谨慎和受监督的前提下行使权力是一件坏事。
  2. 我承认版主也是人。他们可能自己违反行为准则,也可能在处理其他人的违反准则行为是不够诚实。我认为这通常很少见,抱怨这种可能性的人往往没有站得住脚的理由,不过这些情况确实可能存在。要求对管理者的行为进行更严格的审查来避免这种监守自盗的可能性也算合理。根据我的经验,大多数好的版主愿意记录他们的决策过程,并解释决策的理由。

追加讨论:小团体和缺乏透明度

拥有志同道合的朋友是很棒的!关起门来做事情,不必向网络上的陌生人解释你所思考或说的每一件小事也是很棒的!这些事情本身并没有问题。实际上,这通常是许多人感到安全、舒适和愿意参与的先决条件。成为一个被审视的公众人物往往令人筋疲力尽,并且可能会排除那些被天性或环境所限的人。但一定程度的透明度是做出对他人产生影响的负责任决策的必要部分,也是权力行使的一部分。

Rust 社群何以走到今天?

本文有些标题党,实际想讲的内容,是我从部分 Rust 曾经的核心开发者的自述当中,所发现的 Rust 项目社群开源协同模式发展至今的一些特点。

我会从这些自述发言的内容切入和展开,对比其他社群遇到相似挑战的状况和应对方式,讨论 Rust 项目社群在协同方式维度上走到今天的沿革。

Graydon Hoare 的博文

说起 Rust 语言的核心成员,你会先想起谁?

不像 C# 之父 Anders Hejlsberg 和 Python 之父 Guido van Rossum 这样在很长一段时间里可以代表语言本身的人物,也不像 Brian Behlendorf 早期经常代表 Apache Web Server 项目发言,Rust 项目社群的一大特色就是不仅没有所谓的终身的仁慈独裁者(BDFL),甚至很难找到一个有足够权威拍板的人。

这就是 Rust 社区协同模式发展成今天这样的一个核心影响因素。

Graydon Hoare 是 Rust 的第一作者,但是如果你不对 Rust 的历史感兴趣,在日常讨论 Rust 的对话中,你很难听到这个人的名字或者他的观点。这是为什么呢?

通过查看 Graydon 在 Rust 仓库的提交记录,我们可以看到自 2014 年起,他就可以说不再参与 Rust 核心的开发了。

2014 年起,Graydon 的主要精力就投入在某区块链项目和 Swift 语言上:

apple/swift

apple/swift-source-compat-suite

不过,有趣的是,随着 Rust 在区块链领域越来越火,近一段时间 Graydon 又开始写起了 Rust 代码

Rust 1.0 的发布要追溯到 2015 年,而此时 Graydon 早已从 Rust 的核心开发中离开。这其中的原因可以从 Garydon 今年发布的一篇博文《我想要的 Rust 语言没有未来》中窥得端倪。

文章讨论了 Garydon 在设计 Rust 之初和早期开发过程中对 Rust 的定位,以及他对 Rust 语言当前某些特性的锐评,其中包括他不喜欢的一系列语言特性:

  • 跨 Crate 的内联和单态化;Garydon 引用了 Rust 另一位早期开发者 @brson 关于 Rust 编译时间的评论
  • 以库形式定义的容器、迭代器和智能指针;Garydon 希望这些都作为语法特性从而可以在编译器层面做更加极致的优化,这也是 Golang 的做法;
  • 外部迭代器,Async/Await 语法,& 作为类型,以及显式的生命周期;这些都是如今 Rust 语言基石级别的设计,Garydon 有不同的看法。
  • Lambda 表达式的环境捕获。
  • Traits 的设计,不完全的 Existentials 实现;后者简单地说就是跟 dyn Trait 相关的一系列设计和实现的问题。
  • 复杂的类型推导;这个评论可以参考《对 Rust 语言的分析》中的“类型推导”一节。
  • Nominal 类型;Garydon 喜欢 Golang 的 Structral 类型,这个问题跟 Traits 和 dyn Trait 的使用体验是相关的。
  • 缺少反射的支持。
  • 缺少错误处理系统;Garydon 喜欢 Swift 的错误处理方案。
  • 缺少 quasiquotes 功能,缺少语言级别的大整数支持,缺少语言级别的十进制浮点数支持;这些功能现在部分由生态中的第三方库实现,例如 @dtolnay 的 quote 库实现了 quasiquotes 的能力,但是 Garydon 认为它应该是语言的一部分。
  • 复杂的语法。
  • 缺少尾调用的保证。

可以看到,Garydon 设计和期望中的 Rust 语言跟如今实际成长出来的 Rust 语言大相径庭:关于引用和生命周期的设计,关于 Traits 和类型系统的设计,关于性能和编程效率之间的取舍,Garydon 的思路都不同于 Rust 如今的主流思路。

Garydon 对上面这些异议都分享了他 argue 的历史,实际上,他在博文一开始就对他在 argue 中失败的情形做了分类:

  1. 我计划或初步实现了 X 方案,其他人以高度的热情和坚持支持 Y 方案,包括利用舆论的影响力。最终,他们得到了自己想要的结果,而我失败了。
  2. 我初步实现了 X 方案,其他人更喜欢 Y 方案并做出了原型。Y 方案很吸引人,于是大家转而投入 Y 方案的开发,而我们时间紧迫,没有重新审视 Y 方案的所有缺点。
  3. 我初步实现了 X 方案,但是实际情况(通常是 LLVM 的限制)要求我们采用 Y 方案快速实现。我们暂时开发除了 Y 方案,然后同样由于时间紧迫,我们就一直将错就错的在 Y 方案上持续发展。
  4. 我初步实现了 X 方案,但是实现得明显有问题。于是在正式发布前我们把它从语言核心中移出,免得开发者在错误的基础上构建软件。于是一个语言级别的功能没有实现,后来生态系统或许填补了这一空白,甚至或许有多个竞争者。

对号入座,上面的众多 Garydon 不满意的特性都能分类进这四项中来。Garydon 在介绍这些他不喜欢的特性时多次提到了“我输了”,甚至在关于尾调用的讨论中,他写到:

由于实现尾调用的计划和其他成员对“性能上胜过 C++ 语言”的目标有冲突,我最终被说服不要使用尾调用。于是,我写了一篇悲伤的帖子来表达我对这个结果的不满,这是这个话题中最令人悲伤的事情之一……如果我是“终身的仁慈独裁者”,我可能会让语言朝着保留尾调用的方向发展。早期的 Rust 有尾调用,但主要是 LLVM 的原因让我们放弃了这个功能,而对跟 C++ 在性能上比拼的执著,使得这个结论几乎永远不会被推翻。

这就是 Rust 项目社群不同于其他编程语言项目社群的一个重要特点:它的作者不喜欢语言实际采用的许多核心设计,他没有妥协,也没有说服其他人,最终只能选择离开。

开源世界里有没有作者后来离开的例子,或者朝着不同于最初目标发展的案例呢?其实是有的。

Python 的作者 Guido van Rossum 在前几年也开始输掉不少关于 Python 新语法的争论。终于在 2018 年,他精疲力竭地离开了。但是至少 Python 的几乎所有核心功能都是在他的领导下实现的,同时他也能够以 BDFL 的身份强推社群从 Python 2 迁移到 Python 3 上来,尽管他事后表示这样的事情再来一次他或许从一开始就不会做。而在 Rust 项目社群当中,你很难想象有人能够做出这种级别的 breaking changes 并让社群接受。

C# 的作者 Anders Hejlsberg 在开始搞 TypeScript 以后实际也不怎么参与 C# 的发展了,但是他奠定了 C# 的基础和核心设计,且 C# 总体由思路一致的微软研发团队主导开发,所以这没有带来什么重大的影响。

Apache Flink 的作者最近也去创业搞别的事情了,不过由于社群人丁兴旺,社群发展过程一直是开放讨论达成共识,在早期开发者离开后,现在的开发者持续维护没有什么问题。如果熟悉 Flink 的历史,你会发现起初 Flink 是一个跟 Spark 正面竞争的批处理引擎,是在 2014 年中 Gyula Fora 带着他的实习生在 Flink Runtime 的基础上把整个项目改造成了流计算框架。不同于 Rust 的是,Flink 的作者们愉快地接受了这个改造,并把 Flink 重新定位成带状态的流计算框架全力发展,最终走出了一条不同于 Spark 的竞争之路。

当然,除了作者离开的,还有坚守在原地且不断整合不同人的意见的。在这一点上,做得最出色的毫无疑问是 Linux 的 BDFL 林纳斯·托瓦兹。

《时代周刊》曾经评论到:

有些人生来就注定能领导几百万人,有些人生来就注定能写出翻天覆地的软件。但只有一个人两样都能做到,这就是林纳斯·托瓦兹。

其实,这样的例子在开源世界中实在是少之又少。跟 Graydon 一样离开项目的有没有呢?Redis 的作者或许可以算一个。即使 Redis 的基石是他奠定的,但是后来 RedisLabs 的很多发展明显跟他对 Redis 的期望是有很大出入的,他于是在 2020 年公开声明不在担任 Redis 的维护者。

@withoutboats 的博文

Garydon Hoare 的博文分享了上古时期 Rust 团队的争论,而 @withoutboats 最近的几篇博文补充了许多 Rust “中世纪”的故事。

例如,上一节中 Garydon Hoare 不喜欢的外部迭代器,Async/Await 语法,& 作为类型,以及显式的生命周期这些特性,在 Why Async Rust 一文里 @withoutboats 是高度正面评价的。

那么,@withoutboats 是谁?

他是 Async Rust 的主要实现者之一,主导确定了 Async/Await 的语法,并实现了 Pin 和 Waker 等关键接口。

可惜的是,由于一些 GitHub Commit 和账号关联的问题,我们并不能简单地列出他的所有贡献。不过,就算是所有的 Commits 都能正确显示,@withoutboats 从提交数上看仍然不能排到 Rust 哪怕前 100 的贡献者名单里,从 2020 年开始,@withoutboats 就没有在 Rust 语言相关仓库的新提交了。

如果我们看看 @withoutboats 文章中提到的另外两位 Async Rust 的核心开发者,情况会更加有趣。

aturon-contribution

Aaron Turon 从 2017 年开始就没有任何参与了。

alexcrichton-contribution

Alex Crichton 是非常重要的 Rust 核心开发者。除了 Async Rust 的开发以外,他是 Cargo 项目的核心作者,且以 rust-lang-nursery 为阵地,打造了一批 Rust 早期的关键生态。

然而,他从 2022 年起也慢慢淡出了 Rust 项目社群,投入到 wasmtime 项目的开发中去了。

当然,wasmtime 项目的核心也是以 Rust 语言编写的。Alex Crichton 的这一决定,其实有点像从 2018 年开始淡出了 Rust 项目社群,2019 年加入 PingCAP 开发 TiKV 项目的 Brian Anderson

brson-contribution

2021 年,Brian Anderson 从 PingCAP 离开,关注起区块链公司 Solana Labs 的项目,甚至最近还跟 Graydon Hoare 主持的 Rust 项目 stellar/rs-soroban-env 有些合作,也是一种循环。

这个时候,我们再来看看 @withoutboats 对 Rust 语言演化建议的几篇博文,可能就会有不一样的感受:

《大教堂与集市》第三篇《开垦心智层》里讨论了关于开源软件话语权的问题,里面提到话语权的两种来源:

  1. 代码是你写的,于是你拥有代码的“所有权”,根据“责任背后是权力”的规则,你能够对如何演进这部分代码做定夺。
  2. 争议的双方并没有明确的所有权,但是一方在整个项目中投入更多,也就是在整个项目中拥有更多的领土权,所以他作为资深者胜出。

书中提到,如果这两条规则不能解决,那么“则由项目领导人来决断”。

很显然,Rust 项目不怎么符合这些条件。如前所述,拥有 Async Rust 的代码“所有权”的人都已经离开,且很难说有什么明确的传承。“项目领导人”在 Rust 社群中可以认为并不存在,第一作者 Garydon Hoare 伤心地离开了项目,堪称继父的 Brian Anderson 也从 2017 年起投入到 Rust 写成的项目而非 Rust 语言本身。

如此,Rust 项目社群就进入了 @withoutboats 所观察到的现状:尽管用户对 Async Rust 的后续进展很不满意,尽管 Rust 语言对 Immoveable / Unforgetable / Undroppable 这些能够与编译器深度协作的基础类型的需求是清楚的,但是放眼整个 Rust 社群,对于已经确定要做的事情如何做,争论几乎总是无法收敛,而对于不确定要不要做的提案,尤其是核心假设的进化与兼容方案,更是在未来几年内都看不到达成一致的希望。

@withoutboats 在《关于 Async Rust 的四年计划》的最后一段无不担忧地分享了一个过往的失败经历:

对于那些不了解的人来说,有一个关于 Rust 中 await 运算符应该是前缀运算符(就像其他语言一样)还是后缀运算符(最终的选择)的大辩论。这引起了极大的争议,产生了超过 1000 条评论。事情的发展是,几乎所有语言团队的人都达成了运算符应该是后缀的共识,但我是唯一的反对者。此时,明显已经没有新的论点出现的可能性,也没有人会改变主意。我允许这种状态持续了几个月。我对自己的这个决定感到后悔。显然,除了我屈服于大多数人的意见外,没有其他的出路,然而我却没有第一时间这样做。我让情况不断恶化,越来越多的“社群反馈”重复着已经提出的相同观点,让每个人都筋疲力尽,尤其是我自己。

我从这个经历中学到的教训是:区分真正关键的因素和无关紧要的因素。如果你对某个问题固执己见,最好能够清楚地阐述为什么它很重要,并且它的重要性应该超过语法选择之间微小的差异。自那以后,我试图将这一点铭记在心,以改变我在技术问题上的参与方式。

我担心 Rust 项目从这个经验中得出了错误的教训。正如 Graydon 在这篇文章中提到的,Rust 开发团队坚持只要有足够的构思和头脑风暴,就能找到每个争议的双赢解决方案,而不是接受有时必须做出艰难决策的事实。Rust 开发团队提出的解决这种无休止的争议所带来的 burn out 的方式,是转向内部讨论。设计决策现在主要以 Zulip 线程和 HackMD 文档等未索引的格式进行记录。在公开表达设计方面,主要是在相关开发者的个人博客上发布。如果你是一个开发团队以外的人,那么你几乎不可能理解 Rust 开发团队认为什么是优先事项,以及这些事项的当前状态如何。

我从未见过 Rust 项目与其社群的关系如此糟糕。社群当中存在着无价的知识,封闭自己不是解决方案。我希望看到项目成员重建与社群成员之间的相互信任和尊重的关系,而不是无视目前的敌对和不满的现状。对此,我要感谢那些在过去几个月中与我就设计问题进行交流的项目成员。

Nick Cameron 的博文

对于 @withoutboats 在上一节的最后提出的问题,Nick Cameron 在今年早些时候有几篇文章做出了讨论,直接相关的是这篇《关于开放式协作的一些想法》

非常有趣,在我介绍 Rust 的历史沿革的时候,我大量引用了其核心参与者的博客,这可以看做是 @withoutboats 所提到的“在公开表达设计方面,主要是在相关开发者的个人博客上发布”的习俗对整个 Rust 项目社群工作方式的一个更深远的影响。

那么,Nick Cameron 又是谁?

nrc-contribution

他是 Rust 语言团队曾经的成员,Rust 曾经的核心开发者。跟 Brian Anderson 一样,他在 2019 年加入了 PingCAP 公司,并于 2021 年离开 PingCAP 加入 VS Code 团队做 Rust 语言集成。于是,他在 Rust 主仓库的参与也从 2019 年开始消失,只在做回 Rust 语言层面相关的工作后做了一点工作。可以说,他所写的代码,如今也进入到上一节里提到的困境中:他是作者,但是已经离开太久,且他并未找到明确的继任者,于是这些代码如今是无主的。他对相关代码的演进没有说一不二的话语权,其他人也没有。

其实,在 Rust 项目社群中对编译器或标准库还能有很强势话语权的人,我能想到的大概就是:

  • 语言团队的领袖 Niko Matsakis
  • 编译器团队的成员 Oli Scherer
  • 发布团队的领袖和 Leadership Concil 的成员 Mark Rousskov

其他人就算是所谓 Leadership Concil 的成员或者是某个团队的领袖,从《开垦心智层》里提到的朴素的代码“所有权”的理解方式来看,也不一定是有说服力的。

说回 Nick Cameron 的博文,《关于开放式协作的一些想法》的核心是重申了 Rust RFC 流程的价值,并希望 Rust 在代码以外的治理流程里也坚持相同的开放原则和勇敢做出决定而不是征求所有人意见,这跟 @withoutboats 博文的观点是一致的。

实际上,Nick Cameron 在前两年年强力推进 Async Rust 的部分工作和 GAT 进入稳定版本。后者引起了巨大的争论,这同样让他精疲力竭,写下这两篇文章表达自己对这些争论的看法:

在具体项目的开放式协作以外,Nick Cameron 的其他几篇博文揭示了 Rust 项目社群今日协同格局的另一个重要影响因素:Rust 基金会。

评论

Rust 项目社群发展成今天的样子,其最核心影响因素,就是开发层面没有一个说一不二的领导人,或者一个团结的核心团队。

相信很多人还记得前两年 Rust Mod Team 集体辞职的事情,作为某种后续,实际上 Mod Team 批评的 Core Team 成员包括 Core Team 本身也都从 Rust 社群中消失了。

取代 Core Team 的是所谓的 Leadership Council 组织.该组织于今年六月份成立,起初每周有一次会议,现在减少到每个月有一次会议。讨论的内容主要关注治理、流程和标准问题。

这种情况是否经常发生呢?实际上它也不算罕见。

几乎可以称作 1:1 复刻的例子是 Perl 社群 2021 年的一个故事:致力于激进推动 Perl 发展的项目领袖 Sawyer X 想要推动 Perl 7 版本的发布,结果被其他项目成员联手弹劾。最终项目夭折,他也失望地离开了 Perl 语言团队。Perl 如今也是由一个叫 Perl Steering Council 组织管理。不过不同点是 PSC 尽管在语言发展上相对保守,但确实是领导语言开发的,且工作内容全面公开

作为补充,有心的读者可能已经发现上面列举的三位撰写跟 Rust 发展相关的博文的前核心开发者,如今并不在 Rust 的任何治理机构上。所以尽管没有那么激烈,但是他们的处境和 Sawyer X 是有某种相似之处的。

最后,Rust 项目社群的未来会如何发展呢?我想最有可能的结局,就是像 C 或者 C++ 那样,演化成由标准化委员会主导的语言项目。

实际上,这个新的 Leadership Council 做的一件重要的事情就是开始搞所谓的 Rust 标准

Rust 不像 C# 和 Golang 那样,语言本身就是某家公司的独占软件;也不像 Java 那样,虽然有 JCP 和委员会,但是 Oracle 以模块化提案为契机,奠定了自己几乎说一不二的地位;更不像 Ruby 或者 Elixir 这样的个人作者可以作为 BDFL 拍板。

Rust 基金会的成员就像 C++ 标准化委员会里的成员那样,哪个不是行业大鳄,哪个不是已经有或者打算有海量 Rust 生产代码。为了保护自己生产代码不被 Rust 演进制造出庞大的维护迁移成本,这些厂商势必要尽己所能的向 Rust 项目社群发挥自己的影响力。

由于 Rust 的第一作者和绝大多数早期核心作者已经长期离开项目社群,即使现在回来也不可能再建影响力。唯一有足够长时间和技术经验的 Niko Matsakis 又只关心语言技术发展,甚至 Leadership Council 的语言团队代表也让其他成员参与。这种情况下,Rust 项目社群的个人开发者,是不可能跟基金会里的企业有对等的话语权的。

实际上,如果 Rust 真能发展到 C++ 那样的状况,即使 C++ 有公认的第一作者 Bjarne Stroustrup 存在,他也无法在 C++ 委员会中强力推行自己的主张。

如果你对这样的未来感到好奇,推荐阅读 Bjarne Stroustrup 的论文 Thriving in a Crowded and Changing World: C++ 2006–2020 的第三节 C++ 标准委员会。我预计是已有之事,后必再有。

Elastic License 2.0 与开源协议的发展

译序

我在此前的多篇文章中讨论了商业开源的话题:

这些讨论当中观点的源头,除了我在商业开源公司的工作经历以外,也有对国外企业主和律师的内容的理解。其中,撰写了《Open Source for Business》(中文版为开放原子开源基金会律师刘伟翻译的《商业开源》)的大律师 Heather Meeker 的观点尤为重要。早在夜天之书 #6 一文里,我就引用过 Heather Meeker 的观点。

今年五月份前后,我读到了 Elastic License 2.0 and the Evolution of Open Source Licensing 一文。它是 Heather Meeker 律师带头撰写 Elastic License 2.0 协议背后的故事和自述。我深感它对于近五年商业开源软件形势发展的影响,于是向 Heather Meeker 申请了翻译本文的授权。今天终于有时间完成翻译,希望能帮助国内关注商业开源的企业家、开发者以及律师,了解发生在北美软件行业的一系列变化。

以下原文翻译。

2021 年 2 月,Elastic 发布了其软件产品的新协议,即 Elastic License 2.0 协议。通过这一举措,包括 ElasticSearch 和 Kibana 在内的一系列重要软件采用了一种新的、公开的以及简化的协议模型。这一变化是如何发生的?其背后的原因是什么?这些变化又意味着什么呢?

Elastic 的新协议是针对采用开放发展模式的公司在软件协议最佳实践方面的一个重要趋势的结晶。它并不是一个开源协议,但它旨在设定最低限度的限制,以在自由使用、共享和修改软件之间取得平衡,并防止对社群造成损害的行为的发生。

UNIX / Linux / 自由软件 / 开源

要想理解 Elastic License 2.0 所代表的新协议趋势,知道它是如何从开源协议运动中发展而来的至关重要。

开源运动或自由软件运动,源于开发者对软件私有化和软件开发分叉的担忧。UNIX 的一系列操作是这些担忧的来源。

UNIX 是当时最流行的操作系统。多年来,UNIX 的许可条件非常慷慨,因为它的开发者 AT&T 贝尔实验室受制于 1956 年的同意法令,不能从其研究项目中获利,其中包括 UNIX 和 C 语言。学者、研究人员和开发者开始分享他们的改动和改进,因此 UNIX 很快成为操作系统的领导者。

原注 “Modification of Final Judgment,” August 24, 1982, filed in case 82-0192, United States of America v. Western Electric Company, Incorporated, and American Telephone and Telegraph Company, U.S. District Court for the District of Columbia web.archive.org/web/20060827191354/members.cox.

然而,上述同意法令在 1983 年一经解除,AT&T 就立刻根据传统的商业条款设计不允许分享更改的软件协议。此后,UNIX 分裂成许多不兼容的版本,并且其专有软件协议禁止用户像以前那样通过分享改动进行合作。

自由软件运动以及随后的开源运动是对 UNIX 私有化的回应。它们试图防止基础设施软件再次走向封闭。这个运动以 UNIX 的自由软件替代品 Linux 为中心,并很快发展成一个认为“所有软件生而自由”的具有巨大影响力的运动。这个运动的核心理念包括用户有权访问源代码、改进软件和分享软件的改进版本。这些原则体现在 GNU 通用公共协议(GPL)中,该协议要求二进制文件的分发者必须向接受者免费提供相应的源代码。

随着时间的推移,尤其是 2000 年初互联网的兴起,开源协议变得越来越受欢迎。尽管部分开源协议(例如 GPL 协议)新颖且复杂,引发了一些法律上的担忧,但它们为企业间的合作铺平了道路。很快,开源以及它所促进的合作被整个技术行业全心接受。如今,开源是电子商务的支柱,企业经常合作开发基础软件。

云的兴起和 AGPL 协议

GPL 协议要求分享修改后的源代码,但是这个要求只在二进制分发时生效,即取得二进制的人有权索要源代码。但是,这意味着 GPL 允许制作和使用“私有版本”:如果不对外分发二进制,也就无需分享更改。在大多数软件仍然依靠本地分发的年代,这种方式有效地促使了分享。从 2000 年开始,软件交付开始向公共云迁移,软件服务提供商不再需要直接向客户分发任何二进制文件。相反,客户可以在不获取本地副本的情况下使用软件。

随着云服务的业务规模增长,这种范式转变激化了部分开源社群和 AWS 等企业之间的紧张关系。云厂商没有任何法律义务分享他们的改进。有点讽刺的是,这种情况有时被称为“Google 漏洞”。“Google 漏洞”这一称谓之所以说讽刺,是因为尽管谷歌依赖 Linux 来支持其搜索服务,但是谷歌和许多其他顶级云厂商(如 IBM 等)为包括 Linux 在内的开源社群做出过重大贡献。

自由软件社群对此的回应,是创造了一种名为 Affero GPL (AGPL) 的 GPL 替代形式。AGPL 3.0 与 GPL 3.0 几乎完全相同,但增加了一个远程网络交互条款,该条款规定:“如果你修改了程序,你的修改版本必须明显地向通过计算机网络与之远程交互的所有用户,提供通过某种标准或习惯的软件复制方式,无偿地从网络服务器获得您版本的相应源代码的支持。”这个新的协议旨在强制云厂商分享它们的源代码改进,从而再现 GPL 约束 Linux 发行版开放其源代码的成功。

If you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network … an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software….

AGPL 和双重许可

AGPL 自首次发布以来就备受争议。

在 GPL 3.0 起草并最终于 2007 年发布的过程中,有一派人想将 GPL 改为与如今的 AGPL 一样的网络共享模型。然而,自由软件社群最终决定保留 GPL 3.0 中的“漏洞”。

几个月后,自由软件基金会发布了 AGPL 作为解决该漏洞的替代方案。但是 AGPL 并没有得到广泛采用。不过,就像 GPL 有 Linux 这一杀手级应用,AGPL 也有自己的杀手级应用,这就是 MongoDB 数据库。

MongoDB 是一款非常受欢迎的分布式数据库产品。虽然一开始,很多企业难以理解和接受 AGPL 协议,但是大多数用户从未更改过软件,也没有将其作为服务提供,因此他们能够理性地决定在 AGPL 下使用该软件。

MongoDB 基于 AGPL 协议设计了它的双重许可商业模型,即软件可以根据被许可人选择的两种协议之一提供:(1)AGPL 协议(2)经过协商取得的商业软件协议。那些不希望遵守 AGPL 要求,或不愿进行法律分析以确定是否能够遵守的人,选择购买商业软件协议。

这种商业模型最初由 MySQL 开创,MySQL 当时使用了 GPL 的一个变种。随着时间的推移,AGPL 成为双重许可模式的首选软件协议。MongoDB 在这种协议模型下取得了相当的成功。AGPL 是常用软件协议里最强的强制共享(Copyleft)软件协议,因此在推动商业谈判方面最有用,也就被用在双重许可模型上。但是,AGPL 的起草者批评了这种使用 AGPL 的方式,称该商业模型是一种有害的勒索行为。尽管如此,APL 的源码分享条件并不足以阻止企业在不给开发者或用户社群带来任何回报的前提下大规模商用。

Strip-mining

译注:国外开源语境下的 strip-mining 含义和国内所谓的“白嫖开源”意义类似。

就像云计算的发展打破了基于 GPL 的双重许可模型一样,2010 年代云计算的进步,云交付模式开始对基于 AGPL 的双重许可模型施加压力。

这次问题有所不同,“漏洞”出现在 GPL 或 AGPL 的范围仅限于一个单独的程序可执行文件。这个“漏洞”是有意设计到 GPL 中的,理论上,版权协议只能为一个可受版权保护的作品规定条款。因此,GPL 对衍生作品(derivative works)有源代码共享的要求,但对集体作品(collective works)没有。在法律上,这两者之间的界线非常模糊,完全取决于观察者的主观看法。但在过去,随着 GPL 的普及,强大的行业实践已经形成:一个程序被定义为一个可执行进程。自由软件基金会在其 GPL FAQ 中长期阐述了这些原则。

然而,随着云服务的发展,发生了两件事:

  1. 软件工程越来越专注于云部署。云厂商曾经需要修改开源软件以使其能在云环境中正确运行,但软件工程的进步使现有的开源软件更加适应云厂商的“即插即用”需求。也就是说,不用修改一行代码,开源软件也可以与云基础设施良好的集成。
  2. 云厂商开始在核心开源软件之外进行创新。他们开发了额外的软件来管理、监控和部署软件。这些创新推动了云服务的业务增长。同时,它们是跟核心开源软件独立的软件,即使核心软件以 AGPL 发布,也无法强制云厂商分享这些辅助软件的源代码。

这两者相结合形成了这样一种形势:商业开源公司实际上成为大型云厂商“资产负债表以外的研发机构”。这个问题在开源的平台软件或中间件方面尤为突出,因为这些软件位于顶层应用程序和操作系统之间,在应用程序栈中起着重要作用,且对于云部署非常有用。

这种变化在商业界引起了对云厂商使用开源软件的强烈抗议。在 2018 年的一份具有里程碑意义的宣言中,贝恩资本的 Salil Deshpande 写道:“明确地说,这并不违法。但我们认为这是错误的,不利于开源社群的可持续发展。”另一位评论家写道:“AWS 正在攻击开源的阿喀琉斯之踵:窃取他人的工作,并销售租赁这些工作成果的服务。”问题在于,所有主要的开源协议都允许以这种方式使用软件。

商业开源公司及其投资者对开源模型的局限感到不满,他们手头没有任何软件协议可以利用版权法强制云厂商进行共享。即使是 GPL 和 AGPL 也对此无能为力。

同时,拥有庞大客户群的云厂商可以为开源软件提供更好的云平台集成。在 AWS、Azure 或 Google Cloud 平台上,客户可以轻松地一键添加软件。一些开源软件的开发者提供了自己的云服务,但是发现与免费使用他们开发的开源软件的大型云厂商竞争太困难了。即使开发者的服务更好,与云厂商建立合作关系也存在交易成本,而不仅仅是云厂商原生集成服务的一键集成体验。

SSPL 和源码可得协议

在 2018 年,整个行业的发展来到了一个临界点:随着 AWS 等云厂商持续不断地通过托管开源软件挣钱,开发者们开始采取应对措施,首先就是一系列快速的软件协议变更。

商业开源公司对 strip-mining 问题做出了两种不同的反应:一种是超强网络共享软件协议,另一种是带有限制条件的源码可得协议。在此之前,还没有人对这两类协议进行明确定义。这两类协议都旨在支持双重许可模型,即引导潜在客户经过协商购买商业软件协议,就像帮助构建 MySQL 和 MongoDB 的模式一样。

超强网络共享软件协议的方法由 MongoDB 推进发展,他们在 2018 年发布了 Server Side Public License (SSPL) 协议。SSPL 与 AGPL 几乎完全相同,但扩展了 AGPL 的远程网络条款,如下所述:

  1. Offering the Program as a Service.

If you make the functionality of the Program or a modified version available to third parties as a service, you must make the Service Source Code available via network download to everyone at no charge, under the terms of this License. Making the functionality of the Program or modified version available to third parties as a service includes, without limitation, enabling third parties to interact with the functionality of the Program or modified version remotely through a computer network, offering a service the value of which entirely or primarily derives from the value of the Program or modified version, or offering a service that accomplishes for users the primary purpose of the Program or modified version.

“Service Source Code” means the Corresponding Source for the Program or the modified version, and the Corresponding Source for all programs that you use to make the Program or modified version available as a service, including, without limitation, management software, user interfaces, application program interfaces, automation software, monitoring software, backup software, storage software and hosting software, all such that a user could run an instance of the service using the Service Source Code you make available. [emphasis added].

译注:法律文本翻译极其拗口,这里放原文。主要 SSPL 跟 AGPL 的区别就在于 AGPL 仅对衍生作品提出分享源码的要求,即前文所述的运行在同一进程的代码,而 SSPL 定义了 Service Source Code 的概念,即要求整个服务栈相关的代码都需要以 SSPL 的条款提供。

SSPL 的编写旨在为 strip-mining 问题提供一个开源解决方案。它的源码共享要求比 AGPL 更广泛。这种更广泛的源码共享描述被有意设计成类似于 GPL 对分发软件的要求的形式。MongoDB 继续采用双重许可模型,其软件可根据 SSPL 协议或经过协商的商业软件协议提供。

MongoDB 将 SSPL 提交给开源促进会(OSI)审议。经过数月的激烈争议后,SSPL 的 OSI 认证申请被驳回,但 MongoDB 继续在其双重许可模型的“开源”选项中使用 SSPL 协议。关于为什么 SSPL 符合或不符合开源定义,讨论很复杂。不过,符合开源定义并不是唯一的争论点,总体而言,这个要求共享范围如此广泛的软件协议是否能够“保证软件自由”,尚且没有一个明确的结论。

其他人选择了不同的道路。一些公司采用了由 Salil Deshpande 主持编写的 Commons Clause 条款,而其他公司则自行制定了软件协议,例如 RedisConfluentCockroachDB 等,以及 Elastic 公司的 Elastic License 1.0 协议。不同于 SSPL 协议,这些软件协议从未期望符合开源定义。相反,它们具有专门针对 strip-mining 的限制条款。

为什么选择了这些不同的道路?这与一个被称为 Freedom 0 的概念有关。Freedom 0 的内容是:用户按照自己的意愿运行程序,用于任何目的的自由。

原注:自由软件定义和开源定义类似,但是更加简短和清晰。

开源或自由软件协议的主要特点之一,就是它不包含任何许可限制或限制条件。

原注:开源协议可以包含条件,例如发布时包含 NOTICE 文件或要求共享源代码,但是这些并不是限制用户使用软件的规定。它们只要求如果用户选择执行某些操作,您也必须执行其他操作。(译注:例如,GPL 允许用户自由使用,但是如果一个人要分发修改版本的二进制,那么分发者需要保证接收者能够以和 GPL 相同的条款使用修改版本,其中包括取得修改版本的源代码。)

相比于典型的商业软件协议,终端用户协议只允许用户使用软件,但不允许分发或修改;企业软件协议通常限制软件使用的用户数、服务器数或物理位置,并要求公司对其使用进行审计。但是,开源协议不包含任何此类限制。因此,可能有些反直觉,虽然开源软件源代码总是免费提供,但是如果一个自称开源协议限制软件的非商业使用,那么它也违反了开源定义。

这意味着任何许可限制都使软件协议不再属于开源协议。

在 2018 年及以后的重新许可浪潮中发布的所有协议都具有大致相似的限制。虽然每个协议都有自己的条款,但它们都侧重于允许用户免费使用软件,同时禁止使用软件提供竞争性的托管服务。

Elastic License 2.0

在2021年初,Elastic 开辟了新的道路,同时选择了上述两种路径。它使用 SSPL 和 Elastic License 2.0 (ELv2) 对 ElasticSearch 进行双重许可。

ELv2 非常简短。它用通俗的语言编写,内容总共只有一页多。ELv2 授予用户一个典型开源协议授予的几乎所有自由。软件的接收者可以自由使用、更改和重新分发软件。即使您以前从未阅读过软件协议,也值得一读。

ELv2 在上述自由之外有两个关键限制:

You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.

You may not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key.

第一条限制是为了解决 strip-mining 问题。通过这条限制,ELv2 不授权接受者基于该软件提供托管服务。

第二条限制禁止破解软件许可密钥。这样的限制在软件许可中长期存在,但在源码可得协议中的使用刚刚开始。这些条款允许开发人员运行与软件交互的付费服务,或者保留一些软件组件用于付费功能。

ELv2 的其他条款非常直接,对于任何阅读过开源协议的人来说应该是熟悉的。

为什么选择双重许可?

在提供 SSPL 和 ELv2 两种选择给用户时,Elastic 选择了一条不寻常的道路。如今,许多公司采用“开放核心”模型,事实上,Elastic 之前也使用过这种模型。两者之间的区别可能很微妙。开放核心模型在开源协议下提供核心软件:通常使用 Apache License 2.0 这样的宽松开源协议。然后,这些公司开发额外的适用于大规模企业部署的功能,并只在限制型协议下提供,或者仅作为商业服务提供。但是,通过新的软件协议,Elastic 坚持采用双重许可模型,即相同的软件可在两种不同的软件协议下使用。这种模型由 MySQL 首创,通常使用类似 GPL、AGPL 或 SSPL 的强制共享协议作为免费软件协议选择。由于开源协议和云服务之间的紧张关系,这种模型在最近几年变得不太流行。

Elastic 的选择更加不寻常,因为它提供了两种免费的软件协议选择,SSPL 和 ELv2 都有免费使用的条款,而双重许可通常只提供一种免费选项。通过做出这个独特的选择,Elastic 强调了其灵活性,可以免费向几乎所有用户提供软件。

Elastic License 2.0 和软件协议的最新发展

Elastic 采用了新的软件协议模型,以尽可能保持开放性,同时保持对用户和开发人员公平可持续的商业模式。在这样做的过程中,它呼应了源代码可用运动中其他参与者的目标,并在创建软件协议时寻求同行的意见。

正如 ELv2 FAQ 提到的,Elastic 的软件协议变更预计不会对其客户群体产生影响,对社群用户的影响也很小。因为大多数用户在 Elastic 的软件上构建应用程序,并不从事“将软件作为托管或管理服务提供给第三方”的业务。

设计一个更好的软件协议

此外,通过投入资源起草 ELv2 协议,Elastic 努力推动软件协议起草的技术水平。从某种意义上说,源码可得协议的存在时间与软件一样长。事实上,仅二进制的软件协议是上世纪 80 年代 PC 平台标准化的产物;在那之前,几乎所有的软件都以源代码格式进行许可。但是随着时间的推移,软件协议的形式和部署方式发生了很大变化。

ELv2 是这一趋势的最终体现。在形式上,它采用了一些最受欢迎的开源协议特性:简单直观的起草和模板协议。它的密钥保留条款使得供应商发布同时包含免费功能和付费功能的软件事也能轻松地使用 ELv2 协议。

与几十年前专有 UNIX 的不兼容版本一样,专有软件协议是一个由自定义条款和条件组成的混乱合成体。即使是普通消费者软件产品的简单终端用户协议通常也很长,晦涩难懂,大多数用户无法理解。关于没人阅读用户协议的笑话屡见不鲜。但是,大部分复杂性是不必要的。这是开源协议的一个教训,特别是宽松开源协议:一套简单的规则就足够了,而且规则越易理解,用户越有可能遵守。

ELv2 不仅简短、简单和易理解,而且其他人也可以将其用作模板。自反对 strip-mining 的辩论开始以来,用户愈发希望出现一个能够支持流畅部署软件、可以具有合理限制,但是必须简单易懂的软件协议。但是,大多数小型软件公司没有资源来起草自己的协议。因此,毫不奇怪,许多软件初创公司都希望采用 ELv2 或 Confluent Community License 等现成软件协议来构成其软件协议模型。

这个趋势愈演愈烈,最终形成了一个名为 Fair Code 的倡议和标准,其中写到:

Fair-code is not a software license. It describes a software model where software:

  • is generally free to use and can be distributed by anybody
  • has its source code openly available
  • can be extended by anybody in public and private communities
  • is commercially restricted by its authors

虽然这项倡议还处于非常早期阶段,但显然行业开始意识到需要一个公平对待用户和开发者的范式。同时,这一范式还应当允许商业开发者,以一种比如今的开源协议更加灵活的方式,在两者之间取得平衡。一位评论员甚至将最近的软件协议发展称为后开源时代。不过事实上,在商业和软件协议模型不断发展的过程中,源码可得协议与开源协议通常是并行发展的。因此,这两种模型互补,而不是互为替代品。

同一时间,其他标准化的软件协议工作也在进行。2020 年,一群律师发起了 PolyForm 项目,起草了一系列源码可得协议模板。这些软件协议由在开源和专有协议方面经验丰富的律师进行同行评审。就像 Creative Commons 用于开放内容协议一样,它提供了一系列选项,如非商业、仅用于评估和反竞争协议。所有这些软件协议,就像 ELv2 一样,都允许免费使用、提供源代码,并授予必要的专利许可。PolyForm Perimeter 和 PolyForm Shield 与其前身 Confluent Community License 类似,而 ELv2 也遵循了这一趋势,推进了软件协议可选项的发展。

如果您有疑问或想要阅读更多信息,这里有一些资源:

注:本文的起草代表了我(Heather Meeker)的个人观点。然而,我想指出,撰写这篇白皮书的工作部分受到了 Elastic 的资助。

中国不缺好的开源开发者

过去数年,我从参与 Perl 6 和 Apache Flink 等项目出发,逐步进入到开源的世界当中。我在 2019 年成为 Apache Flink Committer,2020 年成为 Apache Curator 的 PMC 成员,2021 年全力投入 TiDB 社群的建设,2022 年成为 Apache 软件基金会(ASF)的正式成员和孵化器导师并接连帮助三个开源项目加入孵化器孵化,2023 年成为 Apache ZooKeeper Committer 和 Apache Pulsar PMC 成员。

在这个过程里,我持续接触到了风格各不相同的开源社群和开源开发者,并逐渐认识到中国开发者是开源运动的中坚力量之一:中国不缺好的开源开发者。

然而,当下大部分人讨论和评价中国开源的现状时,往往会认为中国开源距离全球主流水平仍有较大差距。《中国信息化周报》撰文提到,中国开源“整体呈现‘散似满天星’之势,还未形成‘聚似一团火’的强大合力”。

立足现状,我想以一个普通的开源开发者的视角,分享我所遇见的高水平中国开源开发者的形象,进而讨论从开发者个人出发,为建设好中国开源做贡献的几个可能的探索方向。

中国开源开发者气象

中国开源开发者人数众多,且覆盖面极广。发展到今天,几乎任何主流开源软件领域都能看到中国人的身影。我虽然在开源生态中多少积累了若干年的经验,但是熟悉的领域也算不上宽泛,所以下面举出的例子,多少会局限在特定的领域里。

不过,我也不打算罗列不同领域中只听说过名字的顶级开发者,而将从学生参与开源、开源社群建设和基于开源盈利三个类别来介绍我所熟悉的中国开源开发者。

学生参与开源

毫无疑问,学生是开源的未来、开源的希望。

GSoC开源之夏等活动的宣传、鼓励和引导下,在一线开源开发者走进校园布道的影响下,同时也在校园 KOL 的示范作用和实打实的求职优势的吸引下,越来越多的中国学生以各种各样的方式参与到开源社群中来,做出贡献,锻炼自己。

例如,清华大学的研究生宋子阳 @SzyWilliam 就在去年底受邀加入 Apache Ratis 社群的 Committer 队伍

Apache Ratis 是一个 Java 构建的 Raft 共识算法实现,被应用在 Apache Ozone 和 Apache IoTDB 等多个项目当中。宋子阳是清华大学 IoTDB 研发团队的一员,从开发 IoTDB 新的共识模块的实际需求出发,为 Ratis 实现了一系列生产实用的重要功能:

这跟我从给 Flink 改进高可用模块出发,为依赖项 ZooKeeper 和 Curator 反而做了不少工作,并先后成为这两个项目的 Committer 的经历有些类似,说明要想在开源世界里做出有效的贡献,立足于实际需求是一个重要前提。此外,贴近理论、算法和正确性的工作,在校学生做起来是有优势的。

@PragmaTwice 在 2022 年成为 Apache Kvrocks 的 PMC 成员。彼时,他是一个在校研究生;现在,他是 Kvrocks Contributor 提交榜单上排名第二的提交者,仅次于从第一天就开始提交代码的项目作者。

Twice 在 Kvrocks 的具体工作可以参考我在《Apache Kvrocks 毕业随感》的介绍。在文中介绍的代码贡献之外,他还积极参与社群建设的工作:

同时,他也能够发现社群当中的活跃成员,帮助活跃成员在社群当中做出更大的贡献。

@SzyWilliam 和 @PragmaTwice 的故事让我相信,只要有价值明确的项目和合理的引导,中国学生参与开源是可以做出重要代码贡献甚至成为社群领导者的。

不止参与成熟的开源项目,学生创造的开源项目的例子也很有一些:

  • 目前就读于 CMU 的校园 KOL @skyzh 出于教学目的编写了 type-exercise-in-rustRisingLight 等多个项目,给 Rust 生态和数据库领域的入门教学创造了公共价值。
  • 目前同样就读于 CMU 的校园 KOL @ice1000 在中科院 PLCT 实验室的支持下创造了 Aya 形式化数学编程语言。

开源社群建设

开源社群建设的中坚力量是各个项目的维护者。健康发展的开源社群离不开维护者们的默默付出和及时响应,这几乎是判断一个开源项目到底是真的建设起了一个社群,还是仅公开源码的一个重要标准。

512 ""

上图中提到两个项目,Apache SkyWalking 的作者吴晟 @wu-sheng 就是一名中国工程师。他创造的 SkyWalking 应用监控系统享誉海内外,被大量生产系统应用集成。

从核心可观测平台及应用探针出发,SkyWalking 生态基于自身需求开发了 SkyWalking Eyes 软件许可证分析工具,SkyWalking Infra E2E 集成测试框架,以及 BanyanDB 可观测数据数据库。虽然不到操作系统那么底层,但是这也体现出高创造力的开源系统作为某种“根社区”带动其他需求开发的特性。

吴晟本人于 2021 年成为 ASF 的第一位来自中国的董事,并且在不同平台上为#中国开源发声:

近期,吴晟因为指出计划加入 ASF 孵化器的 OzHera 提案的问题再次引起了关于开源项目好坏标准的讨论。在此之前,他已经是 APISIX/DolphinScheduler/ECharts/OpenDAL/Pekko/ShardingSphere 等多个项目的导师。可以说,他为中国开源项目的发展投入了很大的心血。

另一位新晋的活跃开源社区维护者是 OpenDAL 的作者 @Xuanwo 漩涡。

他曾经经历过在青云发起运作 BeyondStoragde 项目的失败,也遇到过参与开源社群的种种问题。在 2021 年年底加入 DatafuseLabs 之后,他将 BeyondStorage 项目的理念跟 Databend 数据仓库对数据存储访问的需求相结合,创造了统一数据访问层 OpenDAL 库。

漩涡是一个非常外向的开源开发者,且有市场工作的相关经验。OpenDAL 满足了一众 Rust 写作的新项目简化数据访问逻辑开发的需求,独立运作后不久,就在 Databend 之外被 GreptimeDB 和 RisingWave 等项目采用。同时,漩涡主动出击,将 OpenDAL 推销到 Mozilla 的 sccache 项目里;基于 Python 绑定,将 OpenDAL 推销到一众 AI 项目当中解决它们访问存储在不同系统上的模型的需求。

在 2023 年我作为 Champion 将 OpenDAL 带到 ASF 孵化器之后,OpenDAL 发展更盛。漩涡回复 OpenDAL 社群消息的时效和吴晟相似。在他的活跃下,从年初 OpenDAL 进入孵化至今半年,Contributor 数量从 37 人增长到 122 人,Committer 数量从 4 人发展到 11 人,还迎来了第一位新的 PPMC 成员。应该说,漩涡主导维护下的 OpenDAL 社群,长期保持活跃,也一直再激励和提名新成员,但是绝对数量上又没有降低门槛大放水。这保证了整个项目按照立项愿景前进,同时又真正地在开源共同体当中发展广泛合作。

项目作者之外,如何找到能够承担项目维护工作的志同道合之人,是项目可持续的重要一环。

NexT 是 Hexo 博客框架下使用率极高的主题,我的个人博客也是基于 Hexo + NexT 的解决方案来搭建的。NexT 项目作者大约从 2020 年起就不再维护项目,转而由中国开发者 @stevenjoezhang 维护。

Apache ZooKeeper 是业内使用范围最广,也是最稳定的分布式共识系统;Apache Curator 是 ZooKeeper 的客户端库,相当于 Guava 之于 Java 的定位。前者于 2008 年开源,后者于 2011 年开源,迄今均十年有余。它们最初的作者也早已将重心转移到其他软件项目的开发。

今天,这两个项目里最活跃的开发者是从创造 ZooKeeper Rust 客户端 出发,发现了上游 Curator 和 ZooKeeper 的一系列问题并着手解决的中国工程师 @kezhuw

@kezhuw 曾经不止一次提到他对开源声誉的看重,我想这也是他为什么对每一个 patch 都如此用心并且对自己参与开发的模块具有强大责任心的理由。也是因为这个原因,我才在去年将他提名为 Apache Curator 的 PMC 成员。因为我相信他符合我对 Maintainer 的期待:富有责任心,勇于做出决定,知道何时寻求帮助。

基于开源盈利

上面介绍的中国开源开发者,无论是学生还是参加工作的工程师,大多不直接从开源项目中盈利。也就是说,他们在参与开源社群之外有其他的谋生手段。这就从根本上规避了由于“全职”维护开源项目而缺乏经济来源带来的一系列问题。

不过,基于开源盈利仍然是可能的。如果能够有人基于开源软件走通了可复制的盈利模式,确实能够对开源生态整体可获得的投入和可持续性带来正面激励。

当然,在今天的中国市场上,基于开源软件实现大规模盈利的案例仍未出现,但是这并不代表没有人在探索。

国内目前能走通的模式是小团队甚至一个人,做出一个解决应用痛点的开源软件,通过提供服务和交付赚钱,虽然可能客户完全不在乎你是不是开源软件。

XXL-JOB 由大众点评的工程师许雪里 @xuxueli 开发,部署在成百上千家公司的应用环境上,在中国软件市场当前成熟度的背景下成功成为一个解决实际问题的分布式任务调度平台。该软件基本由作者单人维护,依靠个人捐赠和企业赞助获取支持存续。

飞致云(FIT2CLOUD)把这个模式用企业化的方式运作。JumpServer 和 Halo 都是贴近终端用户,解决特定具体问题的软件。飞致云并不自己从头研发这些软件,而是跟市场上已经有足够多用户群体的软件合作,采用收购 + 雇佣维护支持的方式,把个人开源开发者的支持交付盈利模式用公司的平台规模化并摊薄成本。

此外还有研发开源软件创业的模式。

这一方式最极端的是完全从头开发一个系统,例如前青云工程师 @BohuTANG 创造的 Databend 数据仓库。@BohuTANG 曾经是 MySQL 的一个著名分支 TokuDB 的核心维护者,也是深度了解 ClickHouse 并参与贡献的开发者。Databend 起源于 @BohuTANG 用 Rust 写一个 ClickHouse 的想法,但是在后来的发展过程中,其核心技术几乎完全换了一遍。软件开发不可能不借鉴现有系统,以 Databend 的实际情况而言,这就是完全从头开发的项目。

与之相比稍有基础的是基于成功内部实践再造开源项目。例如,GreptimeDB 的创始人庄晓丹 @killme2008 曾经创造过 SOFAJRaft 等知名开源软件。在蚂蚁集团工作的后期,他负责了蚂蚁集团智能监控的设计开发工作,并基于这段工作经验创造了 GreptimeDB 项目。

接着是基于成熟开源软件打造商业产品的模式。其实上面提到的飞致云也可算入这一类别,不过这里与之区别的是基于开源软件创造新的产品并反馈上游,而不就是开源软件一模一样的产品,只是额外提供交付和服务。

例如,郭斯杰 @sijie 是 Apache BookKeeper 的 PMC Chair 和 Apache Pulsar 的 PMC 成员,Matteo Merli @merlimat 是 Apache Pulsar 的 PMC Chair 和 Apache BookKeeper 的PMC 成员。他们分别作为 StreamNative 公司的 CEO 和 CTO 运作基于 BookKeeper 和 Pulsar 的云原生消息队列和数据流平台

当然,这个组合并不那么“纯粹”地是中国人组成团队,但是中国开源事业的未来肯定是要跟世界接轨的。

上面这些例子里,Databend 和 GreptimeDB 其实还在非常早期的初创阶段,并没能实现盈利;StreamNative 虽然有营收,但是也并未上市;飞致云已经运作进十年,多次融资,经营状态良好,不过也还未上市。个人开发者通过提供支持或打造个人品牌获得资助的方式有人走通,但是并不能大规模复制。

中国开源发展的困难

承接上文,我用反过来的顺序讨论中国开源发展的困难和解决这些困难可能的探索方向。

商业探索与可持续

目前中国市场或中国团队基于开源盈利的模式探索、进展和困难,上一段已经穿插的讲了一些。我在过往文章当中也多次讨论过企业开源的策略和基于开源的商业模式:

在这里,我不在重复之前讲过的内容,仅就基于开源盈利这个主题分享几个观点。

第一个,商业和开源之间没有必然联系。

首先,开源软件的可持续并不一定要商业化。上面例子里有大量的志愿者撑起来的项目,它们要么是因为离终端应用太远很难找到商业产品化盈利模式,要么是因为用的人太少本身就是同好项目。前者例如 Apache Maven 等研发基础设施,后者例如 Linux 生态里海量面向黑客的自由软件。不用商业化,开源软件也可以活得很好,且这是 MongoDB 和 Elastic 炒作“开源商业模式”之前的常态。因此,那些爱管闲事整天怀疑开源软件“不赚钱”就活不下去的人,可以歇一歇了。

其次,开源并不是一种商业模式,也不直接带来商业优势。这在上面链接的《开源不是商业模式》里已经讨论过了。在过去的几年里,这主要是一个市场炒作的集中方向,通过营造一种“开源了就赢了”的集体幻觉来制造泡沫。如今热度减退,我们应该理性的认识到软件开源不能带来直接的商业优势。

当然,即使不能带来直接商业优势,软件开源于企业还是有价值的,同样可以参考上面链接的几篇文章了解企业开源的动机和可选的策略。

第二个,开发者只有在保证生存的前提下,才有可能参与开源社群。

这一点主要是澄清把开源当做一种哲学、宗教或信念来宣传带来的影响。当然,有人愿意这么做,这无可厚非,但是这并不是开发者参与开源社群主要的动机。

欧洲和北美的开源开发者主要参与动机是工作需要或个人需要,即他们的工作依赖了某个开源软件或日常生活使用了某个开源软件,遇到问题向上游反馈或提供补丁。前者例如一众大数据项目来自用户企业的参与者,具体点比如字节跳动参与 Apache Calcite 的李本超 @libenchao 同学;后者比如我在使用上面提到的 NexT 和 Linux 上的输入法 Fctix 遇到的问题,都会到上游社群的反馈,能修的我就提个补丁。

国内跟上面这个画像目前还有出入的主要原因是缺少上游优先(Upstream First)理念的宣传和成功案例的鼓舞。尤其是因为英文水平不行,其实真想到上游去反馈一个什么问题,很多时候说不清楚,或者一门心思强调我很急、我老板在催我等等问题,导致与上游沟通不畅。不过这个状况在越来越多开发者走通了沟通方式,并主动出来分享经验,传帮带后来的开发者之后,目前是在逐渐改善的。当然,整体情况仍然处于非常糟糕的处境,尤其是对成长于 Oracle / MicroSoft 时代的企业中层技术领导的科普缺失,导致在解决具体上游协同的问题之前,整个方向都被直接否定;或者企业上层制定了开源策略,执行到这一层就不知所措无法推进。

国内参与开源项目的主要动机要论相似,应该说和印度开源开发者最为相似:求名。

通常,中国和印度的开源开发者热衷于寻找知名开源项目,套磁询问有没有可以贡献的内容。不管自己用不用得上吧,反正是搞完就是个 badge 可以挂在包括简历上的各个地方。往往这些开发者也更急切地想知道如何成为 Committer 或维护者,最好有一个量化的制度,按照解题的方式有付出就有回报。

这个问题展开还有很多细节,但是总的来说,无论是工作需要和简历求职需要都导向开发者参与开源是保证生存的一部分,而“只是为了好玩”的开源玩家往往有着其他收入支持生存。

例如,Linux 的作者 Linus 曾坦言每年从 Linux Foundation 接受 90 余万美元作为解决他维护 Linux 后顾之忧的资金;吴晟维护 SkyWalking 一半是公司 Tetrate 对 SkyWalking 的需求,一半是在有公司支付工资以后,出于对自己项目的责任心付出时间处理 SkyWalking 社群的消息;漩涡维护 OpenDAL 实际上也是有 DatafuseLabs 公司的需求在支撑,而他通过完成这些需求维持工资收入,这使得他能将业余时间投入到社群的其他需求和创新中,而不是首先全力找工作维生。

试图投入全部做开源项目开发和维护的人,很有可能遇到 core.js 作者的困境。这位作者最后解决问题的方式是写文章陈述自己遇到的困难,并以此赢得维持其生活的捐赠。

第三个,基于开源的商业可以是一种开源社群可持续模式。

上一点提到,不少开源开发者参与上游社群的动机是工作需要,这其实隐含了为什么我们要讲开源与商业结合的理由。

如今,资本体系是主流社会运作的基本规律,要想让中国开源进入主流社会的循环分工,阐释和发展商业当中的开源成分就非常重要。

例如,红帽是第一个出圈的“开源商业公司”。它的成功和持续的商业投入,实际上为许多开源开发者创造了岗位。例如,红帽资助了 Debezium 等一众开源软件的开发,自身提供了这些开源软件交付运维的全套支持和咨询服务。

例如,Aiven 是一家开源软件的发行版开发商,它的员工积极参与到 Apache Kafka 等上游的技术讨论,并将其技术方案和实现公开,以求和上游共同发展。

例如,Upstash 提供了 Redis 和 Kafka 的全球可用的托管服务,它曾经资助了许多 Redis 和 Kafka 的多语言客户端,以寻求合作帮助其用户能够将任何语言写成的应用连接到 Upstash 提供的 Redis 或 Kafka 服务上。

例如,Snowflake 是大家耳熟能详的商业数据仓库公司,其核心代码并不开源。但是,它的云上方案重度依赖 Apple 开源的 FoundationDB 系统,因此它雇佣了许多 FoundationDB 的开发者。在今天的 FoundationDB 提交榜单上,可以看到大量 Snowflake 的员工参与贡献(以 sfc-gh- 开头的账号就是)。

商业软件公司是直接可理解的基于开源的商业产品的所有者,但是上面我提到阐释商业当中的开源成分也非常重要。因为无论公司主营业务为何,只要它的主营业务核心链路上重度依赖开源软件,它就可能是为开源付费的企业。

例如,Apple 对制造数据的实时分析的需求产生了对 Apache Flink 的需求,进而招募了 Flink Streaming API 的两位作者,并开发且开源了 Flink Kubernetes Operator 项目,为 Flink 在云原生环境下的部署使用做出了巨大贡献。

例如,腾讯业务生产环境上越来越多的 Java 应用产生的对 JDK 定制化的需求,使得腾讯招募了 OpenJDK 的 Committer 杨晓峰开发出腾讯的 Kona JDK 发行版。

项目与社群的发展

应当说,结合商业是目前中国开源刚起步探索的方向,阐释商业当中的开源成分的意义和现状甚至还没有得到太多重视。

与之相比,目前确实已经有一批中国开源开发者创造了一系列项目,解决了自己或企业的特定问题。但是,这些项目普遍遇到的一个问题就是如何围绕开源软件建立起一个繁荣的社群。

社群建设其实包括好几个方面的问题。首先是要搞清楚自己创造的开源软件解决了哪些人的问题,进而大致可以建立起一个什么规模和风格的社群;然后是具体社群建设和经营过程中,如何与开发者建立联系,如何获取信任,以及如何进行协同等问题。

同样,这些问题我在过往文章中也都有讨论:

海外关于社群建设有两份非常值得参考的材料:

我在今年的 ApacheCon Asia 2023 上做了一个名为《社群长青:开源社群如何可持续发展》的主题演讲,也是因为看到在中国开源大跨步式地从一无所有直接进入到发起创造项目的阶段,这个过程里从国外吸取的经验大多是如何参与到现有的开源社群,反而缺少帮助开源软件的作者建立起一个社群的知识。

开源社群建设的内涵太过丰富,我在这里如果展开恐怕收不住。只简单讲一点,有任何其他问题可以加入我的知识星球〖夜天之书〗提问交流。

256 ""

这一点就是加入基金会对开源项目的帮助。

我在《社群长青》主题演讲中也提到加入 ASF 孵化器能够帮助项目可持续发展。这是因为 ASF 作为慈善组织,其目的就是制造服务于公共利益的开源软件,所以整个 ASF 投入了很大的精力讨论社群的基本原则和构成要素、社群如何成功,并搭建了一套社群运作的基础设施。

The Apache Software Foundation (ASF) exists to provide software for the public good. We believe in the power of community over code, known as The Apache Way. Thousands of people around the world contribute to ASF open source projects every day.

在合规与品牌保护之外,ASF 对于中国开源项目最有价值的参考材料有以下这些:

Apache 的工作方式和理念并不一定适合每一个项目,例如知名的开源软件 Zipkin 和 MyBatis 都曾经是 ASF 项目,从 ASF 归档并更名运作后仍然健康。但是,总的来说 ASF 在过去的几年间对中国开源项目的发展尤其是国际化发展还是起了相当大的作用的。

这不仅得益于 ASF 二十多年来积累的社群发展经验,也有许多早年就参与 ASF 项目的开源领路人作为导师提供帮助,并且在国内基于 ASF 的经验宣传开源理念和成功案例。

在过去的几年里,ASF 价值观的核心理念社群重于代码(Community Over Code)深入人心。ASF 对项目开放沟通、国际化运作和合规的要求,培养了一批熟悉相关问题的开源开发者,这些人薪火相传,最终达到目前能够成规模地面向学生、面向企业决策者输出知识和方案的状态。

这里就引出一个问题:毕竟 ASF 是建立在美国的基金会,中国是否需要建立一个自己的基金会,打造一套孵化机制呢?

我认为这个需求是有的。

在目前的形势下,依赖一个建立在美国的基金会推动开源项目及其社群的发展,在充分利用国内资源和国内落地转化的环节总会遇到一些阻碍。所以,客观来说,中国需要一个可行的孵化机制,以及按照这个机制运行的成功的项目,并且实现这个“把大象装进冰箱里”的问题的要点是明确的:

  1. 参与孵化的项目是否有价值。缺少独特价值的项目杠杆太短,而且做出来了也很难说事儿。
  2. 规则是否执行到位,背后是基金会或组织是否有核心理念。否则项目很多有自己的想法,最后跑偏了也就无从谈起“孵化机制”,都是自己把自己孵出来,而不是机制。

中国官方的基金会面临的一大问题是缺少有公认价值的项目。

诚然,ASF 和 LF 的项目质量也参差不齐,甚至可以说半数以上的项目都“没有显著的价值”(注意,这不意味着没有价值)。但是,ASF 立足于曾经席卷全球的 Apache Web Server 项目,后来成为 Java 生态必不可少的成分,更在大数据时代成为绝大多数开源大数据项目的孵化基地,这是 ASF 的项目价值为它奠定的国际地位。LF 立足于全球第一的开源操作系统 Linux 社群,后来又争取到了全球第一的容器编排系统 Kubernetes 加入成立 CNCF 子基金会,并在最近吸纳了 Istio 作为成员项目,LF 由此制作了一系列技术白皮书,这是它的项目价值为它奠定的国际地位。

中国官方的基金会缺少这种经过考验的开源项目,甚至是先有基金会,再来找项目加入。对于成功的开源项目来说,这样的基金会完全是一个外来机构,加入它的意义非常模糊,除非是动用某些力量直接摊派任务,否则极少有开源项目想要主动加入这些基金会。

相比之下,加入 ASF 是认同它的开源理念,希望得到孵化过程的指导和利用 ASF 提供的开发基础设施,ASF 的品牌在许多其他社群是一个可靠的背书,这对于开发者来说有明确的价值;加入 LF 或 CNCF 是希望得到基金会的市场营销资源,由于 LF 是商业联盟,加入 LF 总是以企业身份参与的,这对于企业来说有明确的价值。

中国官方的基金会建立的逻辑主要是十四五规划提到了要“支持数字技术开源社区等创新联合体发展,完善开源知识产权和法律体系,鼓励企业开放软件源代码、硬件设计和应用服务”,说白了就是为了开源而开源。这自然很难说是有核心理念,也就无法讨论核心理念的好坏,进而往适合中国发展的方向去迭代。

中国民间的联合开源社群面临的一大问题是缺少核心理念。

中国民间是无法建立开源基金会的,这使得一些有能力搭建基金会组织的团队往往也在中国开源生态之外完成这项工作,例如 OpenRetry 基金会Alluxio 基金会

中国民间的开源组织往往以联合开源社群出现,即几个开源项目放在同一个新取的组织名字下面运作。这些组织的项目或多或少还是有用户和开发者的,具备持续发展的基本条件。但是它们的核心团队往往没有能力定义出该组织的核心理念,基本是照搬 ASF 的开源理念,或者把听说过的开源理念做一通缝合。由于不存在理念或者对理念仅仅是简单的船货崇拜,这些组织无法落实和迭代自己的核心理念,落入到上面提到的“项目都是自己把自己孵出来”,跟机制没有关系。

甚至,这些开源组织或其项目会把进入到 ASF 孵化作为发展目标,这就更无从谈起中国的孵化机制了。

开源世界的领路人

最后,落到点亮中国开源的星星之火的话题上。

应当看到,即使面对挑战和存在不足,今天的中国开源相比起若干年前的状况仍然有了长足的进步。这种进步从根本上说就是中国开源“散似满天星”的形势,即越来越多的人了解开源、参与开源,投入到开源软件的开发和创造中来。

这种变化,与第一代中国开源开发者作为领路人是分不开的。

上一段谈到 ASF 的开源经验在国内传播和实践的时候,就已经提到了领路人的作用。吴晟作为 ASF 第一个来自中国的董事,指导过不少于六个中国开源项目的孵化。姜宁作为 ASF 第二个来自中国的董事,也是第一个连任至今的董事,指导过不少于十个中国开源项目的孵化。曾经帮助过中国项目在 ASF 和 LF/CNCF 孵化的开源领路人还要更多。

同时,姜宁老师还是我成为 ASF Member 的提名人。在他的帮助下,我才更深刻地接触到 ASF 的运行机制和项目孵化机制,并参与或发起了三个中国项目的孵化。

此外,虽然我们总要走出一条适合自己的道路,但是理解开源是如何走到今天这个局面对开展工作也大有裨益。这就要求有人能够把英文的开源资料翻译成中文。

姜宁和刘天栋(Ted)参与的 ALC Beijing 为翻译 ASF 社群建设材料做了许多工作,例如:

Ted 还在开源雨林组织里翻译了一系列开源世界正在发生的重大事件的文件:

开放原子开源基金会的“源译识”项目翻译了著名开源律师 Heather Meeker 的著作《商业开源:开源软件许可实用指南(第三版)》。华东师范大学和同济大学联合成立的 X-LAB 开放实验室翻译了 GitHub 前高管写作的《开放式协作:开源软件的生产与维护》

此外,当然还有翻译了《大教堂与集市》和一系列开源软件协议的卫 Sir 以及他的公众号〖卫sir说〗。

在这几年,我花费了大量的时间发现优秀的中国开源开发者和中国开源项目,帮助他们在开源共同体当中发挥更大的价值并取得更瞩目的成就。

最近一次,是上周我鼓励在美团深度使用 ZooKeeper 的工程师 @zhaohaidao 将我们讨论的一个问题及其解法提交到上游。

ZOOKEEPER-4743: Jump from -2 to 0 when zookeeper increments znode dataVersion

对于很多开发者来说,ZooKeeper 是距离自己非常远的一个开源项目。尽管可能自己的业务核心链路上就依赖 ZooKeeper 正常运转提供全局配置读写、主节点选举以及全局计数器,但是发生问题以后往往也是绕过了事或者纯粹当做黑盒重启并祈祷。这其实是我前面提到的“缺少上游优先(Upstream First)理念的宣传和成功案例的鼓舞”以及英文能力没有在相关环境下锻炼的一个体现。

在我之前,ZooKeeper 有海外华人 @hanm@lvfangmin 参与,也有身在北京的工程师 @maoling 参与。我在 @maoling 和意大利开发者 @eolivelli 的帮助下参与到 ZooKeeper 社群,并随后帮助了 @kezhuw 和 @horizonzy 提交 ZooKeeper 的补丁。

从我的经验出发,很多开发者只要成功合并了第一个 non-trivial 的补丁(即不是修改拼写错误或坏链接等跟项目实现逻辑无关的修改),通常就能完成对开源参与的祛魅,理解开源协同的大致流程,建立起信心,逐渐走向常态化 contribute 的状态。长此以往,最终会像我在前几个月的感慨一样,发现自己给开源软件提交补丁和参与社群讨论已经如呼吸般自然。

商业源码协议为何得到 HashiCorp 等企业的垂青?

当地时间 8 月 10 日,知名开源软件公司 HashiCorp 发布一则公告,称其原先在 Mozilla Public License 2.0 下发布的 Terraform、Consul 和 Vault 等多款软件,将在未来的版本中改为使用商业源码协议,即 Business Source License 1.1 来发布。

此前,已有其他知名开源软件公司也改用或从一开始就使用商业源码协议许可其源码公开的软件。

  • MariaDB 公司的产品 MaxScale 数据库代理;
  • CockroachLabs 公司的产品 CockroachDB 数据库;
  • Lightbend 公司的产品 Akka 框架;
  • Materialize 公司的产品 Materialize 流数据库;
  • Outline 公司的产品 Outline 知识库。

本文首先介绍商业源码协议的由来和内容,进而以剖析选择该协议的公司和软件的方式讨论其作用。最后,从企业开源的动机和利益权衡出发,讨论为何 HashiCorp 等一众所谓开源软件公司,最终都走向带有商业保护条款的源码公开协议。

什么是商业源码协议

商业源码协议是 Business Source License 的逐词翻译,常被简称为 BSL 协议或 BuSL 协议,当前版本是 1.1 版本。

在此前的文章中,我基本使用 BSL 1.1 来指代当前版本的商业源码协议。但是,在 Linux 基金会推动的 SPDX 标准里,商业源码协议以 BUSL-1.1 作为标识符。这是因为 BSL 被用来指代 Boost Software License 1.0 这个更早出现的协议:它是一个类似 MIT License 的协议。因此下文及以后的文章里,我会尽量用 BuSL 1.1 来指代商业源码协议。

BuSL 1.1 是 MariaDB 公司为其产品 MaxScale 数据库代理量身定制的带有商业保护条款的源码公开协议。该协议实际使用时必然带有以下两个特点:

第一,协议授权接收方复制、修改、衍生和重新分发被许可软件的权利,但是只能在非生产环境下使用该软件。许可方可能在附加使用条款中允许其他受限的生产环境使用条款。

由于 BuSL 1.1 不授权接收方即下游用户或开发者使用不同协议分发的权利,因此上述生产环境使用的限制将传递到所有直接或间接的下游。

第二,协议约定一个不超过四年的期限。在该期限过后,接收方将获得许可方指定的变更协议授予的权限,同时第一点中的条款终止。变更协议必须与 GPL 2.0 或更高版本兼容。

把这段协议文本翻译成人话,意思就是软件以 BuSL 1.1 协议发布后,在最多四年的期限后会“变成”以新协议发布。这个新协议必须是跟 GPL 兼容的协议,兼容方向是 GPL 许可的软件能够跟该协议许可的软件集成后以 GPL 发布。再直白点说,所有 Permissive 开源协议和 Copyleft 开源协议里选一个。

所以,BuSL 1.1 协议,比起同类型的 Elastic License 2.0 (ELv2) 协议拿来即用,更像是一个协议模板。要想使用 BuSL 1.1 协议,需要决定是否约定一个小于四年的协议变更期限,以及是否在附加使用条款中允许客户在其他特定生产环境使用。

当然,BuSL 1.1 协议在许可方契约一节里也约定了任何自称 BuSL 1.1 的填完空的具体协议,只能细化这两处,且不能与一开始就授予的复制、修改、衍生和重新分发等权利相冲突。

显然,BuSL 1.1 协议并不是开源协议,因为它限制生产环境的使用,而这不满足开源定义当中的第六条“不得歧视特定领域的使用”。BuSL 1.1 在其协议文本说明中也直接地说明了这点,同时提供了两份分别对于下游用户和许可方的常见问题解答:

下面,我们从具体的应用案例出发,分析 BuSL 1.1 的使用方式及其作用。

商业源码协议的应用案例

MaxScale

首先看到的是协议诞生之地,MariaDB 的 MaxScale 数据库代理。毫不意外的是它约定了最长的四年变更期限,并以 GPL 2.0 or later 作为变更协议。这部分没有太多讨论必要,主要看它对生产环境使用的条款:

You may use the Licensed Work when your application uses the Licensed Work with a total of less than three server instances in production.

简言之,可以在生产环境使用,但是单个应用不能部署超过三个节点。

这其实就是限制了大规模集群的场景,允许个人用户和小企业用起来,做产品反馈和帮助传播。对于需要维护大集群的用户,通常也是更有实力付费的用户,限制其使用,促使它们谈判商业合同付费购买使用许可。

CockroachDB

CockroachLabs 起初使用 Apache License 2.0 (ALv2) 来许可自己的数据库。随后开发出一套所谓的 CockroachDB Community License Agreement 来发布一些高级功能和插件,以图促使高级用户付费。

稍微展开一下这个 Community License Agreement 对下一节讨论协议采用动机有帮助,因为这个模式是在 ELv2 和 BuSL 1.1 出现之前开源商业公司追加商业保护条款的常见做法。

这个协议很长,所以对开发者极不友好。去掉所有套话,其中最关键的条款是第四条“费用”和第五条“试用协议”。简言之,开发者通过申请,每个自然年最多可以试用 30 天企业版,除此以外的使用统统要付钱。

所以这其实是一个很典型的商业协议,甚至没有跟源码公开协议、开源协议放在一起讨论的价值。只是因为它是一家开源商业公司 CockroachLabs 创造,且在相当一段时间内被其他开源商业公司使用,所以做一个解释。同时它的名字 Community License Agreement 也非常的令人费解:这就是一个商业软件协议,跟 Community 没有任何关系。

回到正题,CockroachLabs 在实行 ALv2 + Community License Agreement 几年后,发现仍然无法达成商业目标,于是干脆把原先用 ALv2 许可的核心部分也改用 BuSL 1.1 协议来许可。

CockroachDB 选择的变更期限是三年半,变更协议是此前使用的 ALv2 协议。同样这个不需要展开,主要看它对生产环境使用的条款:

You may make use of the Licensed Work, provided that you may not use the Licensed Work for a Database Service.

A “Database Service” is a commercial offering that allows third parties (other than your employees and contractors) to access the functionality of the Licensed Work by creating tables whose schemas are controlled by such third parties.

可以看到,和 MaxScale 不同的是,CockroachDB 不限制一般意义上的集群使用,只是限制下游使用 CockroachDB 提供数据库服务。CockroachLabs 本身以提供数据库服务盈利,所以这可以认为是一个禁止竞争的条款。

这个条款的写法是我比较喜欢的,因为它把数据库服务定义得非常清楚:当你的应用基于 CockroachDB 搭建时,你不能支持外部用户创建他们自定义结构的表。

从一个工程师的角度来看,这意味着内部使用 CockroachDB 是可行的,只要整个领域模型仅内部可见。例如,作为一家电商公司,我可以用 CockroadDB 存储操作我的用户数据和交易数据,因为用户表和交易表都是我内部定义的,外部用户并不知道这些定义,也无法自定义表。同样,我可以把 CockroachDB 用来存任何其他事务数据,用来支持搜索引擎,这都没有问题。

Redpanda

Redpanda 公司在 2020 年开始许可自己的代码,全面复刻了 CockroachLabs 的策略:仿照 CockroachLabs 的 Community License Agreement 如法炮制了一个 Redpanda 版本,同时使用 BuSL 1.1 许可核心代码。

不出意料,Redpanda 选择了四年的“保护期”,指定变更协议为 ALv2 协议,其生产环境使用的条款如下:

You may make use of the Licensed Work, provided that you may not use the Licensed Work for a Streaming or Queuing Service. A “Streaming or Queueing Service” is a commercial offering that allows third parties (other than your employees and individual contractors) to access the functionality of the Licensed Work by performing an action directly or indirectly that causes the creation of a topic in the Licensed Work. For clarity, a Streaming or Queuing Service would include providers of infrastructure services, such as cloud services, hosting services, data center services and similarly situated third parties (including affiliates of such entities) that would offer the Licensed Work in connection with a broader service offering to customers or subscribers of such of such third party’s core services.

内容很长,一句话就是你不能允许外部用户创建主题。Redpanda 是 Kafka 的平替,这里所说的主题也就是 Kafka 或者消息队列领域里的主题。

这个限制和 CockroachDB 基本类似,也就是允许用户内部使用,但是斩断了向外部用户提供消息队列功能的可能。毕竟我是一个消息队列服务的用户,我肯定是要自己先创建主题,再往其中生产消费消息的。内部使用基本不受影响,因为内部使用属于雇员或合同工创建主题的例外范畴。举个例子,你还是可以用 Redpanda 作为内部数据同步的管道。

Materialize

Materialize 公司打从一开始就用 BuSL 1.1 来许可自家的流数据库。在同样的四年“保护期”和 ALv2 作为变更协议以外,其生产环境使用的条款为:

Within a single installation of Materialize, you may create one compute cluster with one single-process replica for any purpose and you may concurrently use multiple such installations, subject to each of the following conditions:
(a) you may not create installations with multiple clusters, nor compute clusters with multiple replicas, nor compute cluster replicas with multiple processes; and
(b) you may not use the Licensed Work for a Database Service. A “Database Service” is a commercial offering that allows third parties (other than your employees and contractors) to access the functionality of the Licensed Work by creating views whose definitions are controlled by such third parties.

很简单,MaxScale + CockroachDB 的限制一起上,即不能创建大集群,也不能提供数据库服务。

Outline

Outline 是一款知识库产品,可以对标 Notion 或印象笔记等软件。在同样的四年“保护期”和 ALv2 作为变更协议以外,其生产环境使用的条款为:

You may make use of the Licensed Work, provided that you may not use the Licensed Work for a Document Service.

A “Document Service” is a commercial offering that allows third parties (other than your employees and contractors) to access the functionality of the Licensed Work by creating teams and documents controlled by such third parties.

基本是仿照了 CockroachLabs 的写法,不过把数据库服务改成了文档服务。其中,对文档服务的定义是允许外部用户自行创建或使用文档。

简言之,如果只是在公司内部做知识库构建,可能可以允许外部用户查看,但是不允许外部用户创建团队和文档,这是协议授权范围内的。这基本满足了用户自建知识库和共享给客户查阅的需求。

Akka

Akka 是久负盛名的 Scala 语言异步通信和应用开发框架,实现了 Actor Model 的设计,号称 JVM 上的 Erlang/OTP 实现。Akka 开源的时间非常久,从 2009 年到去年中修改协议,已经以 ALv2 发布了十多个年头。许多开源软件和业务都搭建在 Akka 的底盘上。

Akka 于 2022 年九月宣布未来版本将改用 BuSL 1.1 许可。在填空题上,Akka 选择了三年“保护期”和 ALv2 作为变更协议。其生产环境使用的条款如下:

If you develop an application using a version of Play Framework that utilizes binary versions of akka-streams and its dependencies, you may use such binary versions of akka-streams and its dependencies in the development of your application only as they are incorporated into Play Framework and solely to implement the functionality provided by Play Framework; provided that, they are only used in the following way: Connecting to a Play Framework websocket and/or Play Framework request/response bodies for server and play-ws client.

写得非常复杂,但是总结下来是很简单的一句话:任何情况都不能在生产环境部署 Akka 应用,除非你是用 Play 框架开发应用,且应用中没有直接和 Akka 的接口通信。

这个涉及到一些历史背景:Play 框架和 Akka 都是 Lightbend 公司的产品,早在几年前 Lightbend 公司就宣布不在以公司投入维护 Play 框架,而是转为“社群维护”。这也是个后来被广泛应用的话术,用以指代公司放弃某个开源软件的开发。由于 Play 框架的底层就是 Akka 支撑的,修改 Akka 协议时,或许出于避免级联影响到此前已经弃养的 Play 框架的用户,Lightbend 公司通过这一条款规避了原 Play 框架用户可能收到的冲击。

从协议上看,Akka 直接禁止了生产环境的使用,可以说是最严格的 BuSL 1.1 实例。不过在 Lightbend 的博文中提到,年收入在一千万美元以下的公司,可以向 Lightbend 公司申请免费使用的许可。

HashiCorp

最后,终于我们可以看看今天的主角 HashiCorp 是怎么使用 BuSL 1.1 协议的了。

毫无意外,HashiCorp 选择了四年的“保护期”,变更协议是此前它们采用的 Mozilla Public License 2.0 协议。其生产环境使用的条款如下:

You may make production use of the Licensed Work, provided such use does not include offering the Licensed Work to third parties on a hosted or embedded basis which is competitive with HashiCorp’s products

这个协议,我称之为毫无诚意的协议,跟带兜底条款的竞业限制协议是类似的。它写的是:不能把被许可的软件用在向第三方提供和 HashiCorp 产品存在竞争的服务上。

那么,什么是“跟 HashiCorp 存在竞争”就是一个非常模糊的说法。对此,HashiCorp 的解释很有一家商业公司典型的做派:如果你不清楚是否违规,请联系我们的律师。

HashiCorp considers a competitive offering to be a product or service provided to users or customers outside of your organization that has significant overlap with the capabilities of HashiCorp’s commercial products or services. For example, this definition would include providing a HashiCorp tool as a hosted service or embedding HashiCorp products in a solution that is sold competitively against our offerings. If you need further clarification with respect to a particular use case, you can email licensing@hashicorp.com. Custom licensing terms are also available to provide more clarity and enable use cases beyond the BSL limitations.

当然,如果你给钱,HashiCorp 将竭诚为您定制符合您需要的协议。

开源商业的深层次动机及得失计算

我在《诱导转向的伪开源策略》一文中提到:

自诩“开源商业公司”的企业一开始不提供商业收费的版本,投入巨大的人力开发唯一的开源软件,这可能是一种不成熟的行为。尤其是如果公司创始人没有想好此后如何盈利,只是觉得自己写好一个软件,以开源的方式打开名声,就能坐地收钱,那日后破防也是不可避免的。
对于这些企业来说,初心就是“写个软件好卖钱”,而且确实也是创始人辛辛苦苦拿到融资,组织起来一帮兄弟姐妹领工资夜以继日地开发软件。到头来用户只是使用而不付费,甚至因为太好用找不到付费的理由。这个时候,又冒出来几家供应商把代码拿过去改改做出自己的解决方案,甚至由于上游使用宽容开源许可,这些供应商源代码一藏,随口就说碾压上游版本。
这实在是大大超出了企业创始人的预料,于是修改软件许可,让这些用户和供应商搞清楚状况就会被加紧提上日程。

在企业转向带有商业保护条款的源码公开协议时,此前利用开源标签赢得口碑和声望的企业都会写一篇辩白书:

可以看到,Materialize 和 Redpanda 这样一上来就是 BuSL 1.1 的软件是不用写一篇《我的忏悔》的。

上述辩白书不全是转向 BuSL 1.1 的,还有 SSPLv1 和 ELv2 等。但是它们都提到了同一个问题:自己投入人力开发的软件,商业对手却坐享其成,这使得竞争对手在这个层面上存在投入产出的优势。当然,这些企业随后会放大这种竞争对手的优势,控诉他们“不向上游回馈”,自己几乎要因此而无法存续,然后警告用户自己无法存续等于软件死亡。

这个逻辑链条有两个不成立的地方:

  1. 挑战通常不是无法持续,而是无法扩张。资本的本性就是扩张,赚了一百万就想赚一千万,市值一个亿就想冲一百亿。除了 Lightbend 确实有可能是因为 Scala 式微导致维持现有规模都有挑战以外,MongoDB、Elastic 和 CockroachDB 控诉 AWS 等云厂商竞争导致它们丢掉客户,实际上是增长受挫的反应。
  2. 公司等于社群对社群的存续未必是一件好事。这些企业会把开源软件的存续和自己捆绑,由于这些开源软件确实是公司产权,这也无可厚非。不过公司死了,开源软件有用,未必它就不能发展了。例如 Akka 修改协议以后,其庞大的用户群自发从最后一个开源版本拉出分支继续以开源协同的形式维护,目前在 Apache 孵化器以 Pekko 的名字在继续。

当然,逻辑链条不对不代表客观上没有道理。

关于增长受挫的问题,其之一是资本的本性就是扩张,所以增长受挫确实是资本所不能接受的。其之二是云厂商搭建起来的完善交付体系,确实挤压了采用原先商业模式的开源商业公司的生存空间。作为反制,Red Hat 虽然不能修改 Linux 的软件协议,但是通过改变其发行版的打包流水线,实际上增加了云厂商在其企业级发行版上原封不动打包交付的难度。

关于社群存续的问题,毫无疑问核心参与者的离开会重挫开源社群的可持续性。当开源社群中心的软件是企业产权,开源社群的核心开发者都是该公司雇员的时候,一旦公司倒台,大部分情况下确实软件也会随之消亡。但是事实证明 Akka 不会,ElasticSearch 也不会,我相信 Redis 和 Kafka 这样的项目,没有公司做商业化,也能持续下去。所以这是个有相关性但不绝对的论断。

我在《企业实践开源的动机》一文中,对“商业模型是销售开源软件”的企业做过判断,认为它们早晚会将自己的开源软件改成专有软件:

如果一个企业的商业模型就是销售“开源”软件本身的功能,那么他们的核心功能转向专有软件只不过是时间问题,或者说只要面临商业竞争,就是经不起挑战的。

回到修改软件协议这件事上,我在《企业开源的软件协议模型实践》一文里已经提出了这个风险:

  • 风险:竞争对手的商业竞争
  • 风险:团队分裂并参与竞争
  • 策略:运维和高级功能
  • 策略:独特品牌建设
  • 策略:赛道维度升级
  • 策略:市场占有率与开放标准

随后我提到的“源码公开但禁止商业竞争”其实就是第五个策略:改变协议。其中又包含“核心禁止商业竞争”和“高级功能需要付费”的分支。

应该说,在现有的市场经济体制和资本主义体系下,开源软件本身是不能作为企业盈利的核心的。即使在《大教堂与集市》的《魔法锅》一章中,Eric Raymond 提出了十几个与开源软件相关的获利途径,他也同时承认“开源使得从软件中直接获取销售价值变得更加困难”。

对于商业开源来说,企业在当前的体制体系下能够找到的相对最优解,就是 BuSL 1.1 协议或 ELv2 协议:绝大部分和开源协议一样,但是禁止(直接)商业竞争。因为如果发行商能够直接和原厂竞争,那么原厂投入的研发资源就被竞争对手搭便车,竞争对手从而能够构建出其他排他的优势。典型的案例就是云厂商可以优化自己的交付体验和云基础,直接移植开源软件提供收费服务。对于没有这样一个完整基础设施团队和获取客户的强大销售团队的原厂来说,这确实使得它们在市场竞争下失去了独占软件产权的优势。

不过,对于开源软件本身,这并不是什么致命的变化,甚至都谈不上变化。

开源软件只有很少的一部分是由一家企业主导的。Redpanda 闹腾得再厉害,开源的标准是 Kafka 协议。不管 Redpanda 和 Confluent 还在不在,我相信 Kafka 会一直在。当初依靠做 Hadoop 生态发行版的企业几经浮沉,并没有太多影响 Hadoop 自己的发展。至于 RedisLabs 公司,我甚至觉得它如果死了,对 Redis 可能是一件好事。毕竟以 @antirez 现在的声誉,仿照 Linus 或尤雨溪的路子养活自己并不难,而他的参与贡献才是 Redis 发展的核心。

企业选择带有商业保护条款的源码公开协议,可以看做是从完全闭源的商业协议,到冒进的全面开源协议,在之后的一次螺旋上升的准备。我相信开源协议是完善的,它解决了源代码如何自由分享的问题。在《大教堂与集市》的论述中,Eric Raymond 从未指望供应商自己转变,而是试图通过说服用户理解开源的价值,以及鼓动任何企业开源其非盈利软件来实现开源的未来。这也是我认可的方式。

对于个人开源来说,Apache 基金会的前任董事、经验丰富的开源开发者 Ted Dunning 说过一个故事。

Ted 曾经在发布自己的软件源码时,授权接收方自由使用、更改和分发,但是如果接收方要商用,那么需要向他付一笔授权费用。但是后来他发现,用 Apache 协议发布自己的软件以后,自己一下子得到了更多的用户和他们宝贵的反馈,而这才是他作为一个开发者所期待的。同时,作为个人,他没有那么强的动力,实际上也不可能花费大量自己的时间向每一个商用的用户索要费用。

他于是说:如果我不愿为授权费奔走,那么我根本不必提出要求。

所以,商业源码协议对个人应该是没有什么帮助的(笑)。

❌