不知不觉漩涡事件已经过去两年多了,与此同时各大项目中仍在不断重演漩涡事件,正如 @jackworks_asref 最近遇到的这样:

这一期的周报想聊聊我对漩涡事件的理解和反思,并分别从项目贡献者和维护者的角度来探索可行的解决方案,希望对大家有所助益。

什么是漩涡事件?

2021-02-01,我向 pingcap/tipb 提交了 PR Add elems into FieldType,PR 迟迟没有合并。后来 @hanfei1991 提交了一个一模一样的 PR: add elems for fieldtype,当天就被合并了。随后项目维护者以 PR#217 已经被合并为由,提议关闭 PR#208。

现在已经不太记得当时的心情了,总之我在 PR 里留下了失望的评论,关闭了 PR,并且在 TiDB 的贡献者群里也发表了一通言论。后面 PingCAP 的处理也非常到位,专门拉了沟通群,据说内部还进行了专门的反思和总结,最后还作为案例上了 PingCAP 出版的《与开源同行:揭秘PingCAP七年创业实践》。在整个 PingCAP 处置漩涡事件的过程中,我最有印象的是 tison 跟我打的一通语音电话,分享了一些他在公司内部的观察视角,跟我探讨了公司在开源运营中的一些策略和问题出现的必然性。

漩涡事件暴露了哪些问题?

当年我还是一个青涩的开源贡献者,如今我也是几个开源项目的维护者,对漩涡事件的背后成因有了更实际的体会。正如 tison 两年前跟我的探讨的那样,漩涡事件是开源项目发展过程中不可避免的问题之一。在纯异步通信环境下,消息传递可能会丢失,特别是当消息由外部不可靠信道传递而内部缺乏协调者,并且所有工作人员都已经十分忙碌时,该消息很可能被直接忽略。这种现象在分布式系统中经常出现,同样也适用于开源项目的开发。

如今我再回顾漩涡事件,我能发现以下问题:

  • 公司开源项目治理不健全
  • 公司内外沟通渠道缺乏
  • 贡献者本身工作不到位

公司开源项目治理不健全

理解这个问题首先需要对 PingCAP 的发展历史有一定了解,这里我简单聊两句(肯定不完整也不准确,欢迎 PingCAP 的同学补充)。PingCAP 历史上首先基于 HBase 开发出了第一版 TiDB 用于验证架构可行性,当时 TiDB 算是一个简陋但是完整的 SQL 引擎。后来 PingCAP 使用 Rust 开发了 TiKV,TiDB 与 TiKV 之间使用 gRPC 通信,所使用的 protobuf 定义就在 tipb 项目中,后续 TiFlash 也同样使用 tipb 与 TiKV 通信。

从这个历史沿革中不难得出如下结论:TiKV 和 TiPB 都是为了 TiDB/TiFlash 服务的,TiPB 尽管形式上是一个独立的项目,但是其资源分配和 PR Review 受到 TiDB/TiFlash 的高度制约。换句话来说,在没有 TiDB/TiFlash 主动发起的前提下,对 TiPB 做任何形式的修改,都需要来自 TiDB/TiFlash 团队的 Review。因此我们可以看到 PR#208 等待了很久只获得了一个无权限的 Approve,而 PR#217 在发起的当天就获得了合并。从实际的情况来看,TiPB 完全是一个内部项目,只是不得不对外开源。

对于这样的项目,公司层面应当是要予以特殊考虑的。比如说:

  • TiPB 这样的项目是否要 Open for Contribution?
  • 如果是,在流程和制度上应当如何给予安排?
    • 涉及到 pb 文件的修改,是否应当 assign 关联项目的 maintainer 参与 review?
    • TiPB 的修改必然要涉及到跨部门的沟通,如何避免互相踢球的现象?是否存在着仲裁机制?

公司内外沟通渠道缺乏

次要的问题是公司内外沟通渠道缺乏。如果我记得没错的话,PingCAP 当时的沟通工具正在全面由 Slack 转向飞书,外部的贡献者已经很难直接与对项目有实际影响力的人取得联系。与此同时,公司内部的维护者也更倾向于使用方便快捷的内部沟通渠道:飞书私聊 ping 一下显然要比通过 Github 沟通方便得多。这更进一步加剧了项目维护者只关注内部动态的倾向,此时外部的 PR 已经完全没有人在参与维护了。尽管有一些同学尝试在 Github 上推进 PR 的 review 进度,但是迟迟无法上升到维护者做决定的层面。此时就出现了开源项目的内外双负面循环:公司内部做自己的,社区层面做社区的,而社区由于持续的负反馈会自然流失,从而进一步加剧了公司内部自己做自己的必要性。

