Fossil SCM同步协议使用CRDT

2020-11-28 14:34:10

以下是自2019年6月30日起的已知实用名称:send-private send-private实用指令指示服务器将其所有私有工件发送到客户端。如果用户具有“ x”或“私人”特权,则服务器将仅服从该请求。

send-catalog send-catalog指示命令服务器为每个已知工件传输igotcard。这可以帮助客户端和服务器在先前的协议错误后恢复到同步状态。化石同步命令的“ --very”选项将导致发送目录编译指示。

uv-hash哈希uv-hash编译指示从客户端发送到服务器,以引起未版本化内容的异步。哈希是客户端上所有未转换文件的名称,修改时间和单个哈希的SHA1哈希。如果来自客户端的未版本化内容哈希值与服务器上的未版本化内容哈希值不匹配,则服务器将通过“ pragma uv-push-ok”或“ pragma uv-pull-only”卡后跟一个“ uvigot”卡进行回复当前保存在服务器上的foreach未版本化文件。响应“ uv-hash”编译指示而发送的“ uvigot”卡的集合称为“未版本化目录”。客户端将使用unversioned目录,以确定客户端和服务器之间需要同步哪些文件(如果有),并在下一个HTTP请求上发送适当的“ uvfile”或“ uvgimme”卡。

如果客户端发送uv散列杂项并且没有收到uv-pull-only或uv-push-ok杂项,则意味着服务器上的内容与客户端上的内容完全匹配,并且不需要进一步的同步。

仅uv-pull服务器响应于内容哈希参数不匹配的uv-hash语法,服务器将uv-pull-pragma发送给客户端。此实用程序指示客户端和服务器之间的未版本化内容存在差异,但是只能将内容从服务器传输到客户端。服务器不愿接受来自客户端的内容,因为客户端登录缺少“未写版本”权限。

uv-push-ok服务器将uv-push-ok编译指示发送给客户端,以响应内容哈希参数不匹配的uv-hash编译指示。此实用程序指示客户端和服务器之间的未版本化内容存在差异,并且内容只能沿任一方向传输。服务器愿意接受来自客户端的内容,因为客户端登录名具有“未写版本”权限。

客户端将“ ci-lock”编译指示发送到服务器,以指示它将作为CHECKIN-HASH签入的子级并在与CHECKIN-HASH相同的分支上添加新的签入。已经表明它也在尝试对CHECKIN-HASH进行提交,这表明将要发生分支,服务器将以“ ci-lock-fail”编译指示答复(请参阅下文)。当实际发生入住时或在超时之后(当前为24小时,但可能会更改),入住锁将自动过期。

当服务器从同一客户端但从不同客户端收到两个或更多个“ ci-lock”编译指示消息时,第二个后续ci-lock将在答复中引发ci-lock-fail编译指示,以使客户端知道它是否继续签入时可能会产生一个叉子。 LOGIN和MTIME参数旨在向客户端提供信息,以帮助客户端生成更有用的错误消息。

成功提交后,客户端会将“ ci-unlock”编译指示发送到服务器。这指示服务器释放对该客户端先前持有的任何签入的任何锁定。ci-unlock编译指示有助于避免如果中止签入然后在分支上重新启动时可能出现的假阳性锁定警告。

任何以“#”(ASCII 0x23)开头的卡都是注释卡,并且会被忽略。

如果服务器发现请求有任何问题,则会在其回复中生成一个错误卡。当客户端看到错误卡时,它将向用户显示错误消息并中止同步操作。错误卡如下所示:

错误消息是英语文本,被编码为单个令牌。空格(ASCII 0x20)表示为“ \ s”(ASCII 0x5C,0x73)。换行符(ASCII 0x0a)为“ \ n”(ASCII 0x6C,x6E)。反斜杠(ASCII 0x5C)表示为两个反斜杠“ \\”。除空格和换行符外,错误消息中不允许其他空格字符或任何不可打印的字符。

服务器还可以发送消息卡,该消息卡还可以在客户端控制台上打印消息,但这不是错误:消息文本使用与错误消息相同的格式。如果客户端或服务器看到以上未描述的卡,则它将生成错误并中止。

当存储库知道工件存在并且知道该工件的ID,但不知道工件内容时,则将该工件存储为“幻像”。存储库通常会在收到其不持有的工件的仿卡或接收到引用不持有的增量源的文件卡时创建幻像。当服务器生成其答复或客户端生成新请求时,通常会为其所持有的每个幻像发送gimme卡。

簇是一种特殊的人工制品,它可以说明其他人工制品的存在。存储库中遵循集群句法规则的任何工件都被视为集群。

集群是面向行的。集群的每一行都是一张卡。这些卡用换行符(“ \ n”)分隔。每张卡均由单个字符卡类型,空格和单个参数组成。不允许有多余的空格,也不允许尾随或前导空格。群集中的所有卡必须按照严格的字典顺序出现。

群集由一个或多个“ M”卡,然后是单个“ Z”卡组成。每个M卡都有一个参数,该参数是存储库中伪影的伪影ID。 Z卡具有一个自变量,它是之前所有M卡的MD5校验和的小写十六进制表示,直到并包括在启动Z卡的Z之前发生的换行符。

