在 Subversion 1.8 中减少网络流量

本文经原地址 http://blogs.collab.net/subversion/reducing_network_traffiic_in_subversion_1-8 许可转载。失效链接已删除或更新。

作者: C. Michael Pilato

摘要: 基于 Subversion 1.7 中发布的 WC-NG,如果文件内容已存在于本地原始存储中,则存储库访问模块 libsvn_ra_serf 可以显着减少 svn switchsvn update 的网络流量。

发布 2012-10-24

这篇文章讨论了 Apache Subversion 的功能,这些功能在撰写本文时已在开发代码库中可用,但尚未在官方发布版中发布,并且可能在发布之前发生更改。

我从未对 Subversion 的稀疏检出功能表示歉意,也从未试图隐藏我对它的喜爱。从该功能可用那一刻起,我就将我本地 Subversion 版本控制的项目从大约 30 个分散的、独立的 trunk 和分支工作副本的混乱状态,重新组织成每个项目一个工作副本,根目录位于项目的根目录,并从那里稀疏填充。

说实话,对于我来说,最直接的益处(至少对我来说)是一个软性的益处——一种无形的改进,不是针对 Subversion 为我做了什么,而是针对我在 Subversion 操作之间所做的事情。我的工作区变得更加井井有条。我花更少的时间去尝试记住哪个工作副本包含了什么。我能够通过运行一个覆盖所有这些区域的单一更新,更好地了解我关心的项目树部分中发生的事情。这些不是你可以用基准测试工具或回归测试套件来衡量的东西——它们是通过我原本杂乱无章的大脑中暴露的表面积的百分比来衡量的。

Subversion 开发人员在 Subversion 1.5 中首次推出了稀疏检出,并在随后的版本中对其进行了改进。例如,在 Subversion 1.6 中,可以排除——也就是“缩回”——现有的工作副本成员。现在,你不再需要废弃和重建包含不再感兴趣的子树的工作副本。Subversion 的合并和合并跟踪功能(后者也是在 Subversion 1.5 中引入的)也得到了改进,以便它们在涉及稀疏填充的工作副本时按预期运行。

Subversion 1.7 为 Subversion 整体带来了另一个有用的改进。对工作副本管理区域的重新设计——包括其存储机制和编程接口——部分是为了帮助激发新的功能开发。WC-NG(新工作副本概念的名称)是对工作副本元数据组织和访问方式的重大重写,正如预期的那样,它的几个特性引起了我的注意。首先,WC-NG 将所有缓存的原始文本基础副本(你的版本化文件)移动到一个单一位置,并根据它们的 SHA-1 校验和对其进行索引。为了优化磁盘使用,如果存在相同内容的重复副本,则只保留一个副本。我可能在 Subversion 的存储库和存储库访问功能方面比其客户端行为做了更多的开发,所以这里存在着一种对我来说很明显的对称性:Subversion 存储库也使用基于 SHA-1 校验和的索引来跟踪文件内容,并且也尝试只保留给定文件内容“表示”的单个副本。其次,WC-NG 不会抢先清除不再严格需要的原始文本基础(因为它们与当前工作副本状态无关)。相反,这些原始版本会随着时间的推移而累积,用户可以运行 `svn cleanup` 来强制 Subversion 将存储的原始文本基础与当前工作副本状态进行协调,并丢弃不必要的文本基础。

在 Subversion 1.7 发布周期结束时,我意识到了一个机会,我教 Subversion 的 mod_dav_svn Apache 服务器模块向请求更新(或检出、切换、合并、差异等)的客户端提供它推荐或提供给客户端的任何文件内容的 SHA-1 校验和。(服务器从 Subversion 1.0 之前就开始传输此内容的 MD5 校验和,但我们一直试图作为一个项目转换为 SHA-1。)我不确定客户端会用这些信息做什么,但对我和其他意识到同样问题的 Subversion 开发人员来说,它似乎很有价值。

在 Subversion 1.7 发布之后,我终于能够填补这个空白。Subversion 1.8 预计将使用单个 HTTP 仓库访问模块 libsvn_ra_serf 发布。Serf 处理 HTTP 通信的方式不同于我们之前基于 Neon 的方式,它更倾向于使用许多小的、流水线化的请求,而不是更少的、同步的、整体的请求。使用 libsvn_ra_serf 执行的检出和更新会要求服务器提供一个更新报告,其中包含客户端需要从服务器获取的项目列表——可以理解为一个购物清单——以便完成更新过程。在 Subversion 1.7 中,我已经教会服务器在购物清单中包含每个所需项目的 SHA-1 校验和(如果继续使用这个比喻,就是 UPC 代码)。现在,在 Subversion 主干(又名“1.8-dev”)代码库中,我已经教会 libsvn_ra_serf 使用这个 UPC 代码,询问 WC-NG 是否有包含该代码的“原始食品储藏室”中的文件,如果有,则直接从本地原始存储中获取文件,而不是再次向服务器请求该文件的副本。(在我最初的实现之后,我还教会了 libsvn_ra_serf 以相同的方式处理 MD5 校验和,有效地允许这种优化即使针对最旧的支持的 Subversion 服务器也能正常工作。)