此时就凸显出一个开源社区运营的重要性:他需要打破这面墙,做开源项目的破壁人。对公司,他需要向研发团队输入社区动向,化解社区贡献的 block;对社区,他需要与社区沟通,帮助贡献者了解进度,对贡献条件尚不成熟的 PR 可以及时予以劝退。当然,单个人的吞吐毕竟是有上限的,同时此处的破壁人只是不得已而为之之举。更好的做法应当是消除这层壁障,不去刻意区分公司内外,将公司视作社区的一部分,去做社区的引领者,而不是将公司和社区分为独立的两块,变成社区的管理者。只有当社区中的每个参与者(包括公司雇员)都积极参与到社区沟通中,形成自主的点对点沟通网络,才能最大化整体的带宽。

此外,从公司管理层面来看,我们还需要考虑雇员的 OKR/KPI 制定策略:雇员花费在开源治理上的时间如何考评绩效?社区参与和项目进度如何兼顾?开源治理与自己公司的商业目标如何协同?

这些问题我也不懂,提出来仅作思考

贡献者本身工作不到位

最后是贡献者本身工作不到位。

其一是沟通问题:在开源贡献中有个很明显的现象是沟通更积极的 PR 更容易被合并,相反,越是在等待 Review 的 PR 往往无人理会。

这其实很好理解,大型开源项目的维护者往往每天会收到上百个通知,他们的 backlog 会被无限拉长,维护者的处理队列更接近于一个基于任务优先级和最近 AT 时间的优先队列而不是朴素的先入先出队列。所以在提交 PR 之后需要尽量与该模块的维护者取得联系,积极推进 PR。很多维护者并没有 Watch 他们自己的 repo,也没有将自己添加为 CODEOWNER,这就导致即使在 PR 提交之后也不会受到任何通知。此时如果不主动 AT 维护者的话,这个 PR 可能会永远处于等待 review 的状态。

所以这就要求贡献者主动发起沟通,甚至需要过度沟通,在 PR 发起之前就与项目维护者沟通和协调,明确想法,取得支持;并在 PR 进行的过程中,及时跟踪状态,尽可能地回应和解决问题。当然这一切都取决于贡献者自己的意愿,如果对 PR 快速合并没有什么追求,放慢节奏,慢慢等着也是完全 OK 的。

其二是贡献完善程度:很多时候贡献者提交的 PR 没有说明清楚修改的原因和目的,导致维护者即使看到了 PR 也无法做出是否 Approve 的判断。

这里尤其需要注意的是,大型开源项目的维护者内部信息传递也是有开销的。所以即使你在其他项目中与维护者已经达成一致,也仍然需要在 TiPB 的 PR 中写清楚前因后果,关联上之前的讨论链接(公开沟通的的重要性!)。在 PR#208 中就出现了这样的问题:尽管我已经与 TiKV 的维护者沟通过,确认了修改的思路,但是我在提交的 PR 中并没有说明上下文,也没有对本次修改做出说明,PR 的描述完全是空的。相比之下 PR#217 则写清楚了该 PR 解决了 TiFlash 的需求,并解释了为什么要做这样的修改。从 TiPB 维护者的角度来看,PR#217 的质量是要比 PR#208 高很多的,尽管他们做出的修改完全相同。

如何避免发生漩涡事件?

维护者视角

  • 开源项目治理需要因地制宜,不适合公开贡献的项目需要有特殊的制度安排,比如设置 CODE_OWNER 等
  • 健全沟通渠道,提倡公开沟通,条件不成熟的情况下可以安排开源社区运营做定期的分流
  • 公司内部应当梳理开源项目与商业产品之间的关系,避免参与社区成为维护者用爱发电的事情
  • 停滞两周以上的 PR 可以通过 Actions 定期列出来并做处理,条件成熟的合并,不成熟的说明清楚原因予以关闭,仍在进行中的可以更新一下状态

贡献者视角

开源当过度沟通。

  • 在提交 PR 之前可以首先与项目维护者沟通和协调,明确自己的想法,取得维护者的支持
  • 在 PR 中应尽可能携带沟通的上下文,描述清楚本次修改的内容和目的,便于维护者判断
  • 在提交 PR 后,AT 项目的维护者邀请参与 review,及时跟踪状态并与维护者进行沟通,尽可能地回应和解决问题。
  • 如果 PR 迟迟得不到任何响应,可以早点 close 换个活跃项目贡献,甚至可以发起自己的 Fork 成为新的上游

总结

本文聊了聊我对漩涡事件的理解和反思并分别从项目贡献者和维护者的角度来探索可行的解决方案,希望对大家有所助益。