完全不符合集群规范的任何工件都不是集群。工件中必须没有多余的空格。必须有一张或多张M卡。必须有正确的MD5校验和的单个Z卡。并且所有卡片必须严格按照字典顺序排列。

每个存储库都维护一个名为“未聚簇”的表,该表记录了集群中未提及的每个工件和幻影的身份。可以将未聚簇表中的条目视为人工制品上的叶子。某些非集群工件将是其他集群。这些群集可能包含其他群集,其中可能包含更多群集,依此类推。从未聚簇表中的工件开始,可以遵循集群链来查找存储库中的每个工件。

典型的拉动操作如下所示。实际实现的细节可能会非常少,但是在以下步骤中可以了解到要点:

如果用户没有拉权限,服务器将检查登录密码并拒绝会话。

如果服务器上非集群表中的条目数大于100,则服务器将构造一个新的集群工件以覆盖所有那些非集群条目。

服务器为它的非幻像表(不是幻像)中的每个工件发送Igot卡。

客户端会为服务器答复中提到自己不具备的工件的每个智能卡创建一个幻像。

当增量源是客户端不具备的工件时,客户端会为文件卡的增量源创建幻像。

这十个步骤代表一个HTTP往返请求。前三个步骤是在客户端上生成请求的处理。中间的四个步骤是在服务器上进行的处理,以解释请求并生成区域。最后三个步骤是客户端对回复进行解释的过程。

在拉动期间,客户端将继续发送HTTP请求,直到它保留服务器上存在的所有工件。

请注意,服务器尝试将其回复消息的大小限制在合理的范围内(通常约为1MB),以便如果回复太大,它可能会停止发送文件卡,如步骤(6)所述。

步骤(5)是创建新集群的唯一方法。通过仅在服务器上创建集群,我们希望在具有单个服务器和许多客户端的通用配置中,最大程度地减少集群之间的重叠量。即使有多个服务器,或者服务器和客户端有时会更改角色,相同的同步协议也将继续起作用。这些异常安排的唯一负面影响是,可能会生成超过最小数量的群集。

典型的推动操作大致如下进行。随着拉动,实际的实现可能会略有不同。

客户端会发送文件卡,以获取从未保存过的任何工件(即来自本地签入的工件)。

如果这是推送中的第二个或更晚的周期,则客户端将为服务器在上一个周期中发送的所有gimme卡发送文件卡。

客户端会为非幻像表中的非伪造表中的每个工件发送Igot卡。

如果出现任何问题,服务器将检查登录卡和推送卡并发出错误。

服务器为提及它不拥有的工件的Igot卡或提及它不拥有的增量源工件的文件卡创建幻像。

客户端会记住服务器中的gimme卡,以便它可以在下一个周期生成文件卡作为答复。

与拉取一样,重复推入操作的步骤,直到服务器知道客户端上存在的所有工件为止。同样,与拉时一样,一旦请求的大小达到1MB,客户端就会通过抑制文件卡来阻止请求的大小过大。

同步只是同时发生的一次拉动和一次推动。一次拉动的前三个步骤与一次推压的前五个步骤结合在一起。拉动的步骤(4)至(7)与推入的步骤(5)至(8)组合在一起。然后将拉杆的步骤(8)到(10)与推入的步骤(9)相结合。

“未版本化的文件”是存储在存储库中的文件,仅保留文件的最新版本,而不是整个更改历史记录。未经版本控制的文件旨在用于存储临时内容,例如最新发行版的已编译二进制文件。未版本控制的文件通过名称和时间戳(mtime)进行标识。仅保留每个文件的最新版本(具有最大mtime值的版本)。使用化石unversioned sync命令同步未版本化的文件。未版本控制的文件同步的示意图如下:客户端向服务器发送“ pragma uv-hash”卡。 uv-hash pragma的参数是客户端持有的所有未版本化文件的所有文件名,mtimes和内容哈希的哈希。

如果来自客户端的未版本化内容哈希与服务器上的未版本化内容哈希匹配,则无需执行任何操作,并且服务器不操作。但是,如果哈希值不同,则服务器将对仅保留uv-pull或uv-push-ok编译指示进行响应,然后对存储在服务器上的所有未版本控制的文件进行uvigot卡答复。

客户端检查从服务器收到的uvigot卡,并确定需要交换哪些未版本控制的文件才能使客户端和服务器同步。然后,客户端将适当的“ uvgimme”或“ uvfile”卡发送回服务器。

服务器使用收到的“ uvfile”卡更新其未版本控制的文件存储,并在答复中用“ uvfile”卡答复“ uvgimme”卡。

如果要传输的未版本化内容比单个HTTP请求中所能容纳的内容多,那么最后两个步骤可能会重复多次。客户端向服务器发送一个或多个PUSH HTTP请求。请求和答复内容类型为“ application / x-fossil”。

存储库会跟踪anycluster中未命名的所有工件,并为这些工件发送igot消息。

存储库会跟踪它们持有的所有幻像,并为这些工件发送消息。