这一切意味着什么?速度。

当使用 Subversion 1.8 客户端通过 HTTP 进行通信时,某些情况下您可能需要下载更少的网络文件,甚至可能不需要下载任何文件。例如,如果您暂时将工作副本回溯到较旧的版本,然后再次更新到您之前所在的版本,Subversion 不需要通过网络获取任何文件的內容。除非您在回溯之后运行了 `svn cleanup`,否则您的工作副本管理区域仍然保留着该较新版本中文件的原始版本。同样,如果有人提交了您本地工作副本中文件的重命名操作,Subversion 不需要再次通过网络获取移动文件的內容——您本地原始文本库存储中已经存在该內容的副本。

现在,我开始这篇文章,喋喋不休地谈论稀疏检出。为什么?有什么联系?因为我认为,正是由于稀疏检出,Subversion 行为的这一小小的改变才有可能大放异彩。由于原始数据存储是针对每个工作副本的,因此可以推断,您的存储库树中使用它的部分越多,就越有可能在其中找到一些东西,否则这些东西将不得不通过网络获取。对于一个稀疏填充的工作副本,每当我需要“订阅”某个分支(例如,在我的项目中),Subversion 的新功能(即不获取我已经拥有的文件内容)就可以使用整个现有的原始存储。Subversion 只需要获取该分支中在我的工作副本中任何其他分支或标签上没有完全副本的任何文件。这与专门用于单个分支的全新检出工作副本非常不同,在该副本中,原始存储在检出之前是完全空的,迫使 Subversion 下载检出集中所有文件。更重要的是,如果我需要排除一个分支或其他特定子树,然后稍后恢复它,那么恢复过程就可以访问该子树在排除之前的原始内容,以及工作副本中其他所有项目的原始内容,以便在诉诸网络传输之前从中提取数据。

以一个相对简单但常见的场景为例。您正在处理项目的 trunk,您决定需要创建一个分支,并进行一些更隔离的开发,但您仍然希望跟踪 trunk。在过去,大多数人会

  1. 创建分支,可能使用 svn copy URL NEW-URL
  2. 检出一个新的分支工作副本,当所有这些文件通过网络传输时,打个哈欠。(熟练的 Subversion 用户会通过制作工作副本目录的本地操作系统级副本,然后使用 svn switch 使该副本跟踪他们的新分支来缩短这个过程。)
  3. 开始在分支上工作。
  4. 偶尔将分支与 trunk 上发生的任何更改同步,所有文件增量将再次通过网络传输。

使用稀疏检出和原始存储来源改进,事情运行起来类似,但网络流量减少了。

  1. 创建分支,可能使用 svn copy URL NEW-URL
  2. 更新以将新分支包含在稀疏填充的工作副本中。新分支的内容与主干完全匹配,因此根本没有文件内容通过网络传输!
  3. 开始在分支上工作。
  4. 偶尔将分支与主干上发生的任何更改同步,但现在仅针对分支上更改的文件传输文件增量。

当然,Subversion 仍然使用与以前相同的基于路径的方法来访问权限。并非因为用户可能能够读取给定分支上的文件内容,他就现在被允许知道相同的文件存在于存储库的某些其他不可读区域。我所做的只是帮助客户端避免重新获取它已经在本地原始缓存中持有的文件内容。

随着 Apache Subversion 社区开始逐步结束对 Subversion 1.8 的开发活动,我鼓励您查看一下该项目的公共材料(例如我们的 路线图页面1.8 版本说明,这两者都在进行中),看看是否有您感兴趣的功能或增强功能。也许您可以帮助我们测试一些新功能,并确保当它在正式发布中问世时,它能达到最佳状态。

关于作者

C. Michael Pilato 是 Subversion 的核心开发人员,是《版本控制与 Subversion》(O'Reilly Media)的合著者,也是 ViewVC 的主要维护者。他从北卡罗来纳州的家中远程工作,担任 CollabNet 的软件工程师,并且从 2001 年初开始就是一名活跃的开源开发人员。Mike 是一位自豪的丈夫和父亲,他热爱旅行、足球、与家人共度美好时光以及这些事情的任何组合。他还喜欢作曲和表演音乐,并且怀有对摇滚明星的秘密幻想。Mike 拥有北卡罗来纳大学夏洛特分校的计算机科学和数学学位。