在GitHub上提高大型Monorepo性能

2021-03-17 00:45:51

每天,GitHub都服务于超过56米的开发人员,工作超过200M的代码存储库。除了来自世界各地的客户,所有这些存储库的一小部分都是具有惊人的表现。

与GitHub一样大的系统,我们的编码和架构中的一些差距仅在他们被推到他们的限制时被推到了许多开发人员每天更新相同的回购 - 而GitHub从少数人收到反馈最大的Monorepo客户,他们看到了影响他们完成推送操作能力的性能问题。

GitHub也是如此。 github / github是我们的monorepo,我们偶尔会偶尔推动自己。

要开始调查,我们与内部团队合作,并与客户一起参与,了解和优化他们对GitHub的使用,包括合并多次推动单一推动,这减少了他们所产生的锁定写作交易的数量,甚至有助于重新加工他们的一些DevOps流程来消除不必要的推动操作。尽管如此,我们仍然看到了不寻常的推动失败水平。

响应于此,Github Engineering创建了Project Cyclops。 Project Cyclops在我们的Git Systems Org(包括我们的Git Storage,Git协议和Git Clients团队)中一直是一个多月份的努力,与GitHub的上游GIT贡献者和工程师在我们的网络前端团队中使用,找到解决方案这些问题。经过大量的协作和努力工作,以及多次改进,我们已经将错误误差下降到近零,即使是我们最大的Monorepo客户拥有数千名贡献者。

Project Cyclops导致了许多不同的方法来提高我们的MONOREPO推送性能。它们在一起,改善了我们以至少一个数量级地处理推送流量的能力。

默认情况下,GitHub在每50个Git推送操作之后运行存储库维护例程,或者在我们收到40MB的未包装文件后,这使我们确保我们有最新的包装文件,可用于巨大的克隆/获取性能,并清理和解重复回购中的数据。根据存储库的大小,维护从几秒到几分钟的任何地方需要。

对于大型MONOREPOS,具有大量开发人员,50个推送操作不需要长时间累积,因此频繁调度维护,而开发人员仍在推动回购变更。我们有许多在我们的最大时间窗口中无法完成维护的那些repos。当REPO失败维护时,推送和参考更新的性能会遭受,这可能导致我们的工程师手动劳动,以便再次整理这些存储库。我们将这些维护失败降低到近零。具体而言,我们改进了Git Repack,以及我们如何安排维护重试。

在回购维护期间,我们运行git重新包装以压缩松散的物体,并为快速克隆准备并提取。

要将一组对象压缩到单个包中,Git尝试查找彼此相关的对象。而不是将所有对象的内容逐字存储,而某些对象将作为ΔStrtas存储为Deltas。找到这些增德拉斯需要时间,并将每个对象与每个其他对象进行比较可行。 Git通过搜索滑动窗口中的Delta / Base对来解决这个问题,通过包装的所有对象的数组。

窗户内的一些三角洲候选人可以通过启发式迅速拒绝,但有些需要CPU密集的比较。

我们已经实施了一个参数,以限制我们愿意制作的昂贵比较数量。通过调整此值,我们降低了在GIT重新包中花费的CPU时间,而仅略微增加生成的PackFile大小。这一变化消除了我们几乎所有的维护失败。

在Project Cyclops之前,当由于任何原因的repo维护失败时,我们不会安排它再次运行七天。多年来,这种节奏为客户提供了足够的服务,但今天的Monorepos等不及了这一点。我们为特定的存储库维护失败引入了新的虚假失败状态 - 通常来自维护期间发生的大量推送流量 - 这使我们可以每四个小时重试维护,最多三次。这意味着我们会在客户的离线时间内重试,当时更少的推动。这一变化消除了剩余的维护失败,因此从我们的接听工程师中取消了更多的劳动。

在我们的文件服务器上 - 实际上持有Git存储库的服务器 - 我们已经有一个参数到位,减慢了我们在每个处理的推送操作速率下减慢了。 GitHub是一个多租户服务,而且,此参数旨在确保从一个客户写入,不会垄断服务器上的资源,这会干扰来自该服务器上所有其他客户的流量。实际上,这是我们服务器可以做的工作量的人为帽。

在调查后,我们慢慢提出了这个参数的价值,允许100%的推送操作立即运行,我们发现使用我们当前的架构的性能超过了足够好的,而且不仅我们提高了限制,我们删除了参数来自我们的代码。这立即改善了我们的MONOREPO性能,并消除了许多与之相关的错误。

默认情况下,GitHub将每个存储库的五个副本写入我们的三个数据中心,以防止服务器,机架,网络和数据中心级别的故障。当我们需要更新GIT引用时,我们在所有数据中心都短暂地锁定所有副本,并在我们的三相提交(3PC)协议报告成功时释放锁定。

在该锁期间,我们在每个副本上计算校验和,以确保它们匹配,并且所有副本都处于同步状态。我们使用增量校验和更快,在正常操作期间,这需要小于50ms,但在维修操作期间,我们在从头开始重新计算校验和,需要更长时间。对于大型MONOREPOS,锁定了20-30秒。

我们在锁定之前进行了更改以计算这些副本校验和。通过预先计算校验和,我们已经能够将锁将锁定到1秒以下,允许更多的写入操作立即成功。

我们的一个大型Monorepo客户可以在Git性能上保留自己的内部指标,他们的号码显示我们的所作所为:他们的推送操作已经显示出几个月的故障。

另一个正在遇到太多失败的Monorepo客户计划迁移以用新鲜的存储库开始,从而最大限度地减少回购中的参考数量,以提高推动成功。在这些变化之后,我们的指标显示了他们的推动失败,近零,并在12月对他们的开发商进行了调查,没有关于最近推动失败的报道。他们取消了迁移,并继续以良好的表现运行。

想要图表?以下是这些客户的图表显示推出修复的推出近零的推送失败。

就像我们说的那样,我们已经将失败降到了近零。其中一些失败是由随机互联网网络问题引起的,并且超出了我们的控制。至于其余的,我们正在寻找消除我们可以在那里的最后一个恼人的失败,并继续更快地制作github。

在Git Systems World中,我们正在刷新我们的存储硬件,以使其更快。我们也在中间的重构努力中,我们可以分解Github着名的Ruby Monolith,并在GO中编写一个新的微服务,这将提高GitHub上每个用户的存储库性能。

Project Cyclops导致具有大型Monorepos的客户的性能和消除失败的故障,我们在文件服务器的船队上浪费了浪费的CPU周期,并且在其中一些最大的客户中使用GitHub以上使用Github的经验,包括我们的客户使用GitHub Enterprise Server。

我们至少将单次存储库更新流量提高至少一个级别。我们现在有多年的余规是我们目前的架构,以处理甚至最大的Monorepos的增长。

我们想说有多令人难以置信的♥我们是我们与我们合作的Monorepo客户,以便更好地制作GitHub。他们在报告中的帮助,有时甚至诊断,问题都是有助于解决这些问题。 ✨周围闪耀! ✨