代码托管平台常常使用 Git 钩子实现一些功能,这些钩子的操作是否等幂,也就是说,钩子的执行结果在不同的副本上退出码必须一致,如果不同副本中执行钩子不做区别,我们要保证钩子中请求 API 授权的结果一致,避免内部服务故障照成影响,执行 post-receive 钩子产生动态或者触发 WebHook 时需要进行消息去重,避免多次执行。当然还有一种方案就是只执行一次钩子,然后使用协调机制将钩子的结果广播到其他副本,共同进退。
存储库在不一致,或者从停机中恢复后,多写高可用依然需要考虑存储库的同步,以保证不同节点的一致性。
要设计好高可用,应该实现一套良好的故障检测机制,合理的方案有多种,可以用专门的服务检测磁盘是否可用,服务是否联通,出现故障时标记不可用,恢复后直接标记为正常即可;还可以通过学习,将前端服务与存储节点通信的错误采集分离,进行健康评估,在节点故障时将其下线。两者都需要不断的汲取经验,故障的错误标记往往是灾难性的,GitHub 就出现过这样的事故,给其声誉带来了一定的影响。
无论是主从同步还是读写分离以及实时多写架构,都需要给存储库创建多个副本,这就意味着存储空间的消耗加倍,每个存储库有一个副本,存储空间的消耗就要增加一倍,两个副本就增加两倍,所以在设计高可用系统的时候还需要考虑到经济因素对架构的影响,这也是国内代码托管行业高可用架构发展并不顺利的原因之一。
多写系统如果能修改 Git 源码实现一些细节的优化,这在架构上有更好的设计余地,比如我们可以修改 Git 源码支持主动非侵入数据流的原子更新,我们也可以在 receive-pack 中修改执行钩子的逻辑,使其更符合读写系统的设计。而现实并不令人满意,没有足够的人手能够参与 Git 的研究,这阻碍了国内代码托管行业的创新,很容易陷入只能苦苦追随前人的困境。
思考代码托管早期有 SourceForge,我刚刚工作时,构建的 Clang On Windows 便是发布在 SourceForge 上分发的,现在已经好几年没登录 SourceForge 了,Git 的发展不快不慢,但终归是流行起来了,GitHub 把其他平台彻底碾压,有点所向披靡的样子。不过国内得益于政策环境,GitHub 想进来并不容易,国内也就有了另一番天地。但是做到 GitHub 那样的规模并不容易,做到 GitHub 那样的技术更不容易,罗马不是一天建成的,这仍需要同行们的持续努力。
引用资料Git Repository Layout
Git Internals - Git Objects
Git Virtual File System Design History
Git at Scale
《性能,可扩展性和高可用 - 大型 Git 代码托管平台的关键问题》
《探讨 Git 版本控制系统的若干问题 - 2020 版》
《探讨 Git 代码托管平台的若干问题 - 2019 版》
《服务端 Git 钩子的妙用》
《基于 Git Namespace 的存储库快照方案》
《构建恰当的 Git SSH Server》
《Git 原生钩子的深度优化》