文字记录 · CC BY 3.0

MIT 6.824 讲座 6:Raft与容错,第1部分

本页标题、字幕、摘要和阅读笔记为已保存的 AI 译文,可能有误。时间戳与来源一致,可展开查看原始字幕,并对照原视频核对。

MIT 6.824 关于Raft的第1部分:为什么早期系统依赖单一主节点、多数投票如何防止脑裂,以及任期、选举和日志如何运作。

MIT 6.824:分布式系统 · 发布于 · 1小时20分钟 · 英语 · 许可证:CC BY 3.0 · 来源:在YouTube上观看

转录来源:Vidleaf上的自动语音识别(可能包含错误)。AI标点由Vidleaf添加;保留原始措辞和时间戳锚点。原始转录可在下方获取。

AI生成的摘要

本讲座介绍Raft和单主模式,部分是为了支持实验课,同时也是作为正确状态机复制的一个案例研究。它指出,MapReduce、GFS和VMware FT都依赖单一实体来决定主节点,该实体不会与自己产生分歧,但本身也是单点故障,因此核心问题在于避免脑裂。一个复制的test-and-set服务器展示了其中的困难:要求客户端与两个副本都通信并不具备容错性,而只与其中一个通信则会让网络分区产生两个都认为自己持有锁的客户端。自动故障转移则改用多数投票,只有获得所有服务器中的多数才能取得进展,因此最多只有一个分区能够取得进展,而在2f+1台服务器的情况下,系统可容忍f个故障;多数重叠是Raft正确性的重要原因之一。讲座将Paxos和视图标记复制(VSR)归为早期共识系统,指出Raft的设计更接近VSR,并将Raft描述为包含在服务应用中的一个库,应用代码位于Raft层之上,Raft层维护日志,三个副本结构完全相同,外部客户端向当前领导者的应用层发送put和get等请求。接着它解释了请求如何被复制,直到多数副本将其写入日志,之后领导者执行该请求并回复,领导者在下一个append entries RPC中捎带提交索引;它涵盖了Raft为何保留日志、流控缺口、崩溃恢复、apply通道接口、带任期编号且每个任期最多一个领导者的领导者选举、分裂投票和计时器随机化,以及领导者在尴尬时刻崩溃的边界情况,包括可能尚未提交的条目必须被视为已提交。

根据文字记录生成。请对照原始视频检查链接段落。

带时间戳的章节摘要

Raft与单主模式

  • [0:07] 今天和明天的主题是Raft,部分是为了帮助实现实验课,同时也是作为正确实现状态机复制的一个案例研究。
  • [0:25] 到目前为止看到的容错系统都有一个共同模式:MapReduce复制计算,但整个计算由单一主节点控制。
  • [0:49] GFS通过主备方案复制数据,但依赖单一主节点为每一份数据选择主节点。
  • [1:06] VMware FT在主虚拟机和备份虚拟机上复制计算,但依赖单一test-and-set服务器来确保故障时恰好只有一个接管。
  • [1:27] 在这三种情况下,都有一个单一实体被藏在角落里,做出谁是主节点的关键决定。
  • [1:46] 由单一实体决定主节点的好处是它不会与自己产生分歧,但坏处是它本身就是单点故障。
  • [2:23] 关键在于如何避免脑裂,这就是为什么在故障后决定谁应成为主节点需要极其谨慎。

复制的test-and-set脑裂

  • [2:50] 一个复制的test-and-set服务器被用来说明为什么脑裂很难正确处理,其动机是VMware FT依赖test-and-set服务器来选择主节点。
  • [3:34] 两个副本都从状态零开始,唯一的客户端操作是测试并设置,它将两个副本都设为一并返回旧值,充当一个简化的锁服务器。
  • [4:04] 当客户端能与一个服务器通信但无法与另一个通信时,就会出现脑裂,因此系统必须决定当某个服务器似乎无响应时该如何反应。
  • [4:47] 要求客户端对任何操作都与两个副本通信可以避免不一致,但完全不具备容错性,因为两台服务器都必须存活。
  • [5:53] 让客户端只与它能连通的服务器通信同样是错误的,因为由于网络问题,那台无法连通的服务器实际上可能仍然存活。
  • [6:19] 如果一根网线断裂将网络一分为二,客户端一能连通服务器一但无法连通服务器二,而另一个客户端能连通服务器二但无法连通服务器一。
  • [6:49] 在“只与一台通信”规则下,客户端一的 test-and-set 将服务器一置为一并返回零,于是客户端一认为自己持有锁,并认为那台服务器可以是 VMware FT 的主副本,而另一个副本仍然保持为零。
  • [7:07] 随后客户端二也通过服务器二获取了锁,于是两台 VMware 机器都可能认为自己是主副本,而无需咨询另一台服务器,这是彻底的失败。

双服务器权衡与脑裂

  • [7:43] 在这种设置下且只有两台服务器时,选择要么是等待两者(不具备容错性),要么是只等待一台(不正确)。
  • [7:56] 不正确的那个版本通常被称为脑裂。

早期复制与避免脑裂

  • [8:15] 电话交换机和银行计算机的运营者愿意在复制系统上投入巨资,以获得可靠的服务。
  • [8:34] 他们使用了几种技术来实现复制,同时试图排除脑裂。
  • [8:40] 其中一种技术是构建一个不会发生故障的网络,例如笔记本电脑内部连接 CPU 与 DRAM 的线路。
  • [9:03] 在合理的假设、大量资金以及精心控制的物理环境下,一个不会发生故障的网络可以排除脑裂。
  • [9:24] 如果网络不会发生故障,那么客户端无法与服务器二通信就意味着服务器二必定已宕机。
  • [9:42] 另一种可能性是让人来解决问题,客户端默认等待两个副本都响应。
  • [9:57] 可以呼叫人员到机房关闭一个副本,或核实一个已崩溃而另一个仍存活,由人充当仲裁者。
  • [10:24] 人们长期使用这两种方案中的一种;人的响应速度并不快,而不会故障的网络虽然昂贵但可行。

多数投票与法定人数系统

  • [10:41] 自动故障转移系统可以在网络不稳定、可能发生故障和分区的情况下正确工作。
  • [11:03] 构建避免脑裂的自动复制系统的关键洞见是多数投票的思想。
  • [11:29] 第一步是使用奇数台服务器,因为偶数分裂过于对称,双方会做同样的事情。
  • [12:06] 在 Raft 中,取得任何进展(选举领导者或提交日志条目)都需要聚集超过所有服务器半数的多数。
  • [12:41] 这之所以有效,是因为最多只有一个分区能包含多数服务器,因此另一个分区无法取得进展。
  • [13:08] 多数始终是按所有服务器来计算的,而不仅仅是存活服务器。
  • [13:53] 有 2f+1 台服务器时,系统可以承受 f 个故障;三台服务器可以容忍一个故障。
  • [14:20] 这些通常被称为法定人数系统,因为所需的多数有时被称为法定人数(quorum)。
  • [15:04] 任意两个多数至少在一台服务器上重叠,Raft 依赖这种重叠来避免脑裂。

多数重叠确保 Wrapped 的正确性

  • [15:50] 前任领导者可能已提交的任何内容都必须存在于 Wrapped 中多数服务器上。
  • [15:54] 任何新领导者的多数必须至少与一台持有前任领导者所有已提交条目的服务器重叠。
  • [16:05] 这种多数重叠是 Wrapped 正确性的重要原因之一。
  • [16:14] 随后演讲者邀请大家就多数投票系统的一般概念提问。

增加服务器与早期共识系统

  • [16:30] 可以增加或更改服务器集合,这可能在第六节中涉及。
  • [16:43] 长期运行的系统在一段时间后需要更换服务器,原因包括永久性故障、升级或迁移机房。
  • [17:00] 更改服务器集合并非每天都会发生,但对长期可维护性至关重要。
  • [17:07] Raft 的作者们认为自己提出了一种处理此问题的方案,该方案很复杂。
  • [17:21] 大约在 1990,有两个系统被提出,它们使用三台服务器而非两台进行多数投票,以绕过脑裂。
  • [17:45] 一个早期系统叫做 Paxos,Raft 论文对此有大量讨论。
  • [17:53] 另一个早期系统是视图戳复制(view stamp replication,VSR),由麻省理工学院的人发明;Raft 的设计比 Paxos 更接近 VSR。
  • [18:16] 这些系统已有数十年的历史,但大约 15 年前才在部署的大型分布式系统中崭露头角。

Raft 作为库

  • [18:30] 讨论 Raft 的时间大约是在它最初被发明 15 年之后。
  • [18:38] 演讲者转而谈论 Raft。
  • [18:43] Raft 是一个库,旨在被包含在某个服务应用程序中。

Raft 复制服务架构

  • [18:50] 一个复制服务由接收 RPC 的应用程序代码加上一个 Raft 库组成,各个 Raft 库协同工作以维持复制。
  • [19:11] 单个 Raft 副本上层是应用程序代码(例如一个键值服务器),其复制状态由 Raft 帮助管理,例如一张键值表。
  • [19:38] 应用程序之下是 Raft 层;键值服务器向 Raft 发起函数调用,Raft 维护状态,最关键的是操作日志。
  • [20:06] 在一个有三副本的系统中,有三台结构相同的服务器,并且希望两层的数据都相同。
  • [20:33] 客户端是向当前领导者的应用层发送请求的外部代码,目标是客户端无需知道自己正在与一个复制服务通信。
  • [21:13] 键值服务器在应用层的请求可以是 put(接收一个键和一个值并更新表)和 get(返回某个键的当前值)。
  • [21:55] 在 Raft 复制的服务中,应用层不执行请求;它将客户端的请求向下发送到 Raft 层,使其提交到复制日志中。
  • [22:16] Raft 复制该操作,直到大多数副本将其记录在日志中,然后通知领导者,领导者再执行该操作并回复客户端。
  • [23:36] 提交需要多数副本而非全部副本,这样即使某些服务器发生故障,容错系统也能继续推进。
  • [24:10] 当操作被提交时,每个副本的 Raft 库将其向上发送到本地应用层,应用层将其应用到自己的状态,从而使所有副本看到相同的操作流。

Raft 正常运行与提交传播

  • [24:33] 按相同顺序应用确定性操作,使复制状态在所有副本上以相同方式演进。
  • [25:09] 时间图显示客户端向领导者(服务器一)发送请求,领导者向两个副本发送追加条目 RPC。
  • [25:55] 领导者等待包括自身在内的多数副本的肯定回复;在三个副本的情况下,它只需要另一个副本。
  • [26:14] 在凑齐多数副本后,领导者执行命令并将回复发送回客户端。
  • [27:04] 服务器二不知道请求已被提交,因为它的回复可能已被网络丢弃。
  • [27:26] 一旦领导者知道请求已提交,它必须告知其他副本;在 Raft 中,这是通过下一个追加条目 RPC 中的领导者提交字段捎带完成的,而不是通过显式的提交消息。
  • [29:14] 实现者决定领导者何时发送更新的提交索引:如果请求稀少,则通过心跳或特殊的追加条目;如果请求频繁,则捎带发送。
  • [29:56] 副本执行请求的时间并不关键,因为没有人等待它,至少在没有故障的情况下是如此。

副本执行不在关键路径上

  • [30:08] 副本执行请求实际上并不在关键路径上。
  • [30:12] 客户端只等待领导者执行,而不等待副本,因此副本执行可能并不那么重要。
  • [30:20] 这如何分阶段进行可能不会影响客户端感知的延迟。

为什么 Raft 保留日志及其流控缺口

  • [30:43] 明确询问为什么系统如此关注日志以及日志在做什么。
  • [31:03] 日志是领导者对操作进行排序的机制,这对于复制状态机至关重要。
  • [31:07] 副本不仅必须应用相同的客户端操作,还必须以相同的顺序应用相同的操作。
  • [31:41] 日志中编号的槽位是领导者表达其所选顺序的方式的一部分。
  • [32:06] 在跟随者上,日志是存放尚未确认提交的操作的地方,这些操作可能不得不被丢弃。
  • [32:32] 领导者将操作保留在日志中,因为它可能需要将它们重新传输给错过消息的跟随者。
  • [33:28] 重启的服务器会重放持久化到磁盘的日志,以重建其崩溃时的状态,因此日志是持久化计划的一部分。
  • [34:41] 跟随者在执行命令之前就确认命令,因此它们的确认速率不受执行速率的限制。
  • [34:56] 如果跟随者确认的速度快于执行的速度,它们会积累无界的日志,最终将耗尽内存。
  • [35:26] Raft 没有应对这种情况所需的流控机制。
  • [35:44] 生产系统可能需要一条额外的消息来报告执行进度,以便在领导者过于超前时对其进行节流。

Raft 中的崩溃恢复与日志提交

  • [36:37] 崩溃的服务器已根据图 2 中的一条规则将其日志持久化到磁盘。
  • [36:46] 重启后,服务器不知道它在执行日志方面进展到了哪里。
  • [36:54] 根据图 2 的规则,在首次重启时,它甚至不知道日志中有多少已被提交。
  • [37:03] 在重启并读取日志后,服务器立即不被允许对日志做任何操作,因为它不知道系统已提交到何处。
  • [37:36] 如果所有服务器都崩溃,它们所拥有的只是图 2 中标记为非易失性的状态,其中包括日志,可能还包括最新的任期。
  • [37:56] 它们运行领导者选举,其中一个被选为领导者,领导者通过发送第一个心跳作为副产品来确定状态。

领导者提交点与日志复制

  • [38:26] 多数副本对其日志达成一致即为提交点。
  • [38:33] 一旦通过追加条目机制选出领导者,它便强制所有其他副本拥有与其自身相同的日志。
  • [38:45] 由于领导者知道所有副本的日志与其相同,多数副本必定持有这些条目,因此它们也必定已被提交。
  • [39:13] 图二中的领导者追加条目代码会增加领导者的提交点。
  • [39:18] 然后每个人都可以从头开始执行整个日志,并从头重建其状态,这可能极其费力。
  • [39:30] 从头重新执行并不很有吸引力,但这就是基本协议所做的。

高效版本与接口预览

  • [39:42] 一个更高效的版本使用检查点,这将在明天讨论。
  • [39:49] 前面的序列是在普通的、无故障运行中的情况。
  • [39:56] 演讲者将简要提及接口的样子,听众可能已经部分看到过。

Raft 与键值层接口

  • [40:14] 在每个副本上,键值层和 Raft 层之间的接口有两个主要部分。
  • [40:22] 键值层通过 start 函数将客户端请求转发给 Raft,要求其将请求放入日志中。
  • [40:36] raft.go 中的 start 函数接受一个参数,即客户端命令,并在命令被提交时告知键值层。
  • [40:53] Raft 通知键值层,先前在 start 命令中发送的操作已被提交,这可能不是最近的 start。
  • [41:02] 可能有一百个客户端命令进来,并在其中任何一个被提交之前就调用了 start。
  • [41:13] 向上通信的形式是 Go 通道上的消息,Raft 库发送消息,键值层从中读取。
  • [41:27] apply 通道承载 apply 消息,键值层必须将其与其对 start 的调用相匹配。
  • [41:48] start 命令返回命令将被提交的日志索引(如果被提交的话,也可能不被提交),以及当前任期。
  • [42:13] apply 消息包含命令和索引,所有副本都会收到它,并将命令应用到其本地状态。
  • [42:36] 索引实际上只在领导者上有用,这样它可以弄清楚正在谈论的是哪些客户端请求。

复制键值 RPC 中的客户端回复时机

  • [43:14] 客户端发送请求(put 或 get)并等待响应。
  • [43:31] 领导者只有在知道命令已提交后才发送响应。
  • [43:55] 在软件栈中,键值层调用 start 函数,该函数返回到键值层。
  • [44:05] 键值层尚未回复客户端,因为它不知道请求是否会被提交。
  • [44:18] 如果键值层在 start 返回后立即崩溃,在发送 apply 消息之前,请求可能不会被提交。
  • [44:38] 键值服务器仅在 apply 通道上出现相应的 apply 消息时才执行请求,然后发送回复。
  • [45:01] 这主要在故障情况下重要:当故障发生时,客户端看到的内容变得极其重要。

Raft 日志的分歧与收敛

  • [45:22] 最初,不同副本的日志可能不完全相同。
  • [45:30] 在几种情况下,副本日志可能会短暂分歧。
  • [45:40] 一个例子:领导者发送了一轮追加消息,但在发送所有消息之前崩溃了。
  • [45:45] 收到追加消息的副本会追加新的日志条目,而未收到 RPC 的副本则不会。
  • [46:01] 尽管存在暂时差异,Raft 最终会强制日志变得完全相同。
  • [46:11] 领导者修改日志,直到确保它们全部相同。

Raft 领导者选举基础

  • [46:27] Raft 有两个大主题:领导者选举(实验 2)以及领导者如何处理副本的日志,尤其是在故障之后。
  • [46:50] 领导者并非严格必需;无领导者的集群可以就日志序列达成一致,最初的 Paxos 没有领导者。
  • [47:17] Raft 使用领导者主要是为了在服务器不发生故障的常见情况下提高效率,使得每个请求可以在一轮消息中达成一致。
  • [47:49] 使用领导者可能使系统速度提高两倍,并使推理更容易。
  • [48:07] Raft 使用任期号来区分领导者;跟随者只需要当前任期号,而不需要领导者的身份。
  • [48:20] 关键属性:每个任期最多有一个领导者——可能没有,但同一任期内绝不会有兩個。
  • [48:45] 每个服务器维护一个选举计时器;如果计时器到期而没有收到当前领导者的消息,服务器就认为领导者已死并开始选举。
  • [49:31] 开始选举意味着增加任期并发送请求投票 RPC,候选人投票给自己(n-1 个请求)。
  • [50:16] 即使没有领导者故障,选举也可能发生,例如网络缓慢或心跳丢失,因此正确性必须考虑到这一点。
  • [50:46] 新选举后,旧领导者可能仍然认为自己是领导者,例如在少数派分区中,因此必须处理其行为。

Raft 中的单向网络故障

  • [51:53] 可能出现单向网络通信阻止进展的病态情况。
  • [51:55] 如果领导者的传出心跳正常工作,但无法接收客户端请求,其心跳会抑制其他服务器启动选举。
  • [52:20] 损坏的传入网络阻止领导者听到并执行客户端命令。
  • [52:28] Raft 并不能证明可以抵御所有可能出现的疯狂网络问题。
  • [52:37] 演讲者认为他考虑过的情况都是可以修复的。
  • [52:45] 一种修复方法是要求双向心跳,即跟随者回复心跳。
  • [52:58] 如果领导者不再看到心跳回复,经过一段时间后它决定下台。

Raft 多数规则领导者选举

  • [53:32] Raft 必须确保每个任期最多有一个领导者。
  • [53:38] 要在一个任期内当选,候选人需要获得多数服务器的赞成票。
  • [53:45] 每个服务器在每个任期只投一票赞成票,因此两个候选人不可能都获得多数票。
  • [54:08] 多数规则导致每个任期最多有一个获胜候选人。
  • [54:26] 多数规则意味着即使少数服务器崩溃或不可用,领导者仍然可以被选举出来。
  • [54:34] 如果超过一半的服务器崩溃、不可用或处于另一个分区,系统会不断重试,永远不会选举出领导者。
  • [54:54] 如果选举成功,如果每个人都能得知这一结果,那就太好了。

Raft 服务器如何获知选举结果

  • [55:00] 各方都需要一种方式来了解选举中发生了什么。
  • [55:16] 获胜的候选人直接知道自己获得了多数票,但其他任何人都不能直接知道谁赢了或是否有人赢了。
  • [55:28] 候选人通过心跳 AppendEntries 通知其他服务器,图 2 要求在赢得选举后立即执行此操作。
  • [55:41] 心跳并未明确说明谁赢了或哪个任期;这个信号很微妙。
  • [55:56] 只有某个任期的领导者才能发送 AppendEntries,因此收到任期 19 的 AppendEntries 会告诉服务器有人赢得了该次选举,尽管不知道是谁。
  • [56:21] AppendEntries 还会重置每个服务器的选举计时器,只要领导者以预期速率发送心跳,就能抑制新的候选资格。
  • [56:51] 如果网络故障或数据包丢失,选举仍可能发生,但如果一切顺利,选举就不太可能发生。
  • [57:03] 该方案无法为同一任期选出两个领导者,但可能为同一任期选出零个领导者,主要发生在太多服务器死亡、不可用或网络连接不良,导致无法凑齐多数派的情况下。

Raft 选举超时与分裂投票

  • [57:23] 即使没有数据包丢失,如果两个或多个候选人的超时时间接近并分裂了选票,选举也可能失败。
  • [57:44] 在一个三副本系统中,如果所有选举计时器同时触发,每个服务器都投票给自己,没有人获得多数票。
  • [58:09] 由于选举计时器只能通过追加条目来重置,在没有领导者的情况下,它们会再次触发,并可能反复分裂选票。
  • [58:31] Raft 通过随机化选举计时器使分裂投票变得不太可能,但并非不可能。
  • [59:43] 如果服务器选择不同的随机时间,一个计时器会先触发,如果间隔足够大,它可以在下一个计时器触发之前发送一整轮投票请求。
  • [1:00:23] 随机化使候选人不同步。
  • [1:00:41] 选举计时器必须至少与预期心跳间隔一样长;考虑到可能的数据包丢失,最小值应为心跳间隔的两倍左右,例如 100 毫秒心跳对应 300 毫秒。
  • [1:02:00] 最大随机超时影响系统从故障中恢复的速度,因为在第一个选举计时器触发之前,系统会因没有领导者而冻结。

选举超时调优与随机化

  • [1:02:36] 所需的性能水平和预期的故障频率决定了超时设置的重要性。
  • [1:02:43] 如果故障每年只发生一次,它们可能不太重要;频繁的故障则使它们非常重要。
  • [1:02:50] 恢复时间也是一个考虑因素。
  • [1:03:04] 第一个和第二个计时器触发之间的间隔必须长于候选人从所有人那里收集选票所需的时间。
  • [1:03:12] 该间隔必须超过 RPC 的预期往返时间,后者可能约为 10 毫秒。
  • [1:03:25] 最大超时必须足够长,使得最小和次小随机数之间出现 10 毫秒差异的可能性相当大。
  • [1:03:53] 如果领导者故障未能在几秒内恢复,测试代码会报错,因此应调低最大值,使领导者选举极有可能在几秒内完成。
  • [1:04:20] 每次选举计时器重置时都应选择新的随机超时,而不是重用服务器创建时选择的超时。
  • [1:04:39] 重用相同的随机数可能导致两个服务器选择相同的值,并永远反复导致分裂投票。

少数派分区中的旧领导者

  • [1:05:02] 最后一个领导者选举问题:旧领导者与少数服务器和几个客户端被分区,而多数派选举出新领导者。
  • [1:06:08] 一个非问题:另一个分区中没有多数派的领导者发送追加条目,但无法获得多数派的响应,因此它永远不会提交或执行操作。
  • [1:06:47] 客户端可能向旧服务器发送请求但永远得不到响应,因此没有客户端会被欺骗而认为它执行了任何操作。
  • [1:07:00] 一个更棘手的问题:服务器可能向一部分服务器发送追加条目,然后在做出提交决定之前崩溃。
  • [1:07:24] 这个问题可能需要大约 45 分钟来讨论。

领导者崩溃后的日志分歧

  • [1:07:47] 新当选的领导者可能需要协调各副本之间的分歧日志,以在前任领导者崩溃后恢复一致状态。
  • [1:08:16] 这个话题主要在服务器崩溃后才有意思;当服务器正常运行且拥有多数派时,跟随者必须接受领导者的日志,几乎不会出错。
  • [1:08:50] 当旧领导者在发送消息中途崩溃,或者新领导者在选举后刚上任还未做任何有用的事情就崩溃时,问题就会出现。
  • [1:09:48] 在图中,日志条目写为任期号而不是客户端操作,按槽位对齐。
  • [1:10:03] 在示例中,所有三个服务器在第一个槽位都有一个任期三的条目,但只有服务器 2 和 3 在第二个槽位有一个任期三的条目,而服务器 1 在那里什么都没有。
  • [1:11:03] 如果服务器 3 在任期三中是领导者,向所有人发送了一个命令,然后收到第二个请求并发送给所有三个服务器,但发给服务器 1 的消息丢失或服务器 1 宕机,就可能出现这种情况。
  • [1:11:53] 如果服务器 3 崩溃并选出另一个领导者,新领导者不得丢弃可能已提交的命令,并且必须让服务器 1 用其他服务器拥有的相同命令填充空白。

Raft 日志配置可行性

  • [1:12:28] 由于在发送消息之前发生了崩溃,可能会进行新的领导者选举。
  • [1:12:43] 日志中的槽位编号为 10、11、12、13,以便引用它们。
  • [1:13:03] 在槽位 12 中,服务器 2 有一条来自任期四的命令,服务器 3 有一条任期五的命令。
  • [1:13:17] 在分析服务器会做什么之前,必须先问这种配置是否可能发生。
  • [1:13:25] 有时答案是这种配置不可能出现,因此无需担心。
  • [1:15:00] 这种配置可能出现:如果服务器 2 在任期四被选为领导者,追加了一个客户端请求,然后崩溃了。
  • [1:15:25] 然后服务器 3 可能在任期五被选中,因为它只需要获得多数派的请求投票响应,即服务器 1 和服务器 3,它们的日志并不冲突。
  • [1:15:41] 然后服务器 3 收到一个客户端请求,将其追加到自己的日志中,并崩溃了,从而产生了这种配置。
  • [1:16:03] 需要推演这类场景,以确定 Raft 必须处理它们,还是它们不可能发生。

Raft 可接受的结果

  • [1:16:26] 我们想要说服自己 Raft 会做出合理的行为。
  • [1:16:42] 在讨论 Raft 会做什么之前,我们需要了解什么才是可接受的结果。
  • [1:16:53] 槽位 10 中的命令被所有副本所知,因此它可能已经被提交,不能被丢弃。
  • [1:17:01] 槽位 11 中的命令存在于多数副本中,因此它可能已经被提交,不能被丢弃。
  • [1:17:09] 槽位 12 中的命令不可能已经被提交,因此 Raft 有权将两者都丢弃。
  • [1:17:27] 丢弃槽位 12 中的任一命令是允许的丢弃,而非必须的丢弃。

Raft 日志提交的不确定性

  • [1:17:31] 至少必须丢弃一个日志条目,因为最终的日志内容必须完全相同。
  • [1:17:47] 仅凭日志无法揭示领导者在崩溃前究竟进行到了哪一步。
  • [1:17:58] 一种可能是领导者发送了包含新命令的追加消息后立即崩溃,从未收到响应。
  • [1:18:13] 没有响应,领导者就没有执行该命令,也没有发出递增后的提交索引,因此副本可能也没有执行它。
  • [1:18:28] 该条目有可能未被提交,但 Raft 无法证明它未被提交,因此必须将其视为已提交。
  • [1:18:57] 领导者可能已经收到了追加条目的回复,并在崩溃前回复了客户端,因此不能排除该条目已被提交。
  • [1:19:07] 从日志来看,两种可能性都无法排除,因此必须假定该条目已被提交。

简短的继续提示

[0:01] 好的,我们开始吧。嗯哼。今天,实际上今天和明天,我要讲的是 Raft。嗯,一方面是因为我希望这对你们实现实验有所帮助。另一方面也因为,你知道,这只是一个关于如何正确实现状态机复制的细节案例研究。那么,作为问题的引入,你们可能已经注意到我们迄今为止看过的容错系统中的一个模式。

[0:35] 嗯,一个是 MapReduce 复制计算,但复制是受控的,整个计算由单个主节点控制。嗯,我想引起你注意的另一个例子是 GFS,它复制数据,对吧,作为复制文件实际内容的主备方案,但它依赖于单个主节点来为每一份数据选择谁是主节点。另一个例子,VMware FT 在主虚拟机和备份虚拟机上复制计算,但为了确定下一步该做什么,如果其中一个似乎失败了,它依赖于单个测试并设置服务器。

[1:20] 来帮助它选择,帮助它确保如果发生某种故障,主节点或备份节点中恰好有一个接管。所以在这三个案例中,当然,都有复制系统,但在复制系统的一个角落里,藏着某种方案,需要单个实体做出关键决策,关于我们所关心的情况下谁是主节点。那么,我将从第一个开始。让单个实体决定谁将成为主节点的一个非常好的地方是什么?

[1:52] 是它不会与自己产生分歧。只有一个它,做出某个决定,那就是它做出的决定。嗯,但让单个实体决定,比如谁是主节点,不好的地方在于它本身就是一个单点故障。所以你可以把我们看过的这些系统看作是……把容错机制的核心推到了一个小角落。那就是在发生故障时决定谁将成为主节点的单个实体。现在,这整件事是关于如何避免脑裂的。我们之所以必须极其小心地做出关于在发生故障时谁应该成为主节点的决定,是因为否则我们就有脑裂的风险。

[2:35] 嗯,并且只是……把这一点说得非常清楚,我要提醒你们。问题是什么以及为什么它是一个严重的问题。那么,假设例如我们嗯,我们想为自己构建一个复制的测试并设置服务器。也就是说,我们担心 VMware FT 依赖于这个测试并设置服务器来选择谁是主节点,所以让我们构建一个复制的测试并设置服务器。我要这样做,它将会是坏的,这只是一个说明,说明为什么正确解决脑裂问题很困难。所以,你知道,我们将想象我们有一个网络,也许有两个服务器,它们应该是我们的测试并设置服务的副本,连接在一起,你知道,也许有两个客户端需要知道现在谁是主节点?或者实际上,也许在这种情况下这些客户端是主节点和备份节点。

[3:30] 嗯,在 VMware FT 中。所以,如果这是一个测试并设置服务,那么这两个数据库、这两台服务器一开始的状态都是零,也就是这个测试并设置标志位的状态为零。它们的客户端可以发送的唯一操作就是测试并设置操作,该操作应该将复制服务的值设为一,因此它应该设置两个副本。然后返回旧值。所以它本质上充当一种简化的锁服务器。嗯,谢谢。

[4:01] 好的。问题情况?我们担心的是,脑裂会自动出现。当客户端能与其中一台服务器通信但无法与另一台通信时。所以我们设想,要么是客户端发送请求时,会同时发给两者。嗯,我现在就假设是这样,这几乎无关紧要。所以我们假设协议是客户端通常应将任何请求发送给两台服务器,而且不知怎么的,我们需要思考如果其中一台服务器没有响应,客户端应该怎么做。

[4:32] 或者如果其中一台服务器似乎无响应,系统应该怎么做。嗯,谢谢。那么现在让我们设想客户端一能联系服务器一但联系不到服务器二。系统应该如何反应?一种可能性是我们想,好吧,你知道,天哪,我们当然不想只让客户端与服务器一通信,因为如果我们把这个值设为一,却没有同时把这个值设为一,那第二个副本就会不一致。所以也许规则应该是,客户端在任何操作中总是被要求与两个副本通信,与两台服务器通信,而不应该被允许只与其中一台通信。

[5:05] 那么为什么这是错误答案呢?假设规则是,哦,在我们的复制系统中,客户端为了取得进展,总是被要求与两个副本通信。谢谢。这根本不是容错的。事实上,它更糟。它比与单台服务器通信更糟,因为现在只要这两台服务器中的任何一台崩溃或你无法与它通信,系统就有问题。至少对于非复制服务器,你只依赖一台服务器。但在这里,两台服务器都必须存活。如果我们要求客户端与两台服务器通信,那么两台服务器都必须在线。所以我们不可能要求客户端真正地,你知道,等待两台服务器都响应。

[5:48] 如果我们没有容错能力,我们需要它能够继续运行。所以另一个显而易见的答案是,如果客户端无法与两者通信,那它就只与它能通信的那台通信,并认为另一台已经死了。那么为什么这也不是正确答案呢?令人不安的场景是另一台服务器实际上还活着。所以假设我们遇到的实际问题不是服务器崩溃了(那对我们来说是好事),而是更糟糕的问题:网线出了故障。这个客户端,客户端一,能与服务器一通信,但无法与服务器二通信。

[6:27] 而且也许还有其他某个客户端能与服务器二通信但无法与服务器一通信。所以,如果我们制定规则,当客户端无法与两台服务器通信时,为了容错,它可以只与一台通信,那么不可避免地会发生什么。比如说,这根线缆会断开,从而将网络切成两半。我将向服务器一发送一个测试并设置请求,服务器一会将其状态设为一。将先前的值 0 返回给客户端 1。所以这意味着客户端 1 会认为自己持有锁。

[7:00] 而且如果是 VMware FT,服务器会认为自己可以成为接管的主节点。但这个副本里面仍然是零。对吧,所以现在如果客户端二也发送一个测试并设置请求,给,你知道,我们——尝试将它们发送给两者,看到服务器一似乎宕机了,遵循那条规则说,好吧,你只需发送给你能通信的那台服务器,那么它也会认为,嗯,它获取了,客户端二也会认为自己获取了锁。所以现在,你知道,如果我们设想这个测试并设置服务器将与 VMware FT 一起使用,我们就有,你知道,两者都。

[7:31] 呃,副本,这两台 VMware,呃,机器我认为它们都可以自己成为主节点。嗯,无需咨询另一台服务器。所以这是彻底的失败。所以在这种设置下,有两台服务器,看起来我们只能选择,要么你等待两者,但你不具备容错性,要么你只等待一个,但你就不正确。而不正确的版本通常被称为脑裂。所以。大家都明白了吗?谢谢。

[8:03] 嗯哼。好吧。嗯……所以这基本上就是当时的情况。直到 80 年代末。但是人们确实想构建复制系统。控制电话交换机的计算机,或者运行银行的计算机,你知道。那些地方愿意花费巨额资金来获得可靠的服务,所以他们会复制,他们会构建复制系统,而他们处理的方式——他们拥有复制但试图排除脑裂的方式。有几种技术。一种是他们会构建一个不会故障的网络。

[8:44] 所以通常这意味着,事实上你们一直在使用本质上不会失效的网络。你们笔记本电脑内部的电线,连接CPU和DRAM的,实际上就是一个不会失效的网络。在——在你们的CPU和DRAM之间。所以,你知道,在合理的假设和大量资金投入下,以及那种精心控制的物理环境中,比如你不希望有电缆蜿蜒穿过地板被人踩到,你知道,这必须是精心物理设计的设置。对于不会失效的网络,你可以排除脑裂。这是一个假设,但有了足够的资金,人们可以非常接近这一点,因为如果网络不会失效,那基本上意味着客户端无法与服务器二通信,那就意味着服务器二肯定宕机了。

[9:31] 因为它不可能是网络故障。所以这是人们构建不受脑裂影响的复制系统的一种方式。谢谢。嗯。另一种可能性是让一些人来解决问题,也就是说,不要自动做任何事情,而是让客户端默认情况下,客户端总是必须等待你知道的,两个副本都响应之类的,我永远不允许只用一个副本就继续。但你可以,你知道,呼叫某人的传呼机响起来,某个人去机房看看两个副本,然后要么关掉一个,确保它肯定死了。

[10:08] 或者验证其中一个确实崩溃了,而另一个还活着。所以你本质上是在用人类作为打破平局者,而人类是一个你知道的。如果他们是计算机,他们自己就会是单点故障。俱乐部。所以很长一段时间,人们使用这两种方案中的一种来构建复制系统,而且这不是,你知道的,它们可以被做成能工作的。人类响应不太快。不会失效的网络很昂贵,但并非不可行。嗯,但事实证明,你实际上可以构建自动故障转移系统,嗯,它可以在不稳定的网络、可能失效的网络面前正确工作。

[10:50] 那可以分区。所以这种网络分成两半,两边都在运行但无法相互通信,通常被称为分区。嗯。谢谢。谢谢。而人们为了构建不受脑裂影响的自动复制系统而提出的重大洞见是多数投票的想法。所以这是——谢谢。谢谢。谢谢。一个几乎在Raft论文中每隔一句就出现的概念。

[11:24] 这是一种基本的——嗯,推进方式。第一步是使用奇数个服务器而不是偶数个服务器。比如,这里的一个缺陷是它有点太对称了。这里分裂的两边,它们看起来一样。它们运行相同的软件,它们会做相同的事情,这不好。但如果你有奇数个服务器,那么,它就不再对称了。对。至少单次网络分裂大概是两个服务器在一边,一个服务器在另一边,它们完全不对称。

[11:57] 而这就是多数投票方案所诉求的一部分。所以,基本想法是你有奇数个服务器,为了取得任何进展,所以在Raft中,选举领导者,或使日志条目被提交,为了在每一步取得任何进展,你必须集合多数……服务器,超过一半,超过所有服务器的一半,来批准那一步,比如投票给领导者,接受新的日志条目并提交它。嗯,所以,你知道,最……

[12:29] 呃……直接的方式是需要两个或三个服务器才能做任何事情。嗯,这有效的一个原因当然是,如果存在分区,不可能有超过一个分区拥有多数服务器。这是看待它的一种方式。一个分区可以有一个服务器,这不是多数。或者也许你可以有两个,但如果一个分区有两个,那么另一个分区必须只有一个服务器,因此永远无法集合多数,也无法取得进展。

[13:03] 嗯,而且只是……完全清楚地说,当我们谈论多数时,它始终是所有服务器中的多数,而不仅仅是活着的服务器。这是一个困扰了我很久的点,如果你有一个三个服务器的系统,也许其中一些已经失效了之类的,如果你需要集合多数,它始终是三选二,即使你知道其中一个已经失效。多数始终是基于服务器总数。嗯,这有一个更一般的表述。因为多数投票系统需要三选二才能取得进展,它可以承受一个服务器的故障。

[13:40] 对。任意两台服务器就足以取得进展。如果你需要能够,如果你,你知道,担心服务器的可靠性,或者天哪,你可以构建拥有更多服务器的系统,所以更一般的表述是:如果你有两台 f 加一台服务器,那么你可以承受 F 次故障。再见。所以如果是三台,那意味着 f 是一。在一个有三台服务器的系统中,你容忍 F 台服务器、F1 次故障,并且仍然继续运行。

[14:16] 谢谢。谢谢。谢谢。好的。通常这些被称为法定人数系统,因为三选二有时被认为是一个法定人数。谢谢。好的,关于这些多数投票系统,我已经提到的一个特性是,嗯,最多一个分区可以拥有一个,因此如果网络被分区,我们不可能让网络的两半都取得进展。这里发生的另一个更微妙的事情是,嗯,如果你总是需要多数。

[14:49] 的服务器继续。然后你会经历一系列操作,在每一步操作中,有人组建了一个多数派,比如,你知道的,为领导者投票,或者说为 Raft 的领导者投票。在每一步中,你为该步骤组建的多数派必须至少包含一台曾属于前一个多数派的服务器。任意两个多数派至少在一台服务器上重叠。而 Raft 所依赖的,最重要的正是这一性质。

[15:22] 嗯,为了避免脑裂。事实是,例如,当你有一个领导者、一次成功的领导者选举,并且一个领导者从多数派中获得了选票时,它的多数派保证会与前一个领导者的多数派重叠。因此,例如,新领导者保证会知道前一个领导者所使用的任期号。因为它的多数派与前一个领导者的多数派重叠,而前一个领导者多数派中的每个人都知道前一个领导者的任期号。同样,前一个领导者可能已提交的任何内容都必须存在于 Wrapped 中多数服务器上,因此任何新领导者的多数派必须至少与一台服务器重叠。

[16:01] 与前一个领导者的每一条已提交条目。这是……Wrapped 正确的重要原因。关于多数投票系统的一般概念,有什么问题吗?是的?添加服务器也很常见吗?比如添加处理?JONATHAN GRUBER:可以添加服务器吗?我认为是可能的。这是可能的,你知道的,第……节,也许是第六节,关于如何添加或更改服务器集合。

[16:41] 这是可能的。你需要在长期运行的系统中这样做。如果你的系统运行五10年,过一段时间你就需要更换服务器。你知道的,其中一台永久故障,或者你升级,或者你把机房搬到另一个机房。你确实需要能够支持更改服务器集合。就是这样。这当然不是每天都会发生,但它是这些系统长期可维护性的关键部分。嗯,而且 Raft 的作者们有点自鸣得意。就是这样。

[17:12] 一个处理这个问题的方案,他们也有理由这样做,因为它很复杂。谢谢。谢谢。好的,所以利用这个想法,大约在1990年左右,有两个系统几乎同时被提出。它们意识到,你可以使用这种多数投票系统来绕过避免脑裂的表面上的不可能性。基本上是通过使用三台服务器而不是两台,并采用多数投票。

[17:44] 嗯,这些非常早期的系统之一叫做 Paxos。Raft 论文大量讨论了它。嗯,另一个非常早期的系统叫做视图戳复制。Ciao。缩写为 VSR,即视图戳复制。尽管 Paxos 在这个领域是迄今为止更广为人知的系统,但 Raft 在设计上实际上更接近视图戳复制。后者是由 MIT 的人发明的。

[18:15] 嗯,所以这些系统有着长达数十年的一段历史,而它们真正走到前台并开始大量用于部署的大型分布式系统,大约是在15年前。也就是在它们最初被发明之后大约15年。好的,那么……那么现在让我谈谈 Raft。Raft 以库的形式出现,旨在被包含在某个服务应用程序中。

[18:50] 因此,如果你有一个复制服务,服务中的每个副本都将是一些接收 RPC 之类的应用程序代码,再加上一个 Raft 库。而 Raft 库彼此协作,嗯,以维护复制。所以,单个 Raft 副本的软件概览是,在顶层我们可以把副本看作拥有应用程序代码。所以对于实验三来说,它可能是一个键值服务器。所以也许我们有一个键值服务器。

[19:22] 并且它有状态,应用程序有 Raft 帮助它管理的状态,即复制状态。而对于键值服务器,它将是一个键和值的表。嗯……*敲击声* 下一层是 Raft 层。所以键值服务器将向 RAPT 发起函数调用,它们会来回闲聊一下。Raft 保留一点状态。你可以在图二中看到它。而就我们的目的而言,真正最关键的状态是 Raft 有一个操作日志。

[20:01] 谢谢。好的。谢谢。阿门。而在一个有三个副本的系统中,我们实际上将有三台服务器,它们具有完全相同的结构,并且希望两层中都有完全相同的数据。谢谢。阿门。谢谢。谢谢。谢谢。谢谢。谢谢。

[20:31] 谢谢。好的。在此之外,将会有客户端。而游戏规则是,所以我们有客户端一、客户端二、一大堆客户端。客户端并不真正知道,客户端是,你知道的,需要能够使用该服务的外部代码。而希望是客户端并不真正需要意识到它们正在与一个复制服务通信。对客户端来说,它看起来几乎就像只有一台服务器,它们与一台服务器通信。因此客户端实际上将客户端请求发送到应用程序层。

[21:05] 当前领导者的,即 Raft 中当前领导者的副本。因此这些将是针对数据库、针对键值服务器的应用层请求,这些可能是 put 和 get 请求。put 接受一个键和一个值并更新表,get 要求服务获取某个键对应的当前键、当前值。所以这与 Raft 没有太大关系。这只是我们正在构建的任何服务的客户端-服务器交互。

[21:39] 嗯,但是一旦这些命令之一从请求中被发出,从客户端发送到服务器,实际发生的情况是,在非复制服务器上,应用代码会执行这个请求,比如说响应一个 put 请求而更新表。但在 RAPT 复制的服务中并非如此。相反,如果假设客户端向领导者发送请求,实际发生的是应用层只是把请求、把客户端的请求向下发送到 RAFT 层,说:“看,这里有一个请求。

[22:10] 请把它提交到复制日志中,完成后告诉我。于是此时,raft 节点之间互相交流。嗯。直到所有副本或多数副本把这个新操作写入它们的日志,所以它被复制了,然后当——领导者知道所有副本都有一份这个操作的副本,只有这时才会向上发送一个通知,回到键值层说:“啊哈,你刚才发给我的那个操作,现在已经被提交到所有副本中了,所以它已被安全复制,此时可以执行该操作了。”

[22:53] 这是一个 raft。客户端向键值层发送请求,键值层还不执行它。所以我们不确定,因为它还没有被复制。只有当它进入所有副本的日志中。再见。然后 Raft 通知领导者,现在领导者才真正执行该操作,这对应于……你知道,对于 put,更新键值表;对于 get,从表中读出正确的值,然后最后把回复发送回客户端。

[23:26] 所以这就是该系统的一个普通操作。谢谢。如果它在多数副本中,它就被提交了。再说一次,为什么不能是所有副本,是因为如果我们想构建一个容错系统,它必须能够在某些服务器已经失效的情况下仍然取得进展。所以——嗯,是的。当它在多数副本中时就被提交了。谢谢。是的。所以在领导者执行请求之前,键值属性。

[24:00] 嗯……那些已经被复制的。谢谢。哇。——对不起。谢谢。领导者们,是的,我明白了。于是。此外,当操作最终被提交时,每个副本都会把该操作向上发送。每个 raft 库层把操作向上发送到本地应用层,本地应用层把该操作应用到它的状态上。于是它们都……所以希望所有副本看到相同的操作流。

[24:31] 它们以相同的顺序出现在这些向上调用中,以相同的顺序被应用到状态上,而且你知道,假设操作是确定性的——它们最好是确定性的——嗯,复制状态的状态将在所有副本上以相同方式演化。所以通常这张表就是论文在谈到状态时所谈论的内容。谢谢。看待这种交互的另一种方式,也是本课程中会经常出现的一种记号,是——嗯,一种时间图,我会给你们画一个时间图来说明消息是如何工作的。

[25:12] 那么让我们想象我们有一个客户端,服务器一是领导者,我们还有服务器二。谢谢。服务器三,在这个图上时间向下流动。我们想象客户端把原始请求发送给服务器一。谢谢。之后,服务器一的 raft 层。嗯,发送一个 AppendEntries,这是 RPC。嗯,发给两个副本中的每一个。谢谢。这只是一个普通的,比如说,put 请求。

[25:45] 这是,这些是 AppendEntries 请求。谢谢。服务器现在在等待来自其他副本的回复。一旦收到多数副本的回复,包括领导者自己,就返回。所以在只有三个副本的系统中,领导者只需要等待另一个副本对 AppendEntries 作出肯定响应,一旦它收集到多数副本的肯定响应。嗯,领导者执行该命令,算出答案,比如对于 get。

[26:20] 嗯,然后把回复发送回……与此同时,当然,你知道,如果 S2 实际上还活着,它也会发回响应,但我们并不等待它。谢谢。知道图二中的内容很有用。谢谢。好了,大家都看到了吗?谢谢。这就是系统的一种普通操作。这里没有故障。哦,好的。天哪,是的。我喜欢……

[26:53] 我漏掉了一个——重要的步骤。此时,领导者知道,哦,我得到了多数副本把它放进它们的日志,我可以继续执行它并向客户端回复 yes,因为它是——服务器二还什么都不知道。它只知道,嗯,你知道,我从……收到了这个请求,但我不知道它是否已经被提交。这取决于,例如,我的回复是否回到了领导者那里。就服务器 2 所知,它的回复被网络丢弃了,也许领导者从未收到回复,也从未决定提交这个请求。实际上还有另一个阶段,一旦服务器。

[27:24] 意识到一个请求已被提交,它就需要把这一事实告诉其他副本。阿门。所以有……呃……这里有一条额外的消息。这条消息具体是什么,有点取决于还有什么其他事情在进行。至少在 RAF 中,并没有显式的提交消息。相反,这一信息被捎带在领导者发出的下一个 AppendEntries 中。它出于某种原因发出的下一个 AppendEntries RPC。

[27:58] 比如那个 RPC 中有一个 commit、leader commit 之类的字段。下一次领导者需要发送心跳或其他东西时,需要发出一个新的客户端请求,因为某个不同的客户端请求了某些东西,它就会发出嗯,新的、更高的 leader commit 值,到那时,呃,嗯,是的。副本将执行该操作并将其应用到它们的状态。是的。

[28:29] 谢谢。谢谢。哦,是的。所以这是一个,这是一个包含相当多闲聊的协议。嗯,而且它并不是,超级臃肿。确实,你知道,是的,客户端发送一个请求,请求必须到达服务器,服务器至少要与,你知道,另一个……它发出多条消息,必须等待响应,再发回一些东西。所以有一堆消息往返时间之类的。

[29:02] 嵌入在这里。对教师的后半部分来说本来是有意义的。谢谢。是的,所以……如果……所以,这实际上取决于你作为实现者,究竟在什么时候让领导者发出更新后的提交索引。如果——客户端请求嗯,只是非常偶尔地回来。那么领导者可能想要发送一个心跳或发送一条特殊的追加条目消息。

[29:34] 嗯,如果客户端请求来得相当频繁,那就无所谓了,如果它们来,你知道,如果每秒到达一千个,那么,天哪,很快就会有另一个到来。所以你可以捎带。因此,无需生成一条额外的消息——那有点昂贵。你可以把信息放在你反正要发送的下一条消息上发出去。嗯,事实上,我不认为副本执行请求的时间——它是关键的,因为没人在等它,至少在没有故障的情况下。如果没有故障,副本执行请求实际上并不在关键路径上。

[30:12] 就像客户端并不在等它们,客户端只等领导者执行。所以这可能没那么重要。嗯,它可能不会影响客户端感知的延迟,具体来说就是这究竟是如何分阶段进行的。嗯。再见。谢谢。谢谢。谢谢。谢谢。谢谢。谢谢。好了。你应该问的一个问题是,为什么这个系统如此专注于日志?日志在做什么?

[30:51] 而且尝试给出明确的答案是有一定价值的。对于为什么这个系统完全专注于日志,一个答案是:日志是领导者对操作进行排序的那种机制。对于这些复制状态机来说,至关重要的是所有副本不仅对它们应用相同的客户端操作,而且以相同的顺序应用相同的操作。但它们都必须以相同的顺序应用来自客户端的这些操作。而日志,在许多其他事物之中,是领导者为传入的客户端操作分配顺序的机制的一部分。

[31:32] 你知道,10 客户端向领导者发送操作。与此同时,领导者必须挑选一个顺序,确保每个人、所有副本都遵守那个顺序。而日志——日志具有编号的槽位这一事实——是领导者表达它所选择的顺序的方式的一部分。嗯,谢谢。日志的另一个用途是,在这个点和这个点之间,服务器 3 已经收到了一个操作,但它还不确定该操作是否已提交。它还不能执行它。它必须把这个操作放在某个地方。

[32:06] 直到领导者提交值的增量到来。因此,日志所做的另一件事是,在跟随者上,日志是跟随者放置那些仍然暂定的操作的地方。这些操作已经到达,但尚不知道是否已提交。而且它们可能不得不被丢弃,正如我们将看到的。嗯,所以那是另一个用途。在领导者一侧,与那个用途相对应的另一方面是,领导者需要在其日志中记住操作,因为它可能需要重新传输它们。

[32:38] 给跟随者。如果某个跟随者离线了,也许是它的网络连接短暂地出了点问题之类的,错过了一些消息。领导者需要能够重发任何跟随者错过的日志消息。因此领导者需要一个地方,可以放置客户端请求消息的副本,甚至是它已经执行过的那些。以便能够将它们重新发送给客户端。我的意思是,我们把它们发送给错过了那个操作的副本。而所有服务器都保留日志的最后一个原因是,至少在图二的世界里,如果一台服务器崩溃并重启并想要重新加入,嗯,你真的希望崩溃的服务器实际上能够重启并重新加入。

[33:20] 被包裹的集群,否则你现在就只能用三台服务器中的两台来运行,再也无法承受任何故障了。我们需要重新纳入失败并重启的服务器,而日志就是一台重启的服务器所使用的东西。日志持久化到它的磁盘上。因为规则之一是,每台 Raft 服务器都需要将其日志写入磁盘,这样在它崩溃并重启后日志仍然存在。该日志就是服务器所使用的东西,它从头重放该日志中的操作,以重建它崩溃时的状态。

[33:52] 然后它就从那里继续。所以日志也被用作持久化计划的一部分。作为重建状态的一系列命令。是的。所以我想知道会出现什么场景,让工作者们试图跟进它。呃……嗯,最终,好吧,所以问题是,假设领导者每秒能够执行一千条客户端命令,而跟随者每秒只能执行一百条客户端命令。那是全速下的那种持续速率。

[34:32] 嗯,所以要注意的一件事是,副本、跟随者,在执行命令之前就确认命令。因此它们确认并在日志中累积内容的速度不受限制。所以,你知道,也许它们每秒可以确认一千个请求。如果它们永远这样做,那么它们就会积累起无界大小的日志,因为它们的执行速率会无界地落后于领导者按照我们游戏的规则给出消息的速率。

[35:06] 所以这意味着它最终会耗尽内存。在某个时刻,当他们落后了十亿条日志条目之后,它们会调用内存分配器来为新日志条目申请空间,而这会失败。嗯,所以是的,而且 RAF 没有,嗯,RAP 没有应对这种情况所需的流控制。所以我认为……在真实系统中,你实际上可能需要捎带一些信息,而且不需要实时,但你大概需要某种额外的通信,说:“哦,这是我执行到的位置。”

[35:47] 这样领导者就可以说,好吧,我比跟随者已经执行到的位置领先了太多千个请求。是的,所以我认为在生产系统中,如果你试图把它推到极限,你可能确实需要一条额外的消息来限制领导者,如果它领先太多的话。谢谢。好的。

[36:33] - 所以问题是,如果其中一台服务器崩溃了,它有这么一份持久化到磁盘的日志,因为这是图二的规则之一。嗯,所以服务器将能够直接从磁盘重新加载日志,但当然,那台服务器不知道它在执行日志方面进行到了哪里。而且它也不知道,至少当它刚重启时,按照图 2 的规则,它甚至不知道日志有多少是已提交的。所以对你问题的第一个答案是,在重启之后,在服务器崩溃并重启并读取这份日志之后,它不被允许对日志做任何事情。

[37:09] 因为它不知道系统在其日志中已经提交到了哪里。也许这份日志有一千条未提交条目,而零条已提交条目,对所有人来说都是如此……如果领导者死了,当然,那也无济于事。让我们假设它们全都崩溃了。这有点超前了……这有点超前于我要讲的内容,但我们会假设它们全都崩溃了,所以它们只有图二中标记为非易失的状态,其中包括日志,也许还有最新的任期。

[37:43] 嗯,所以它们不知道。所以如果发生崩溃,如果它们全都崩溃并且全都重启,最初没有一台知道它们在崩溃前执行到了哪里。所以发生的情况是,它们进行领导者选举,其中一台被选为领导者。而那个领导者,如果你顺着图二关于追加条目应如何工作的描述来追踪,领导者实际上会通过发送第一个心跳作为副产品,弄清楚这一点。

[38:14] 嗯,它基本上会弄清楚最新的点是什么,也就是,所有……大多数副本在它们的日志上达成一致的点。因为那就是提交点。另一种看待方式是,一旦你通过追加条目机制选出一个领导者,领导者就迫使所有其他副本拥有与领导者相同的日志。而在那时,再加上……

[38:45] 论文解释了一点额外内容。在那个时候,既然领导者知道它已经迫使所有副本拥有与它相同的日志,那么它就知道,哦,所有副本也必须拥有……必须有多数副本拥有——那些现在相同的日志中的所有日志条目也必须已提交,因为它们被保存在多数副本上。嗯,而在那个时候,领导者——图二中描述的领导者追加条目代码将递增领导者的提交点,然后每个人现在都可以从头开始执行整个日志。

[39:22] 并从头重新创建它们的状态,可能极其费力。嗯……所以这就是图二所说的。显然,这种从头重新执行并不很有吸引力,但这就是基本协议所做的。我们明天会看到,这个版本更高效,使用检查点,我们明天会讨论它。谢谢。好的,所以这是一段普通、无故障运行中的序列。

[39:53] 嗯,嗯……我还想简要提一下这个接口是什么样子。你们可能都因为做实验而见过一点。但粗略地说,如果你有,比如说,这个键值层及其状态,以及它下面的 raft 层,那么在每个副本上,它们之间实际上有两个主要部分。有这个……嗯,键值层可以转达的方法。如果客户端发来一个请求,键值层必须把它交给 Raft 并说:“请,你知道,把这个请求放进日志的某个位置”,这就是 start 函数。

[40:34] 嗯,你会在 raft.go 中看到它。它实际上只接受一个参数,即客户端命令。谢谢。键值层是在说:“请,我收到了这个命令。把它放进日志,并在它提交时告诉我。”而接口的另一部分是,迟早,Raft 层会通知键值层,啊哈,你之前通过 start 命令发给我的那个操作,它很可能不是最近的 start。可能有一百个客户端命令进来,并在其中任何一个提交之前引发对 start 的调用。

[41:09] 迟早,嗯,这个——向上通信采取的形式是 Raft 库在 Go 通道上发送的一条消息,而键值层应该从该通道读取。嗯,所以有这个 apply,谢谢。谢谢。它被称为 apply 通道,你在其上发送一条 apply 消息。谢谢。我不知道。谢谢。谢谢。谢谢。这个 start,当然,你需要键值层能够将它在 Apply 通道上收到的消息与它所发起的 start 调用匹配起来。

[41:47] 嗯,所以 start 命令实际上返回了足够的信息,让这种匹配得以发生。它返回的是索引——start 函数基本上返回的是日志中的索引,如果这个命令被提交——它可能不会被提交——它将在该索引处被提交。嗯,而且我认为它还返回当前任期,以及一些我们不太关心的其他东西。然后这个 apply 消息将包含索引。既包含命令。

[42:20] 嗯。谢谢。谢谢。谢谢。谢谢。谢谢。以及索引。所有副本都会收到这些 apply 消息,所以它们都会知道,“哦,我应该应用这个命令。”嗯,弄清楚这个命令是什么意思,并把它应用到我的本地状态。它们也会收到索引。索引实际上只在领导者上有用,这样它就能弄清楚我们说的是哪些客户端请求。谢谢。我好奇的是下面这一点。客户端把它们的请求发送给后端,也就是领导者,任何地方。

[42:59] 在这一点上——客户端通过发出一个 get 来……对。关于那个。好。好。嗯,我想我回答了一个稍微不同的问题。假设客户端发来任何请求,比如说是一个 put 或一个 get。它可以是 put 或 get。这并不太重要。我就拿 get 来说。嗯,这个时间点——所以客户端再次发送并等待响应。领导者会发送响应的那个时间点,是在领导者知道该命令已被提交之后。

[43:40] 所以这将是一个类似 get 的回复。再见。*敲击声* 再见。所以客户端什么也看不到。我是说,它,所以,嗯,这意味着就实际的软件栈而言,这意味着键值层——RPC 会到达,键值层会调用 start 函数。start 函数会返回到键值层,但是——键值层还不会回复客户端,因为它不知道它是否——实际上,它现在还没有执行客户端的请求。它甚至不知道它是否最终会执行。

[44:14] 因为它不确定这个请求是否会被提交。而它可能不会被提交的情况是,键值层,你知道,收到一个请求,调用 start,而就在 start 返回之后,它崩溃了。它肯定还没有发出它的 apply、pen 消息之类的。什么都还没有被提交。嗯,所以这个流程是:start 返回。时间流逝,与该客户端请求对应的相关 apply 消息出现在 apply 通道上,被键值服务器看到,只有到那时,这才会促使键值服务器执行该请求并——发送回复。

[44:52] 谢谢。谢谢。谢谢。谢谢。谢谢。而这就像——所有这一切——当它……这非常重要。如果一切顺利,那并不太重要。但如果出现故障,我们现在——到了我们开始担心故障的时候,我们会极其关心,如果出现了故障,客户端看到了什么?嗯,谢谢。好。所以出现的一件事是……那就是,嗯,你们所有人都应该熟悉的是,至少最初,关于日志有一件有趣的事,就是它们可能并不完全相同。

[45:30] 有很多情况,至少在一小段时间内,不同……副本日志的末尾可能会分叉,比如,如果领导者开始发送一轮 append 消息,但在能够把它们全部发出去之前崩溃了。这就意味着,一些收到了 append 消息的副本会追加,你知道,那个新的日志条目,而那些没有通过 RPC 收到那个 pin 消息的副本,就不会追加它们。所以很容易看出日志有时会分叉。好消息是,Raft 的工作方式实际上最终会迫使日志在一段时间后变得完全相同。

[46:07] 因为可能存在暂时性的差异,但从长远来看,所有日志都会被领导者以某种方式修改,直到领导者确保它们全都完全相同,只有到那时……嗯。好。我想接下来,关于 Raft 这里实际上有两大主题要讲。一个是领导者选举如何工作,也就是 Lab 2。另一个是,领导者如何处理不同副本的日志,尤其是在故障之后。

[46:38] 所以首先我想谈谈领导者选举。阿门。谢谢。要问的问题是,这个系统为什么会有领导者?我们为什么需要一个领导者?这部分答案在于,你并不需要一个领导者来构建这样的系统。是可以构建一个一致性系统的,让一组服务器就日志中条目的顺序达成一致,而无需任何指定类型的领导者。事实上,论文提到的原始 Paxos 系统,原始 Paxos 并没有领导者。

[47:11] 嗯,所以这是可能的。Raft 有领导者的原因基本上是,嗯,可能有很多原因,但最主要的原因之一是,在所有服务器都不发生故障的常见情况下,你可以构建一个更高效的系统。如果有领导者,就有可能构建一个更高效的系统。因为有了指定的领导者,每个人都知道领导者是谁,你基本上可以用每个请求一轮消息就请求达成一致。而无领导者的系统更像是,嗯,你需要第一轮来就一个临时领导者达成一致,然后第二轮才真正把请求发出去。

[47:49] 所以,很可能使用领导者能让系统速度提高一倍。谢谢。而且它也让思考正在发生什么变得更容易一些。谢谢。嗯,Raft 会经历一系列领导者,它使用这些任期编号来消除歧义,说明我们说的是哪个领导者。事实证明,追随者其实并不需要知道领导者的身份。它们真正只需要知道当前的任期编号是什么。

[48:20] 每个任期最多只有一个领导者。就是这样。关键特性是,你知道,对于每个任期,该任期内可能没有领导者,也可能有一个领导者,但不可能有两个领导者。在同一个任期内。每个任期最多只有一个领导者。嗯,谢谢。谢谢。领导者最初是如何产生的?每个……包装服务器。维护这个选举定时器,它基本上只是一个它记录的时间,意思是,如果那个时间到了,我就要做点什么。

[48:55] 嗯,它做的一件事是,如果整个领导者选举周期到期,而服务器没有听到来自当前领导者的任何消息,那么服务器就大致认为当前领导者很可能已经死了,并开始选举。所以我们有这个选举定时器。如果它到期了,就开始选举。谢谢。。

[49:26] 谢谢。嗯,而开始选举意味着,基本上就是增加任期,那个决定要成为候选人的服务器,也就是强制进行新选举的服务器,首先增加这个任期,因为,你知道,想要有一个新的领导者,也就是它自己,而一个任期不能有超过一个领导者,所以为了有一个新的领导者,我们必须开始一个新的任期。然后它发出这些请求投票的 RPC。

[49:57] 谢谢。我将发出一整轮请求投票。你可能只需要发出 n 减一个请求投票,因为其中一条规则是,新的候选人在选举中总是投票给自己。嗯,所以。关于这一点需要注意的一件事是,并不完全是这样的情况:如果领导者没有失败,我们就不会有选举。或者如果领导者确实失败了,那么只要任何其他服务器还在运行,我们就会有选举,因为总有一天其他服务器的选举定时器会触发。

[50:28] 但如果领导者没有失败,不幸的是我们仍然可能进行选举。所以如果网络很慢或丢掉了几个心跳之类的,我们可能最终会让选举定时器触发,即使有一个完全正常的领导者,我们仍然可能进行新的选举。所以我们在考虑正确性时必须记住这一点。嗯,而这反过来意味着,如果有一场新的选举,很容易出现旧领导者仍然存在并且仍然认为自己是领导者的情况。比如,如果有网络分区,旧领导者仍然活着并且完好地位于少数派分区中。

[51:02] 多数派分区可能会进行选举,而且确实是一次成功的选举,并选出一个新的领导者。前一个领导者完全不知道。所以我们还必须担心,哦,那个前一个领导者会做什么,因为它不知道有一场新的选举?非常感谢。谢谢。谢谢。

[51:43] 所以问题是,是否存在、是否可能存在病态情况,例如单向网络通信可能阻止系统取得进展。我相信答案是肯定的。当然。所以例如,如果当前领导者的网络以某种方式半失效,使得当前领导者可以发出心跳但无法接收任何客户端请求,那么它发出的心跳,由于它的出站网络连接正常工作而被送达。它的出站心跳将抑制任何其他服务器开始选举。

[52:20] 但它的入站网络线路显然坏了,这一事实将阻止它听到并执行任何客户端命令。Raft 绝对不是能够防范所有可能出现的疯狂网络问题的。我相信我想到的那些问题是可以修复的,意思是我们可以通过要求一种双向心跳来解决这个问题,即如果领导者发出心跳,但要求跟随者以某种方式回复心跳,就像它们已经被要求回复那样。

[52:58] 但如果领导者不再看到对其心跳的回复,那么在某个时间段之后,也就是该任期被替换之后,领导者决定下台。我觉得这个具体问题是可以修复的。许多其他问题也可以,但我,但是,是的。你知道,你完全正确。网络可能发生非常奇怪的事情,包括一些协议没有准备好的情况。谢谢。谢谢。谢谢。

[53:29] 好,所以我们有了这些任期选举。我们需要确保每个任期最多、最多只有一个领导者。Raft 是如何做到这一点的?嗯,Raft 要求,为了在一个任期中当选,Raft 要求候选人获得多数服务器的赞成票。服务器,而且每个服务器在每个任期中只会投一张赞成票。所以在任何给定任期中,你知道,这基本上意味着在任何给定任期中,每个服务器只为一个候选人投票一次,嗯,你不可能有两个候选人都获得多数票,因为每个人都只投一次票。

[54:06] 嗯,所以多数派——多数派规则使得最多只有一个获胜候选人。嗯,于是我们每个任期最多只有一个候选人当选。嗯……此外,关键的是,多数派规则意味着即使一些服务器崩溃了,你也可以当选。如果少数服务器崩溃或不可用、有网络问题,我们仍然可以选出领导者。如果超过一半的服务器崩溃或不可用,或者处于另一个分区之类的,那么实际上系统就只会坐在那里,一次又一次地尝试选举领导者。

[54:46]并且绝不选举出领导者。如果它实际上无法做到,如果它们不是活跃服务器中的多数。嗯,谢谢。如果选举成功,各位,如果每个人都能了解到这件事,那就太好了。大约7分钟。我们需要问自己,所有各方如何得知发生了什么?赢得选举的服务器,假设它没有崩溃,赢得选举的服务器实际上会看到来自多数其他服务器对其请求投票的多数或赞成票。正在竞选并赢得选举的候选人,赢得选举的候选人会直接知道,我获得了多数票。

[55:23]但其他人都不会直接知道谁是获胜者,或者是否有人获胜。嗯,所以候选人通知其他服务器的方式就是那个心跳。图2中的规则说,哦,如果你赢得选举,你立即被要求向所有其他服务器发送独立条目。现在,独立条目,那个心跳独立条目并没有明确说,我赢得了选举,你知道,我是任期23的领导者。嗯,这比那要微妙一些。信息传达的方式是,嗯,除非某人是该任期的领导者,否则不允许发送追加条目。

[56:02]所以,我是一个,你知道,我是一个服务器,我看到了,哦,任期19有一场选举。然后渐渐地,我发送了任期为19的追加条目,这告诉我有人,我不知道是谁,但有人赢得了选举。嗯,所以这就是其他服务器知道的方式,它们收到了该任期的追加条目。而那个追加条目也有重置每个服务器的选举计时器的效果。所以只要领导者还在运行,并且按照它应该的速率发送心跳消息或至少追加条目,每次服务器收到追加条目时,它就会重置其选举计时器,并在某种程度上抑制任何人成为新候选人。

[56:46]所以只要一切正常运作,重复的心跳将阻止任何进一步的选举。当然,如果网络故障或数据包丢失,可能仍然会有一次选举。但如果一切顺利,我们就不太可能发生选举。嗯,这个方案可能在某种意义上失败,好吧,它不可能在为一个任期选举出两个领导者这个意义上失败,但它可能在一个任期内选举出零个领导者这个意义上失败。嗯,它可能失败的常见方式是,如果有太多服务器死亡或不可用,或者网络连接不好。所以如果你无法凑齐多数,你就无法当选,什么也不会发生。

[57:22]嗯,选举可能失败的更有趣的方式是,如果所有人都活着。你知道,没有故障,没有数据包丢失。但是——两个领导者成为候选人的时间足够接近,以至于它们之间分裂了选票。或者比如说三个领导者。嗯,假设我们有三个领导者。假设我们有一个三副本系统,嗯,它们所有的选举计时器同时触发。

[57:53]每个服务器都投票给自己。然后当它们每个都收到来自另一个服务器的请求投票时,好吧,它已经投票给自己了,所以它说不。所以这意味着所有三个服务器各得一票,没有人获得多数,没有人当选。嗯,所以然后——它们的选举计时器会再次触发,因为选举计时器只有在收到追加条目时才会重置,但没有领导者,它们都会让选举计时器再次触发。如果我们不走运,它们会同时触发。它们都会投票给自己。没有人会获得多数。呃,所以。

[58:24]所以很明显,我相信你们现在都意识到了,这个故事还有更多内容。而raft D——使分裂投票的可能性变得不大,但并非不可能的方式,就是通过随机化这些选举计时器。所以思考它的方式,以及随机化,好吧,思考它的方式是,所以我们有一条时间线,我要,嗯,在上面画事件。有某个时间点,每个人都收到了最后的追加条目。

[58:56]然后也许那个服务器死了。我们就假设那个服务器发出了最后一个心跳,然后死了。好吧,所有跟随者都——或者说在收到时重置了它们的选举计时器,在同一时间,因为它们可能都在同一时间收到了追加条目。它们都把选举计时器重置到未来的某个时间点,在未来的某个时间点触发。它们选择了未来不同的随机时间,这些时间将会触发。所以我假设死去的领导者是服务器一,所以现在服务器二和服务器三在这个时间点,把它们的选举计时器设定为未来的一个随机时间点。假设服务器二把它的,嗯,我希望某个计时器在这里触发。

[59:38]而服务器三把它的选举计时器设定为在那里触发。而这张图的关键点是,假设它们选择了不同的随机数,其中一个排第一,另一个排第二。对吧?这就是这里发生的情况。而排第一的那个,假设这个间隔足够大,排第一的那个,这个选举计时器会先于另一个的选举计时器触发。只要我们不倒霉,它就有时间发送一整轮请求投票。

[1:00:10]并在第二个选举计时器从任何其他服务器触发之前,从所有活着的服务器那里得到答复。嗯,所以。大家都看到了吗?随机化是如何使这些候选人去同步的。不幸的是,为这些选举计时器设置常数有一点艺术性。你可能想要满足某种相互竞争的要求。嗯,所以一个明显的要求是,选举计时器必须至少与心跳之间的预期间隔一样长。

[1:00:48] 很明显,领导者每隔 100 毫秒发送一次心跳,你最好确保,你知道,任何人的选举计时器都不可能在 100 毫秒内触发,因为那样它会在我们甚至预期新的追加条目之前就触发。所以下限肯定是——下限是一个心跳间隔。事实上,由于网络可能丢包,你可能希望最小选举计时器值设为心跳间隔的几倍。所以对于 100 毫秒的心跳,你可能希望最短可能的选举计时器设为,比如说,300 毫秒。

[1:01:25] 心跳间隔的三倍。嗯,所以这就是那种最小值是……嗯,如果心跳这么频繁,你希望最小值是它的几倍,或者这里。嗯,那么最大值呢?你知道,你大概会在某个时间范围内均匀随机化。你知道,我们应该把——那种最大值设在哪里。谢谢。我们随机化的范围。还有几个……

[1:01:55] 在真实系统中这里有几点考虑。嗯,你知道,这个最大时间,呃……影响系统从故障中恢复的速度,因为记住,从服务器故障的那一刻起,直到第一个选举计时器触发,整个系统是冻结的。没有领导者。你知道,客户端的请求被丢弃,因为没有领导者。而且我们没有指派新的领导者,即使你知道,大概这些其他服务器是活着的。

[1:02:26] 所以我们选择的这个最大值越大,我们在恢复发生之前强加给客户端的延迟就越长。你知道,这重不重要,取决于我们需要它有多高的性能,以及我们认为故障发生的频率。如果故障一年发生一次,那谁在乎?嗯,如果我们预期故障频繁发生,我们可能非常在乎恢复需要多长时间。好的,所以这是一个考虑。另一个考虑是这个间隔,即第一个计时器触发和第二个计时器触发之间的预期时间间隔。

[1:03:03] 嗯,这个间隔,为了有用,必须长于候选人从所有人那里收集选票所需的时间。也就是说……长于预期的往返时间,即发送一个 RPC 并得到响应所需的时间。是的,也许发送 RPC 并得到响应需要 10 毫秒,从所有其他服务器得到响应。如果是这样,我们需要把最大值设得足够长,使得最小随机数和次小随机数之间很可能有 10 毫秒的差异。

[1:03:35] 嗯,谢谢。对你来说,嗯,如果你,呃,嗯,谢谢,如果你不能在几秒内从领导者故障中恢复,测试代码会不高兴的。所以,务实地说,你需要把这个最大值调低,使得很可能在几秒内完成领导者选举。这不是一个非常严格的约束。

[1:04:09] 关于选举超时有什么问题吗?一个小点是,嗯,你想要……每次,每次你,每次我知道——重置它的选举计时器时,选择新的随机超时。也就是说,不要在服务器首次创建时选择一个随机数,然后一遍又一遍地重复使用同一个数。因为如果你做了一个不幸的选择,也就是你选择了同一个。

[1:04:39] 一台服务器碰巧因运气不好选择了与另一台服务器相同的随机数。这意味着你将永远一次又一次地出现分裂投票。所以这就是为什么你几乎肯定要在每次重置计时器时为选举超时值选择一个不同的、新的、新鲜的随机数。谢谢。好的,那么,关于领导者选举的最后一个问题。假设我们处于旧领导者被分区隔离的情况。

[1:05:10] 你知道,网线断了,旧领导者带着几个客户端和少数服务器在外面。另一半网络中有多数派,新的一半网络中的多数派选出了新领导者。旧领导者呢?为什么旧领导者不会……谢谢。导致不正确的执行。谢谢。也许它是——对,就在那里。

[1:05:56] 那就是它将要……——我很乐意。再见。那都完成了。天哪。谢谢。是的。两个潜在问题。或者一个算是非问题的是,如果另一个分区里有一个领导者,而它没有多数派,那么下次客户端向它发送请求时,嗯,那个领导者,你知道,一个拥有少数派的领导者,是的,它会发送追加条目,但因为它处于少数派分区,它无法从包括它自己在内的多数服务器获得响应。所以它将永远不会……

[1:06:34] 提交该操作,它将永远不会执行它。它也永远不会向客户端回应说它执行了。所以这意味着,是的,一个在不同分区里的旧服务器,人们——客户端可能会发送请求,但他们永远不会得到响应。嗯,所以没有客户端会被愚弄而认为那个旧服务器为它执行了任何操作。另一个更棘手的问题,实际上我几分钟后会讲到,是服务器在故障之前向一个子集发送追加条目的可能性。

[1:07:14] 的服务器。然后在做出提交决定之前崩溃了。这是一个非常有趣的问题,我们可能会花上整整 45 分钟来讨论它。那么,实际上,在我转向后面的主题之前,关于领导者选举还有更多问题吗?谢谢。好的。嗯,好的,那么日志的内容呢,特别是新当选的领导者如何在上一任领导者意外崩溃后收拾残局,新当选的领导者如何理清不同副本上可能分歧的日志?

[1:08:01] 以恢复系统中某种一致的状态。嗯,好的,所以第一个问题是,什么情况下,这整个话题实际上只有在服务器崩溃之后才有意思,对吧?如果服务器保持运行,那么相对而言很少会出问题。如果我们有一台服务器在运行并且拥有多数派,你知道,在它运行并拥有多数派的那段时间里,嗯,它只是告诉跟随者日志应该是什么样子。

[1:08:36] 而且跟随者不允许有异议。它们必须接受。按照图二的规则,只要它们或多或少跟上了进度,它们就照做。你知道,它们只是接受领导者发送的任何内容,追加条目,并将其追加到日志中,遵守提交消息并执行。几乎不会出问题。Raft 中出问题的情况是,旧领导者在发送消息的过程中途崩溃,或者新领导者刚当选后,嗯……在它做出任何有用的事情之前就崩溃了。

[1:09:08] 所以我们非常感兴趣的一件事是,经过一系列崩溃之后,日志可能是什么样子?嗯……好的。这里有一个例子。那么我们有三个服务器。谢谢。谢谢。嗯,我画这些图的方式,因为我们会看到很多日志看起来像这样的情况,我们会想,这有可能吗,如果它们确实看起来像那样会发生什么?所以,我的记号将是,我要写出每个服务器的日志条目,嗯,对齐以表示槽位。

[1:09:46] 日志中对应的槽位。我在这里写的值将是任期号而不是客户端操作。我要,你知道,这是槽位一,这是槽位二。嗯,每个人都看到了来自任期三的命令在槽位一中,服务器 2 和服务器 3 也看到了来自任期三的命令,在第二个槽位中,服务器一那里什么都没有。嗯,所以对于这个,就像第一个问题,这能出现吗?

[1:10:18] 这……种情况能出现吗,怎么出现?哦。怎么出现?谢谢。是的,所以。谢谢。所以。

[1:11:03] 所以也许服务器三是任期三的领导者,只是重复你所说的,也许服务器三是任期三的领导者。它收到了一条命令并发送给了所有人。每个人都收到了并作为日志追加了。然后服务器三收到了来自客户端的第二个请求。也许它把它发送给了所有三个服务器,但消息在发往服务器一的途中丢失了,或者也许服务器一当时宕机了之类的,所以只有服务器二……嗯,领导者总是在发送追加条目之前将新命令追加到自己的日志中。嗯,也许追加条目 RPC 只到达了服务器二。所以这种情况,你知道,就像日志现在不同的最简单情况。

[1:11:38] 我们知道它可能如何……出现。所以如果服务器三,也就是领导者,现在崩溃了,你知道,下一个服务器他们需要确保服务器一……嗯,首先,如果服务器 3 崩溃了,或者我们进行选举并选出了另一个领导者,你知道,必须发生两件事。新的领导者……它必须认识到这一点。嗯,一条命令可能已经提交了。它不允许丢弃它。并且它需要确保服务器一用其他人都在那个槽位中拥有的同一条命令来填补这里的空白。

[1:12:15] 嗯,谢谢。好的,所以在崩溃之后,某个,你知道,一个服务器假设,另一种可能出现的情况是服务器三可能已经向服务器二发送了追加条目,但在向服务器三发送追加条目之前崩溃了。所以如果我们正在选举新的领导者,那可能是因为我们在消息发送之前崩溃了。好的,这里还有另一个场景需要考虑。嗯,又是三个服务器。谢谢。谢谢。谢谢。嗯,现在我要给日志中的槽位编号。

[1:12:47] 这样我们就可以引用它们。嗯,我们有槽位 10、11、12。谢谢。13。再次。那是……谢谢。同样的设置,除了现在在槽位 12 中,我们有服务器 2 作为来自任期四的命令,而服务器三有一个来自任期五的命令。谢谢。谢谢。所以——在我们分析这些以弄清楚会发生什么、服务器看到这个会怎么做之前,我们需要问这甚至可能发生吗?

[1:13:25] 有时候问题的答案是,天哪,如果这种配置出现了会发生什么?有时候答案是,它不可能出现,所以我们不必担心。嗯,所以问题是,这可能出现吗,怎么出现?是的。说不会,因为我们有很多。好的。谢谢。谢谢。嗯,好的。

[1:14:00] 而且任何——好的。谢谢。第一种情况发生了。阿门。看着。朋友们。——现在,特洛伊。再见。罗杰。谢谢。什么?呃……对,老师。

[1:14:32] 伙计们。谢谢。——哦我的天。好的。退回去。是的。多远?所以让我重复一下。简短。我们知道这种配置可能出现,那么我们能在这里得到四和五的方式是,假设在下一次领导者选举中,服务器 2 当选为领导者。

[1:15:09] 现在进入第四任期。选出领导者。你收到来自客户端的请求,它将其追加到自己的日志中并崩溃了。所以现在我们有了这个情况。对,我们需要重新选举,因为领导者刚刚崩溃了。现在,在这次选举中,谢谢。所以现在我们必须问谁可能被选出来,或者我们必须在脑子里记住,天哪,谁可能被选出来?嗯,所以我们要声称服务器三可能被选出来。它可能被选出来的原因是它只需要获得多数派的请求投票响应。那个多数派是服务器一和服务器三。

[1:15:41] 你知道,这两个日志之间没有问题,没有冲突。所以服务器三可以在第五任期被选出来,收到来自客户端的请求,追加到自己的日志中并崩溃。这就是你得到这个配置的方式。呃,所以你需要能够——再见。——梳理这些事情,才能达到说“是的,这可能会发生”的阶段,因此,Raft 必须做一些合理的事情,而不是说“这不可能发生”。因为有些事情是不可能发生的。

[1:16:12] 嗯,谢谢。好的,那么……嗯,那么现在可能发生什么?我们知道这可能会发生。希望我们能说服自己 Raft 实际上确实做了一些合理的事情。现在,关于事情的范围,在我们谈论 Raft 实际会做什么之前。我们需要对什么才是可接受的结果有一些认识。

[1:16:47] 对吧?仅凭肉眼观察这个,嗯,我们知道槽位 10 中的命令,由于所有副本都知道它,它可能已经被提交了。所以我们不能丢弃它。同样,槽位 11 中的命令,由于它存在于多数副本中,据我们所知,它可能已经被提交了,所以我们不能丢弃它。然而,槽位 12 中的命令,它们两个都不可能已经被提交。所以我们有权这样做。我们不知道,我们还没有看 Raft 实际会做什么,但 Raft 有权丢弃这两个,尽管它无权丢弃 10 或 11 中的任何一个命令。

[1:17:26] 嗯,这是一种有权进行的丢弃。并不是必须丢弃其中任何一个。而且我们知道它肯定必须丢弃至少一个,因为最终日志内容必须完全相同。这个可能已经被提交了。我们无法仅通过查看日志来判断领导者崩溃前究竟进行到了哪一步。所以一种可能性是,对于这个命令甚至这个命令,一种可能性是领导者发出了带有新命令的追加消息,然后立即崩溃了。

[1:18:04] 所以它从未收到任何回复,因为它崩溃了。因此旧领导者不知道它是否已被提交。而如果它没有收到回复,那就意味着它没有执行它,也没有发出,但是,你知道,它没有发出递增后的提交索引。所以也许副本也没有执行它。所以实际上这个可能并没有被提交。所以,尽管 Raft 不知道,嗯,Raft 这样做可能是合法的。谢谢。

[1:18:36] 如果 Raft 知道的比它现在知道的更多,丢弃这个日志条目可能是合法的,因为它可能并没有被提交。但基于现有证据,没有办法根据这些证据来反驳它已被提交。它可能已经被提交了。而 Raft 无法证明它没有被提交。所以它必须将其视为已提交。对,因为领导者可能已经收到了它,可能在收到追加条目回复之后、在回复客户端之后刚刚崩溃。所以仅看这个,我们无法排除任何一种可能性。

[1:19:11] 领导者回复了客户端,在这种情况下我们不能丢弃这个条目,因为客户端知道它。或者领导者从未回复,那么是的,我们可以。你知道,所以我们必须假设它已被提交。有问题吗?。嗯,呃……

[1:19:46] 不,没有——我是说,在这种情况下,也许服务器在收到之前就崩溃了。好的,我们周四继续这个内容。

查看英文原始字幕

[0:01] All right, let's get started. Mm-hmm. Today, and indeed today and tomorrow, I'm going to talk about Raft. Um, both because I hope it will be helpful for you in implementing the labs. And also because You know, it's just a case study in the details of how to get state machine replication correct. So, by way of introduction to the problem, you may have noticed a pattern in the fault tolerance systems that we've looked at so far.

[0:35] Um, One is that MapReduce replicates computation but The replication is controlled, the whole computation is controlled by a single master. Um, Another example I'd like to draw your attention to is that GFS, replicates data, right, as this primary backup scheme for replicating the actual contents of files, but It relies on a single master. to choose who the primary is for every piece of data. Another example, VMware FT replicates computational right on a primary virtual machine and a backup virtual machine. But in order to figure out what to do next, if one of them seems to fail, it relies on a single test and set server.

[1:20] to help it choose, to help it ensure that exactly one of the primary or the backup takes over if there's some kind of failure. So in all three of these cases, Sure there was a replication system but sort of tucked away in a corner in the replication system there was some scheme where a single entity was required to make a critical decision about. who the primary was in the cases we care about. So, I'm going to start with the first one. a very nice thing about having a single entity decide who's gonna be the primary?

[1:52] is that it can't disagree with itself. There's only one of it, make some decision, that's the decision it made. Um, But the bad thing about having a single entity decide, like, who the primary is, is that it itself has a single point of failure. And so you can view these systems that we've looked at as sort of... pushing the real heart of the fault tolerance machinery into a little corner. That is the single entity that decides who's gonna be the primary. if there's a failure. Now, This whole thing is about how to avoid split brain. The reason why we have to have, have to be extremely careful about making the decision about who should be the primary, if there's a failure, is that otherwise we risk split brain.

[2:35] Um, And just... make this point super clear, I'm going to just remind you. what the problem is and why it's a serious problem. So, supposing for example we're um, We want to build ourselves a replicated test and set server. That is, we're worried about the fact that VMware FT relies on this test and set server to-- choose who the primary is, so let's build a replicated test and set server. And I'm going to do this, it's going to be broken, it's just an illustration for why why it's difficult to get the split brain problem correctly. So, you know, we're gonna imagine we have a network, and maybe two servers, which are supposed to be replicas of our Test and set service connected and you know, maybe two clients that need to know who's the primary right now? or actually, maybe these clients in this case are a The primary and the backup.

[3:30] um, in VMware FT. So, If it's a test and set service, then both these databases, both these servers start out with their state, that is the state of this test and flight bag being zero. the one operation their clients can send is the test and set operation which is supposed to set the of the replicated service to one, so it should set both copies. and then return the old value. So it essentially acts as a kind of simplified lock server. Um, Thank you.

[4:01] OK. The problem situation? What we worry about, split brain arises automatically. when a client can talk to one of the servers but can't talk to the other. So we're imagining either that when clients send a request, they send it to both. Um, I'm just gonna assume that now, it almost doesn't matter. So let's assume that the protocol is that the client's supposed to send ordinarily any request to both servers, And Somehow, we need to think through what the client should do if one of the servers doesn't respond.

[4:32] or what the system should do if one of the servers seems to be unresponsive. Um, Thank you. So let's imagine now the client one can contact server one but not server two. How should the system react? One possibility is that we think, well, you know, gosh, We certainly don't want to just talk to client to server one, because that would leave the second replica inconsistent if we set this value to one, but didn't also set this value to one. So maybe the rule should be that a client is always required to talk to both replicas. to both servers. for any operation and shouldn't be allowed to just talk to one of them.

[5:05] So why is that the wrong answer? Supposing the rule is, oh, in our replicated system, the client's always required to Talk to both replicas. in order to make progress. Thank you. That's not fault tolerant at all. In fact, it's worse. It's worse. than talking to a single server because now the system has a problem if either of these servers is crashed or you can't talk to it. At least with a non-replicated server, you're only depending on one server. But here we have both servers have to be alive. If we require the client to talk to both servers then. Both servers have to be live. So we can't possibly require the client to actually you know, wait for both servers to respond.

[5:48] If we don't have fault tolerance, we need it to be able to proceed. So another obvious answer is that, If the client can't talk to both, well, it just talks to the one it can talk to and figures the other one's dead. So why is that also not the right answer? The troubling scenario is if the other server is actually alive. So suppose the actual problem we're encountering is not that the server crashed, which would be good for us, but the much worse issue that something went wrong with the network cable. And that this client can talk to, client one can talk to server one but not server two.

[6:27] And there's maybe some other client out there that can talk to server two but not server one. So, If we make the rule, that if a client can't talk to both servers, that it's okay in order to be fault tolerant, that it just talked to one, then what's-- inevitably going to happen. said, this cable's going to break, thus Cutting the network in half. I'm going to-- send a test and set request to server one, Server one will set its state to one Return the previous value of 0 to client 1. And so that means client 1 will think it has the lock.

[7:00] and if it's a VMware FT, server will think it can be takeover's primary. But this replica still has zero in it. Right, so now if client two also sends a test and set request, to You know, we-- tries to send them to both, sees that server one appears to be down, follows the rule that says, well, you just send to the one server you can talk to, then it will also think that Um. it acquired, client two will also think that it acquired the lock. And so now, You know, if we were imagining this test and set server was going to be used with VMware FT, we have, you know, Both.

[7:31] uh, replica, both of these VMware, uh, machines I think they could be primary by themselves. Um, without consulting the other server. So that's a complete failure. So with this setup and two servers, It seemed like we had this, we just had to choose, either you wait for both and you're not fault-tolerant, or You wait for just one and you're not correct. And the not correct version is often called split-brain. So. Everybody see this? Thank you.

[8:03] Mm-mm. Well. Um... So this was basically where things stood. until the late 80s. And But people did want to build replicated Systems. the computers that control telephone switches, or the computers that ran banks, you know. Those places are willing to spend a huge amount of money in order to have reliable service, and so they would replicate, they would build replicated systems, and the way they would deal The way that they would-- have replication but try to rule out split brain. It's a couple of techniques. One is They would build a network that could not fail.

[8:44] And so usually what that means, and in fact you guys use networks that essentially cannot fail all the time. the wires inside your laptop you know, connecting the CPU to the DRAM are effectively a network that cannot fail. between the-- between your CPU and DRAM. So, you know, with reasonable assumptions and lots of money, and that, you know, sort of carefully controlled physical situation, like you don't want to have a cable snaking across the floor that somebody can step on, you know, it's got to be carefully physically designed setup. With a network that cannot fail, you can rule out split-brain. It's a bit of an assumption, but with enough money people get quite close to this because If the network cannot fail, that basically means that the client can't talk to server two, that means server two must be down.

[9:31] because it can't have been the network malfunctioning. So that was one way that people sort of built replication systems that didn't suffer from split-brain. Thank you. Um. Another possibility would be to have some human beings sort out the problem, that is, don't automatically do anything, instead have the clients By default, clients always have to wait for you know, both replicas respond or something, I'm never allowed to proceed with just one of them. But you can, you know, call somebody's beeper to go off, some human being goes to the machine room and sort of looks at the two replicas and, either turns one off, to make sure it's definitely dead.

[10:08] or verifies that one of them has indeed crashed, and that the other is alive. And so you're essentially using the human as a as the tiebreaker, and the human is a you know. if they were a computer, it would be a single point of failure themselves. club. So for a long time, people used one or the other of these schemes in order to build replicated systems, and it's not, you know, They can be made to work. The humans don't respond very quickly. And the network that cannot fail is expensive, but it's not not doable. Um, But it turned out that you can actually build automated failover systems Um, that can work correctly in the face of flaky networks, of networks that could fail.

[10:50] that can partition. So this split of the network in half where the two sides operate but can't talk to each other, That's usually called a partition. Um. Thank you. Thank you. And the big insight that people came up with in order to build automated replication systems that don't suffer from split-brain is the idea of a majority vote. And so this is-- Thank you. Thank you. Thank you. a concept that shows up in like every other sentence practically in the Raft paper.

[11:24] It's a sort of fundamental-- um way of proceeding. The first step is to have an odd number of servers instead of an even number of servers. Like, one flaw here is that it's a little bit too symmetric. The two sides of the split here, they just look the same. They run the same software, they're going to do the same thing, and that's not good. But if you have an odd number of servers, Then, It's not symmetric anymore. Right. At least a single network split will be presumably two servers on one side and one server on the other side and they won't be symmetric at all.

[11:57] And that's part of what majority voting schemes are. appealing to. So, basic idea is you have an odd number of servers, in order to make progress of any kind, so in Raft, elect a leader, or cause a log entry to be committed, in order to make any progress at each step, You have to assemble a majority... of the servers, more than half, more than half of all the servers, in order to sort of approve that step, like vote for a leader, accept a new log entry and commit it. Um, So, you know, the most...

[12:29] uh... Straightforward way is to have two or three servers required to do anything. Um, One reason this works, of course, is that If there's a partition, There can't be more than one partition with a majority of the servers in it. That's one way to look at this. A partition can have one server in it, which is not a majority. Or maybe you can have two, but if one partition has two, then the other partition has to have only one server in it. and therefore will never be able to assemble a majority and won't be able to make progress.

[13:03] Um, And just... To be totally clear, When we're talking about a majority, it's always a majority out of all of the servers. not just the live servers. This is a point that confused me for a long time, If you have a system with three servers, and maybe some of them have failed or something, If you need to assemble a majority, it's always two out of three. even if you know that one has failed. The majority is always out of the total number of servers. um, There's a more general formulation of this. Because of majority voting system in which two out of three are required to make progress, it can survive the failure of one server.

[13:40] Right. Any two servers are enough to make progress. If you need to be able to, if you're, you know, you're worried about how reliable your servers are or God. you can build systems that have more servers, and so the more general formulation is If you have two f plus one servers, then you can withstand F failures. Bye. So if it's three, That means f is one. In a system with three servers, you tolerate F servers, F1 failure and still keep going.

[14:16] Thank you. Thank you. Thank you. All right. Often these are called quorum systems because the two out of three is sometimes held a quorum. Thank you. Okay, so one property I've already mentioned about these majority voting systems is that Um, At most one partition can have a and therefore if the network's partitioned, We can't have both halves of the network making progress. Another more subtle thing that's going on here is that Um, If you always need a majority.

[14:49] of the servers to proceed. And you go through a sort of succession of operations in which reach operation somebody assembled a majority like. you know, votes for leaders or Let's say votes for leaders for Raft. At every step, the majority you assemble for that step must contain at least one server that was in the previous majority. any two majorities overlap in at least one server. And it's really that property, more than anything else that Raft is relying on.

[15:22] Um, to avoid split brain. It's the fact that, for example, when you have a leader, a successful leader election, and a leader assembles votes from a majority, its majority is guaranteed to overlap with the previous leader's majority. So, for example, the new leader is guaranteed to know about the term number used by the previous leader. because its majority overlaps with the previous leader's majority and everybody in the previous leaders majority knew about the previous leaders term number. Similarly, anything the previous-- leader could have committed must be present in a majority of the servers in Wrapped, and therefore any new leader's majority must overlap at at least one server.

[16:01] with every committed entry. from the previous leader. This is a big part of Why it is that... Wrapped is correct. Any questions about... the general concept of majority voting system. Yeah? Is it also common to add servers? Like add the processing ? JONATHAN GRUBER: Is it possible to add servers? I think it's possible. It's possible, you know, section something, maybe six in the how to add or change the set of servers.

[16:41] It's possible. You need to do it in a long running system. If you're running your system for five, 10 years, you're going to need to replace the servers after a while. you know, one of them fails permanently, or you upgrade, or you move machine rooms to a different machine room. You really do need to be able to support changing sets of servers. So that's it. It certainly doesn't happen every day, but it's a critical part of this, or long-term maintainability of these systems. Um, And the RAF authors sort of pat themselves on the back There you have it.

[17:12] a scheme that deals with this, which as well they might because it's complex. Thank you. Thank you. All right, so using this idea, in about 1990 or so, there were two systems proposed at about the same time. that realize that You could use this majority voting system to kind of get around the apparent impossibility of avoiding split brain. by using, basically by using three servers instead of two, and taking majority votes.

[17:44] Um, in the one of these very early systems was called Paxos The Raft paper talks about this a lot. Um, And another of these very early systems was called view stamp replication. Ciao. abbreviated as VSR for view stamp replication. And even though Paxos is by far the more widely known system in this department, Raft is actually closer to design in design to view step by few stamp replications. which was invented by people at MIT.

[18:15] Um, And so there's sort of a long, many decade history of these systems and they only really came to the forefront and started being used a lot in deployed big distributed systems about 15 years ago. a good 15 years after they were originally invented. Okay, so... So let me talk about Rath now. Raft takes the form of a library intended to be included in some service application.

[18:50] And so if you have a replicated service, each of the replicas in the service is going to be some application code which receives RPCs or something, plus a raft library. And the raft libraries cooperate with each other Um, to maintain replication. So, a sort of software overview of a single-raft replica is that at the top we can think of the replica as having the application code. So it might be for lab three, a key value server. So maybe we have some key value server.

[19:22] And it has state, the application has state that raft is helping it manage, replicated state. And for a key value server, it's going to be a table of keys and values. Um... *knocking* The next layer down is a raft layer. So the key value server is going to make function calls into RAPT, and they're going to chit chat back and forth a little bit. raft keeps a little bit of state. You can see it in figure two. And for our purposes, really the most critical piece of state is that raft has a log of operations.

[20:01] Thank you. Okay. Thank you. Amen. And in a system with three replicas, we're actually going to have three servers that have exactly the same identical structure and hopefully the very same data sitting in. sitting at both layers. Thank you. Amen. Thank you. Thank you. Thank you. Thank you. Thank you.

[20:31] Thank you. Right. Outside of this, there's going to be clients. And the game is that So we have client one, client two, a whole bunch of clients. The clients don't really know, the clients are, you know, External code that needs to be able to use the service. and The hope is the clients won't really need to be aware that they're talking to a replicated service. That to the clients it'll look almost like it's just one server and they talk to one server. And so the clients actually send client requests to The key to the application layer.

[21:05] of the current leader, the replica that's the current leader in Raft. And so these are going to be application level requests for a database for a key value server, these might be put and get requests. put takes a key and a value and updates the table and get ask the service to get the current key, current value corresponding to Some key. So this has nothing much to do with Raft. It's just client-server interaction for whatever service we're building.

[21:39] Um, But once one of these commands gets sent from the request, gets sent from the client to the server, what actually happens is on a non-replicated server, the Application code would like execute this request and say update the table in response to a put. but not in a RAPT-replicated service. Instead, if assuming the client sends a request to the leader, what really happens is the application layer. simply sends the request, the client's request, down into the RAFT layer to say, "Look, here's a request.

[22:10] Please get it committed. into the replicated log and tell me when you're done. And so at this point, the rafts chit chat with each other. Um. until all the replicas or a majority of the replicas get this new operation into their logs So it is replicated, and then when it's And the leader knows that all of the replicas of a copy of this only then is wrapped send a notification up, back up to the key value layer saying, "Aha, that operation you sent me a minute ago, It's been now committed into all the replicas And so it's safely replicated and at this point it's okay to execute that operation.

[22:53] It's a raft. The client sends a request to the key value layer, QDialyLayer does not execute it yet. So we're not sure. because it hasn't been replicated. Only when it's in the logs of all the replicas. Bye. Then Raph notifies the leader, and now the leader actually executes the operation, which corresponds to... you know, for a put, updating the key value table for a get, reading the correct value out of the table, and then finally sends the reply back to the client.

[23:26] So that's an ordinary operation of the Thank you. . It's submitted if it's in a majority. And again, the reason why I can't be all is that if we want to build a fault tolerance system, It has to be able to make progress even if Some of the servers have failed. So-- Um, Yeah. It's committed when it's in a majority. Thank you. Yes. So before the leader executes the request markets, devalue properties.

[24:00] Um... that have been replicated. Thank you. Wow. - I'm sorry. Thank you. leaders Yeah, I got that. And so. In addition, when the operations finally committed, each of the replicas sends the operation up. Each of the wraths Library layer sends the operation up to the local application layer, and the local application layer applies that operation to its state. And so they all... So hopefully all the replicas seem the same stream of operations.

[24:31] they show up in these up calls in the same order, they get applied to the state in the same order, and you know, assuming the operations are deterministic, which they better be, Um, The state... of the replicated state will evolve. in identically on all the replicas. So typically this table is what the paper is talking about when it, Talks about state. Thank you. a different way of viewing this interaction and one that'll sort of notation that will come up a lot in this course is that Um, A sort of time diagram, I'll draw you a time diagram of how the messages work.

[25:12] So let's imagine we have a client, and Server one is the the leader, and we also have server two. Thank you. Server three, and time flows downward on this diagram. We imagine the client sending the original request. to server one Thank you. After that, server one's wrapped layer. Um, sends an append entry, it's RPC. Um, to each of the two replicas. Thank you. This is just an ordinary, let's say, put request.

[25:45] This is, these are PEND entries requests. Thanks. The server's now waiting for replies. from the servers, from the other replicas, as soon as replies from a majority, a ride back, including the leader itself. So in a system with only three replicas, the leader only has to wait for one other replica. to respond positively to an appendentary as soon as it assembles. positive responses from a majority. Um, the Leader executes the command, figures out what the, Answer is like forget.

[26:20] um and sends the reply back to the... And meanwhile, of course, you know, if S2 is actually alive, it'll, you know, Sendback gets response too, but we're not waiting for it. Thank you. It's useful to know in figure two. Thank you. All right, everybody see this? Thank you. This is the sort of ordinary operation of the system. No failures here. Oh, okay. Gosh, yeah. I like...

[26:53] I left out-- important step. At this point, the leader knows, oh, I got a majority of put it in their log, I can go ahead and execute it and reply yes to the client because it's Server two doesn't know anything yet. It just knows, well, you know, I got this request from the, but I don't know if it's committed yet. Depends on, for example, whether my reply got back to the leader. For all Server 2 knows, its reply was dropped by the network, Maybe the leader never heard the reply and never decided to commit this request. There's actually another stage, once the, Server.

[27:24] realizes that a request is committed, it then needs to tell the other replicas that fact. And Amen. So there's... Uh... There's an extra message here. Exactly what that message is depends a little bit on what else is going on. at least in RAF, there's not an explicit commit message. Instead, the information is piggybacked inside the next append entries that the leader sends out. The next append entries RPC it sends out for whatever reason.

[27:58] Like there's a, commit, leader commit or something field in that RPC. and The next time the leader needs to have to send a heartbeat or something, needs to send out a new client request because some different Client. request something, it'll send out the Um, New, higher things. leader commit value and at that point the, uh, Um, yeah. replicas will execute the operation and apply it to their state. Yes.

[28:29] Thank you. Thank you. Oh, yes. So this is a, This is a protocol that has quite a bit of chit-chat in it. Um, and it's not, Super fat. Indeed, you know, yeah, the client sends in a request, the request has to get to the server, the server talks to at least, you know, another... It sends out multiple messages, has to wait for the responses, sends something back. So there's a bunch of message round-trip times kind of.

[29:02] embedded here. would have made sense to the second part of the faculty. Thank you. Yeah, so... If... So, This is up to you as the implementer actually exactly when the leader, sends out the updated commit index. If-- Client requests a um, come back only very occasionally. then the leader may want to send out a heartbeat or send out a special append entries message.

[29:34] Um, If client requests come quite frequently, then it doesn't matter, If they come, you know, if there's a thousand arrive per second, then, geez, there'll be another one along very soon. And so you can piggyback. So without generating an extra message, which is somewhat expensive. you can get the information out on the next message you are going to send anyway. Um, In fact, I don't think the, time at which the replicas execute the request - It's critical because nobody's waiting for it, at least if there's no failures. If there's no failures, the replicas executing the request isn't really on the critical path.

[30:12] Like the client isn't waiting for them, the client's only waiting for the leader to execute. So it may not be that important. um It may not affect client perceived latency, sort of exactly how this gets staged. Um. Bye. Thank you. Thank you. Thank you. Thank you. Thank you. Thank you. All right. One question you should ask is why is the system so focused on logs? What are the logs doing?

[30:51] And it's sort of worth trying to come up with explicit answers to that. One answer to why the system is totally focused on logs. is that The log is, the kind of mechanism by which the leader orders operations. It's vital for these replicated state machines that all the replicas apply not just the same client operations to their start, but the same operations in the same order. But they all have to apply these operations coming from the clients in the same order. And the log, among many other things, is part of the machinery by which the leader assigns an order to the incoming client operations.

[31:32] you know, 10 clients send operations to the leader. At the same time, the leader has to pick Pick an order, make sure everybody, all the replicas obey that order. And the log is The fact that the log has numbered slots. is part of how the leader expresses the order it's chosen. Um, Thank you. Another use of the log is that between this point and this point, Server 3 has received an operation that it is not yet sure is committed. and it cannot execute it yet. It has to put this operation aside somewhere.

[32:06] until the increment to the leader commit value comes in. And so, another thing that the log is doing is that, on the followers, the log is the place where the follower sort of sets aside operations that are still tentative. that have arrived but are not yet known to be committed. And they may have to be thrown away, as we'll see. Um, So that's another use. On the sort of dual of that use on the leader side is that the leader needs to remember operations in its log because it may need to retransmit them.

[32:38] to followers. If some follower is offline, maybe it's something briefly happened to its network connection or something, misses some messages. The leader needs to be able to resend blog messages that any followers miss. And so the leader needs a place where I can set aside copies of messages of client requests, even ones that it's already executed. in order to be able to resend them to the client. I mean, we send them to replicas that missed miss that operation. And a final reason for all of them to keep the log is that at least in the world of figure two, If a server crashes and restarts and wants to rejoin um You really want a server that crashes to in fact restart and rejoin.

[33:20] the wrapped cluster, otherwise you're now operating with only two out of three servers and you can't survive any more failures. We need to reincorporate failed and rebooted servers, and the log is sort of where, what a server, a rebooted server uses. The log persisted to its disk. Because one of the rules is that Each Rath server needs to write its log to its disk, where it will still be after it crashes and restarts. That log is what the server uses, it replays the, operations in that log from the beginning to sort of create its state as of when it crashed.

[33:52] and then it carries on from there. So the log is also used as part of the persistence plan. as a sequence of commands to rebuild the state. Yes. So I'm wondering what scenarios would arise where the workers are trying to follow up with it. Uh... Well, ultimately, Okay, so the question is, Suppose the leader is capable of executing a thousand client commands a second and the followers are only capable of executing a hundred client commands per second. That sort of sustained rate at full speed.

[34:32] um The, so one thing to note is that the, the The replicas, the followers, acknowledge commands before they execute them. So the rate at which they acknowledge and accumulate stuff in their logs is not limited. So, you know, maybe they can acknowledge it, a thousand requests per second. If they do that forever, then they will build up. unbounded size logs because their execution rate falls will fall an unbounded amount behind the rate at which the leader has given the messages, sort of under the rules of our game.

[35:06] And so what that means is it will eventually run out of memory. At some point, after they have a billion, after they fall a billion log entries behind, they'll just like, they're called a memory allocator. for space for a new log entry and it will fail. Um, So yeah, and RAF doesn't, um RAP doesn't have the flow controls that's required to cope with this. So I think... in a real system, you would actually need probably piggybacked and doesn't need to be real time, but you probably need some kind of um, additional communication here that says, "Oh, here's how far I've gotten in execution.

[35:47] So that the leader can say, well, I'm too many thousands of requests ahead of the point at which the followers have executed. Yeah, so I think there's probably in a production system that you're trying to push to the absolute max. you might well need an extra message to throttle the leader if it got too far ahead. Thank you. Okay.

[36:33] - So the question is, If one of these servers crashes, It has this log that it persisted to dis, 'cause that's one of the rules of figure two. um So the server will be able to be just logged back from disk, but of course, That server doesn't know how far it got in executing the log. And also it doesn't know, at least when it first reboots, by the rules of Figure 2. It doesn't even know how much the log is committed. So the first answer to your question is that immediately after a restart, after a server crashes and restarts and reads this log, it is not allowed to do anything with the log.

[37:09] because it does not know how far the system has committed in its log. Maybe this log has a thousand uncommitted entries and zero committed entries for all So... If the leader dies, of course, that doesn't help either. Let's suppose they've all crashed. This is getting ahead of... It's getting a bit ahead of me, but we'll suppose they've all crashed, and so all they have is the state that was marked as non-volatile in figure two, which includes the log and maybe the latest term.

[37:43] Um, And so they don't know. So if there's a crash, if they all crash and they all restart, None of them knows. initially how far they had executed before the crash. So what happens is, they do leader election, one of them gets picked as a leader. And that leader, if you sort of track through what figure two says about how append entries are supposed to work. the leader will actually figure out as a byproduct of sending out the first heartbeat really.

[38:14] Um, It'll figure out what the latest point is basically that that Amen. all of the, that a majority of the replicas agree on their law. Because that's the commit point. Another way of looking at it is that once you choose a leader, through the append entries mechanism, the leader forces all of the other replicas to have identical logs to the leader. And at that point, plus...

[38:45] A little bit of extra, the paper explains. At that point, since the leader knows that it's forced all the replicas to have a have logs that are identical to it. Then it knows that, oh, all the replicas must also have a-- There must be a majority of of replicas with- that all those log entries in the logs which are now identical must also be committed because they're held on a majority of replicas. Um, And at that point, the leader-- append entries code described in figure two for the leader will increment the leader's commit point and and everybody can now execute the entire log from the beginning.

[39:22] and recreate their state from scratch. possibly extremely laboriously. Um... So that's what figure two says. It's obviously this re-executing from scratch is not very attractive, but That's what the basic protocol does. And we'll see tomorrow. version of this is more efficient, uses checkpoints and We'll talk about it tomorrow. Thank you. Okay, so this was a sequence in sort of ordinary, non-failure operation.

[39:53] Um, um... Another thing I want to briefly mention is what this interface looks like. You've probably all seen a little bit of it. due to working on the labs, but roughly speaking, if you have, let's say, this key value layer, with its state. and the raft layer underneath it, there's On each replica there's really two main pieces. of the interface between them. There's this... Um, method by which the key value layer can relay. If a client sends in a request, the key value layer has to give it to Rath and say, "Please, you know, fit this request into the log somewhere, and that's the start function.

[40:34] Um. that you'll see in raft.go. And really it just takes one argument, the client command. Thank you. The key value layer is saying, "Please, I got this command. Stick it into the log and tell me when it's committed." And the other piece of the, interface is that by and by, The raf layer will notify the key value layer that, aha, that operation that you sent to me in a start command a while ago, which may well not be the most recent start. A hundred client commands could come in and cause calls to start before any of them are committed.

[41:09] by and by Um, this-- upward communication is takes the form of a message on a Go channel that the Raph library sends on and the key value layer. is supposed to read from. Um. So there's this apply, Thank you. Thank you. It's called the apply channel, and on it you send an apply message. Thank you. I don't know. Thank you. Thank you. Thank you. this start and of course you need the Key value layer needs to be able to match up messages it receives on the Apply channel with calls to start that it made.

[41:47] Um, And so the start command actually returns enough information for that matchup to happen. It returns the index the start functions basically returns the index in the log where if this command is committed, which it might not be, it'll be committed at this index. Um, And I think it also returns the current term, and some other stuff we don't care about very much. And then this apply message is going to, contain the index. both the Command.

[42:20] Um. Thank you. Thank you. Thank you. Thank you. Thank you. and the index. And all the replicas will get these apply messages, so they'll all know, "Oh, I should apply this command." um figure out what this command means and apply it to my local state. And they also get the index. The index is really only useful on the leader so it can, figure out. what client requests we're talking about. Thank you. I'm curious about the following. The client is sending their request to the back, rock leader any place.

[42:59] at which-- clients by making a get-go. Right. about that. OK. OK. Um, I think I answered a slightly different question. Let's suppose the client sends any request in, Let's say it's a put or a get. It could be a put or a get. It doesn't really matter. I'll take it to get. Um, the point at which the So the client sends in again and waits for a response. The point at which the leader will send a response at all is after the leader knows that command is committed.

[43:40] So this is going to be a sort of get reply. Bye. *knocking* Bye. So the client doesn't see anything back. I mean it, and so, um, That means in terms of the actual software stack, That means that the key value, the RPC will arrive, the key value layer will call the start function. The start function will return to the key value layer But-- The key value layer will not yet reply to the client because it does not know if it's Actually, it hasn't executed the client's request now. It doesn't even know if it ever will.

[44:14] because it's not sure if the request is going to be committed. And the situation in which it may not be committed is if the key value layer, you know, gets a request, calls start, and immediately after start returns, it crashes. It certainly hasn't sent out its apply, pen messages, or whatever. Nothing's been committed yet. Um, So the game is start returns. time passes, the relevant, apply message corresponding to that client request appears to the key value server on the apply channel and only then and that causes the key value server to execute the request And-- Send a reply.

[44:52] Thank you. Thank you. Thank you. Thank you. Thank you. And that's like-- all this is It's very important when it It doesn't really matter if everything goes well. But if there's a failure, we're now-- At the point where we start worrying about failures, we'll be extremely interested in, if there was a failure, what did the client see? Um, Thank you. All right. And so one thing that has come up is... that's, um, all of you should be familiar with is that, at least initially, one interesting thing about the logs is that they may not be identical.

[45:30] There are a whole bunch of situations in which, at least for brief periods of time, the ends of the different... replica's logs may diverge, like for example, if a leader starts to send out a round of append messages, but crashes before it's able to send all of them out. That'll mean that some of the replicas that got the append message will append you know, that new log entry, and the ones that didn't get that pin message as RPC, won't have appended them. So it's easy to see that the logs are going to diverge sometimes. The good news is that, the The way Raft works actually ends up forcing the logs to be identical after a while.

[46:07] 'cause there may be transient differences, but in the long run, all the logs will sort of be modified by the leader until the leader ensures they're all identical and only then Um. Okay. I think the next, there's really two big topics. to talk about here for Raft. One is how leader election works, which is Lab 2. And the other is, how the leader deals with the different replicas logs, particularly after failure.

[46:38] So first I want to talk about leader election. Amen. Thank you. Question to ask is how come the system even has a leader? Why do we need a leader? that Part of the answer is you do not need a leader to build a system like this. It is possible to build. an agreement system by which a cluster of servers agrees on, you know, the sequence of entries in a log without having any kind of designated leader. And indeed, The original Paxos system, which the paper refers to, original Paxos did not have a leader.

[47:11] Um, So it's possible. The reason why Raft has a leader is basically that Um, There's probably a lot of reasons, but one of the foremost reasons is that you can build a more efficient in the common case in which all The servers don't fail. It's possible to build a more efficient system if you have a leader. Because with a designated leader, everybody knows who the leader is, You can basically get agreement on requests with one round of messages per request. Whereas leaderless systems have more of the flavor of, well, you need a first round to kind of agree on a temporary leader, and then a second round to actually send out the request.

[47:49] So, It's probably the case that use of a leader speeds up the system by a factor of two. Thank you. And it also makes it sort of easier to think about what's going on. Thank you. Um, Raft goes through a sequence of leaders, And It uses these term numbers in order to sort of disambiguate which leader we're talking about. It turns out the followers don't really need to know the identity of the leader. They really just need to know what the current term number is.

[48:20] Each term has at most one leader. That's it. critical property You know, for every term, there might be no leader during that term, or there might be one leader, but there cannot be two leaders. during the same term. Every term has at most one leader. Um, Thank you. Thank you. How do the leaders get created in the first place? Every... wrapped server. keeps this election timer, which is just a It's basically just a time that it has recorded that says, well, if that time occurs, I'm going to do something.

[48:55] um And something that it does is that if an entire leader election period expires, without the server having heard any message from the current leader then the server sort of assumes probably that the current leader is dead and starts an election. So we have this election timer. And if it expires, start an election. Thank you. .

[49:26] Thank you. Um, And what it means to start an election is basically that you increment the term the candidate, the server that's decided it's going to be a candidate and sort of force a new election, first increments this term because, you know, wants her to be a new leader, namely itself, And a term can't have more than one leader, so we've got to start a new term. in order to have a new leader. And then it sends out these request votes, RPCs.

[49:57] Thank you. I'm going to send out a full round of request votes. And you may only have to send out n minus one request votes, because one of the rules is that a new candidate always votes for itself. in the election. Um, So. One thing to note about this is that It's not quite the case that if a leader didn't fail, we won't have an election. or if the leader does fail, then we will have an election assuming any other server is up, because someday the other server's election timers will go off.

[50:28] But if the leader didn't fail, we might still, unfortunately, get an election. So if the network is slow or drops a few heartbeats or something, we may end up having election timers go off, and even though there was a perfectly good leader, we may nevertheless have a new election. So we have to sort of keep that in mind when we're thinking about correctness. Um, And what that in turn means is that if there's a new election, it could easily be the case that the old leader is still hanging around and still thinks it's the leader. Like, if there's a network partition, for example, And the old leader is still alive and well in a minority partition.

[51:02] The majority partition may run an election, and indeed a successful election, and choose a new leader. totally unknown to the previous leader. So we also have to worry about, you know, oh, what's that previous leader going to do since it does not know there was a new election? Thank you. Very much. Thank you. Thank you.

[51:43] So the question is, Are there, can there be pathological cases in which, for example, One way network communication can prevent the system from making progress. I believe the answer is yes. Certainly. So for example, if the current leader, If its network somehow half fails in a way the current leader can send out heartbeats but can't receive any client requests. then the heartbeats that it sends out, which are delivered because it's outgoing network connection works. It's outgoing heartbeats will suppress any other server from starting an election.

[52:20] But the fact that it's incoming network wire apparently is broken will prevent it from hearing and executing any client commands. It was absolutely the case that RAPT is not proof against all um sort of all crazy network problems that can come up. I believe the ones I've thought about, I believe are fixable in the sense that the We could solve this one by having a sort of requiring a two-way heartbeat in which if the leader sends out heartbeats but in which Followers are required to reply in some way to heartbeats, like as they are already required to reply.

[52:58] But if the leader stops seeing replies to its heartbeats, Then after some amount of time in which the season was replaced, the leader decides to step down. I feel like that's specific issue. can be fixed. and many others can too, but I, but, Yeah. you know You're absolutely right. Very strange things can happen to networks, including some that the protocol is not prepared for. Thank you. Thank you. Thank you.

[53:29] Okay, so we got these meter elections. We need to ensure that there is at most, at most, one leader per term. How does RAF do that? Well, RAF requires, in order to be elected for a term, RAF requires a candidate to get yes votes from a majority of the servers. The servers and each server will only cast one yes vote per term. So in any given term, you know, it basically means that in any given term, each server votes only once for only one candidate, Um, You can't have two candidates both get a majority of votes because Everybody votes only once.

[54:06] Um, So the majority's-- The majority rule causes there to be at most one winning candidate. Um, and So then we get at most one. candidate elected per turn. Um... And in addition, critically, the majority rule means that you can get elected even if some servers have crashed. If a minority of servers are crashed or unavailable, have network problems, we can still elect a leader. If more than half have crashed or are not available or are in another partition or something, then Actually, the system will just sit there trying again and again to elect a leader.

[54:46] and never elect one. if it cannot in fact, if they're not a majority of live servers. um Thank you. If an election succeeds, Everybody, it'd be great if everybody learned about it. 7 minutes or so. We need to ask ourselves how do all the parties learn what happened? The server that wins an election, assuming it doesn't crash, The server that wins election we'll actually see a majority or positive votes for its request vote from a majority of the other servers. The candidate that's running the election that wins it, the candidate that wins the election will actually know directly, I got a majority of votes.

[55:23] But nobody else directly knows who the winner was or whether anybody won. Um, So the way that the candidate informs other servers is that heartbeat. the rules in Figure 2 say, oh, if you win an election, you're immediately required to send out independentries. to all the other servers. Now, dependentries that heartbeat dependent entries doesn't explicitly say, I won the election, you know, I'm a leader for term 23. Um, It's a little more subtle than that. the way the information is communicated is that Um, No one is allowed to send out and append entries unless they're a leader for that term.

[56:02] So the fact that I'm a, you know, I'm a, Server and I saw, oh, there's an election for term 19. And then by and by, I send in append entries whose term is 19, That tells me that somebody, I don't know who, but somebody won the election. Um, so that's how the other servers know, is they were receiving append entries for that term. And that append entries also has the effect of, resetting everybody's election time. timer. So as long as the leader is up and it sends out heartbeat messages or append entries at least, you know, at the rate it's supposed to, Every time a server receives an append entry, it'll reset its selection timer and sort of suppress anybody from being a new candidate.

[56:46] So as long as everything's functioning, the repeated heart beats will prevent any further elections. Of course, if the network fails or packets are dropped, there may nevertheless be an But if all goes well, we're sort of unlikely to get an election. Um, This scheme could fail in the sense, well, it can't fail in the sense of electing. two leaders for a term, but it can fail in the sense of electing zero leaders for a term. Um, The sort of morning way it may fail is that if too many servers are dead or unavailable or have bad network connections. So if you can't assemble a majority, you can't be elected, nothing happens.

[57:22] Um, The more interesting way in which an election can fail is if Everybody's up. You know, there's no failures, no packets are dropped. But-- two leaders become candidate close together enough in time that they split the vote between them. or say three liters. Um, So supposing we have three leaders. Supposing we have a three replica system Um, All their election timers go off at the same time.

[57:53] Every server votes for itself. And then when each of them receives a request vote from another server, well it's already cast its vote for itself and so it says no. So that means that all three of the servers each get one vote each, nobody gets a majority, and nobody's elected. Um, So then-- Their election timers will go off again because The election timer is only reset if it gets an append entries, but there's no leader, They'll all have their election timers go off again. And if we're unlucky, they'll all go off at the same time. They'll all vote for themselves. Nobody will get a majority. uh So.

[58:24] So clearly, I'm sure you're all aware at this point, there's more to this story. And the way raft D-- makes the possibility of split votes unlikely, but not impossible, is by randomizing these election timers. So the way to think of it And the randomization, well, The way to think of it is that, so we have some timeline, I'm going to, um, draw events on. There's some point at which everybody received the last append entries.

[58:56] And then maybe the server died. Let's just assume the server sent out a last heartbeat and then died. Well, all of the followers have-- or reset their election timers when they received, at the same time, because they probably all received the append entries at the same time. They all reset their election timers. for some point in the future, to go off at some point in the future. they chose different random times in the future which then were gonna go off. So I suppose the dead leader is server one, So now server two and server three at this point, set their election timers for a random point in the future. Let's say server two set their, um, I'd like some timer to go off here.

[59:38] and server three set. It's election timer to go off there. and The crucial point about this picture is that assuming they pick different random numbers, One of them is first and the other one is second. Right? That's what's going on here. And the one that's first, assuming This gap is big enough. the one that's First, this election timer will go off first before the other one's election timer. as long as we're not unlucky, it'll have time to send out a full round of Vote requests.

[1:00:10] and get answers from everybody where everybody's alive before the second election. timer goes off from any other server. Um, So. Does everybody see? how the randomization desynchronizes these candidates. Unfortunately, there's a bit of art in setting the constants for these election timers. There's some sort of competing requirements you might want to fulfill. Um, So one obvious requirement is that the election timer has to be at least as long as the expected interval between heartbeats.

[1:00:48] This is pretty obvious that the leader sends out heartbeats every 100 milliseconds you better make sure, you know, there's no point in having the election timer anybody's election timer ever go off for 100 milliseconds. because then it will go off before we could even have expected a new append entry. So the lower limit is certainly the lower limit is one heartbeat interval. In fact, because the network may drop packets. You probably want to have the minimum election timer value be a couple of times the heartbeat interval. So for 100 millisecond heartbeats, you probably want to have the very shortest possible election timer be say, 300 milliseconds.

[1:01:25] three times the heartbeat interval. Um, So that's the sort of minimum is the... Um, See if the heartbeats are this frequent, you want the minimum to be a couple of times that or here. Um, so what about the maximum? You know, you're gonna presumably randomize uniformly over some range of times. You know, where should we set the-- kind of maximum. Thank you. that we're randomizing over. And there's a couple of...

[1:01:55] considerations here in a real system. um, you know, this maximum time, uh... affects how quickly the system can recover from failure because Remember, from the time at which the server fails, until the first election timer goes off, The whole system is frozen. There's no leader. You know, the client's requests are being thrown away because there's no leader. and we're not assigning a new leader even though you know Presumably these other servers are up.

[1:02:26] So the bigger we choose this maximum, the longer delay we're imposing on clients before recovery occurs. You know, whether that's important, depends on sort of how high performance we need this to be, and how often we think there will be, Failure. If failure's happening once a year, then who cares? Um, If we're expecting failures frequently, we may care very much. how long it takes to recover. Okay, so that's one consideration. The other consideration is that this gap That is the expected gap in time between the first timer going off and the second timer going off.

[1:03:03] Um, This gap really, in order to be useful, has to be longer than the time it takes for the candidate to assemble votes from everybody. That is... longer than the expected round trip time, the amount of time it takes to send an RPC and get the response. Yeah, maybe it takes 10 milliseconds. to send in RPC and get a response, get a response from, um, all the other servers. And if that's the case, we need to make maximum release long enough that there's pretty likely to be 10 milliseconds difference between the smallest random number and the next smallest random number.

[1:03:35] Um, Thank you. And for you, Um, the test code will get upset if you, uh, um Thank you. if you don't recover from a leader failure in a couple seconds. And so, just pragmatically, you need to tune this maximum down so that It's highly likely that. You'll be able to complete a leader election within a few seconds. That's not a very tight constraint.

[1:04:09] Any questions about the election timeouts? One tiny point is that, um, You want to... Choose new random timeouts. every time there's, every time you Every time I know-- resets its election timer. That is, Don't choose a random number when... server is first created and then reuse that same number over and over again. Because you make an unlucky choice that is You choose the same.

[1:04:39] one server happens by ill chance to choose the same random number as another server. That means that you're gonna have split votes over and over again. forever. So that's why you want to almost certainly choose a different, a new, a fresh random for the election timeout value every time You reset the timer. Thank you. All right, so, a final issue about leader election. Suppose we are in this situation where the old leader's partitioned.

[1:05:10] You know, the network cable is broken, and the old leader is sort of out there with a couple clients. and a minority of servers. There's a majority in the other half of the network, and the majority in the new half of the network elects a new leader. What about the old leader? Why won't the old leader... Thank you. cause incorrect execution. Thank you. Maybe it's Yeah, right there.

[1:05:56] That's how that's gonna... - I'm glad to. Bye. and that's all done. God. Thank you. Yeah. two potential problems. Or one sort of non-problem is that if there's a leader off in another partition and it doesn't have a majority, then the next time a client sends it a request, Um, that leader that, you know, an abarctuation with a minority, yeah, it'll send out append entries, but Because it's in the minority partition, it won't be able to get responses back from a majority of the servers, including itself. And so it will never...

[1:06:34] commit the operation, it'll never execute it. It'll never respond to the client. saying that it executed it either. And so that means that, yeah, an old server off in a different partition People may, clients may send a request, but they'll never get responses. um, So no client will be fooled into thinking that the, that old server executed anything for it. The other sort of more tricky issue, which actually I'll talk about in a few minutes, is the possibility that before a server fails, it sends out append entries to a subset.

[1:07:14] of the servers. and then crashes before making a commit decision. And that's a very interesting question, which um We'll probably spend a good 45 minutes talking about that. And so, actually, before I turn to the back, topic in general. more questions about leader election. Thank you. OK. um Okay, so how about the contents of the logs and how in particular how a newly elected leader possibly picking up the pieces after an awkward crash of the previous leader, How does a newly elected leader sort out the possibly divergent logs on the different replicas?

[1:08:01] in order to restore sort of consistent state in the system. Um, All right, so the first question is, What can things, this whole topic is really only interesting after a server crashes, right? If the server stays up, then relatively few things can go wrong. If we have a server that's up and has a majority, you know, during the period of time when it's up and has a majority, Um, just tells the followers what the log should look like.

[1:08:36] And the followers are not allowed to disagree. They're required to accept. They just do by the rules of figure two if they've been more or less keeping up. You know, they just take whatever the leader sends them, and append entries, and append it to the log, and obey commit messages, and execute. Hardly anything to go wrong. The things that go wrong in rap go wrong when the old leader crashes sort of midway through you know, sending out messages or a new leader crashes, you know, sort of just after it's been elected, Um... before it's done anything very useful.

[1:09:08] So, one thing we're very interested in is what can the logs look like after some sequence of crashes? Um... Okay. Here's an example. So then we have three servers. Thank you. Thank you. Um, And the way I'm gonna draw out these diagrams, 'cause we're gonna be looking at a lot of, sort of situations where the logs look like this, and we're going to be wondering, is that possible, and what happens if they do look like that? So, my notation's going to be, I'm going to write out log entries for, um, Each of the servers sort of aligned to indicate slots.

[1:09:46] corresponding slots in the log. And the values I'm gonna write here are the term numbers rather than client operations. I'm going to, you know, this is slot one, this is slot two. Um, Everybody saw a command from term three in slot one, Server 2 and Server 3 saw command from also from term three, In the second slot, the server one has nothing there at all. Um, And so the question for this, like the very first question is, can this arrive?

[1:10:18] Could this... set up Arise and how? Oh. How? Thank you. Yeah, so . Thank you. So.

[1:11:03] So maybe server three was the leader for, just repeating what you said, maybe server three is the leader for term three. It got a command that sent out to everybody. Everybody received the dependent as the log. And then I got a server three, Got a second request from a client. and Maybe it sent it to all three servers, but the message got lost on the way to server one, or maybe server one was down at the time or something, and so only server two Well, the leader always appends new commands to its log before it sends out append entries. Um, and maybe the append entry RPC only got to server two. So this situation, you know, It's like the simplest situation in which actually the logs are now different.

[1:11:38] And we know how it could possibly... arise. And so if server three, which is the leader, should crash now, you know, the next server they're going to need to make sure server one Well, First of all, If server 3 crashes, or we get an election and some other leader is chosen, You know, two things have to happen. The new leader... It's got to recognize that This. Um, a command could have committed. It's not allowed to throw it away. and it needs to make sure server one fills in this blank here with indeed this very same command that everybody else had in that slot.

[1:12:15] Um, Thank you. All right, so after a crash, somebody, you know, a server suppose, another way this can come up is server three might have sent out the append entries to server two, but then crashed before sending the append entries to server three. So if we're electing a new leader, it could be because We got a crash. before the message was sent. All right, here's another scenario to think about. Um, Three servers again. Thank you. Thank you. Thank you. Um, Now I'm going to number the slots in the lawn.

[1:12:47] So we can refer to them. Um, We've got slot 10, 11, 12. Thank you. 13. Again. That's... Thank you. same setup except Now we have in slot 12, we have Server 2 as a command from term four and server three has a term command from term five. Thank you. Thank you. So-- Before we analyze these to figure out what would happen, what would a server do if it saw this, we need to ask Could this even occur?

[1:13:25] Sometimes the answer to the question, oh geez, what would happen if this configuration arose? Sometimes the answer is, It cannot arise, so we do not have to worry about it. um So the question is, Could this arise and how? Yeah. say no, because we have a lot of . Okay. Thank you. Thank you. Um, All right.

[1:14:00] And any-- All right. Thank you. The first situation happens. Amen. watching. friends. - Now, Troy. Bye. Roger. Thank you. What? Uh... Right, sensei.

[1:14:32] guys. Thank you. - Oh my God. All right. back up. Yeah. How far? So let me just repeat that. Brief. we know this configuration can arise, and so the way we can then get a four and a five here is, Let's suppose in the next leader election, server 2 is elected leader.

[1:15:09] Now for term four. Select the leader. You get a request from a client, it appends it to its own log and crashes. So now we have this. Right, we need a new election because the leader just crashed. Now, in this election, Thank you. And so now we have to ask whether who could be elected, or we have to keep in the back of our heads, oh gosh, who could be elected? Um, so we're gonna claim server three could be elected. The reason why it could be elected is because it only needs request vote responses from a majority. That majority is server one and server three.

[1:15:41] You know, there's no problem, no conflict between these two logs. So server three can be elected for term five, get a request from a client. appended to its own log and crash. And that's how you get this. uh, this configuration. So you need to be able to-- Bye. to work through these things. in order to get to the stage of saying, yes, this could happen, and therefore, Raft must do something sensible, as opposed to, it cannot happen. Because some things can't happen.

[1:16:12] Um, Thank you. All right, so... Um, So what can happen now? We know this can occur. Hopefully we can convince ourselves that Raft actually does something sensible. Now, As for the range of things, before we talk about what Raph would actually do. We need to have some sense of what would be an acceptable outcome.

[1:16:47] Right? and just eyeballing this, Um, We know that The command in slot 10, since it's known by all All the replicas, it could have been committed. so we cannot throw it away. Similarly, the command in slot 11, since it's in a majority of the replicas, it could, for all we know, have been committed, so we can't throw it away. The command in slot 12, however, neither of them could possibly have been committed. So we're entitled. We don't know, we haven't Looked at what Raft will actually do, but Raft is entitled to drop both of the even though it is not entitled to drop in either of the commands in the 10 or 11.

[1:17:26] Um, This is an entitled drop. It's not required. to drop either one of them. And we know it certainly must drop one, at least one, because You have to have identical log contents in the end. This could have been committed. the We can't tell by looking at the logs. exactly how far the leader got before crashing. So one possibility, is that for this command or even this command, One possibility is that a leader send out the append messages with a new command and then immediately crashed.

[1:18:04] So it never got any response back because it crashed. So the old leader did not know if it was committed. And if it didn't get a response back, That means it didn't execute it and it didn't send out but, you know, it didn't send out the incremented commit index. And so maybe the replicas didn't execute it either. So it's actually possible that this wasn't committed. So, Even though Raft doesn't know, Um, It could be legal for Raft Thank you.

[1:18:36] If RAP knew more than it does know, It might be legal. to drop this log entry, because it might not have been committed. but because on the evidence, There's no way to disprove it was committed based on this evidence. It could have been committed. and Raft can't prove it wasn't. So it must treat it as committed. Right, because the leader might have received it, might have crashed just after receiving the append entry replies. and replying to the client. So just looking at this, we can't rule out the possibility either possibility.

[1:19:11] that the leader responded to the client, in which case we cannot throw away this entry because a client knows about it. Or the possibility the leader never did, and yeah, we could. you know So we have to assume that. It was committed. Question? . um, Uh...

[1:19:46] No, there's no--I mean, in this situation, maybe the server crashed before getting the All right, let's continue this. on Thursday.

在Vidleaf工作台中打开

搜索转录文本,选择行,复制带时间戳的引文,翻译。

在工作台中打开 →

署名

MIT 6.824 的“讲座 6:容错:Raft(1)”:分布式系统(https://www.youtube.com/@6.824),根据 CC BY 3.0(https://creativecommons.org/licenses/by/3.0/)许可。源视频:https://www.youtube.com/watch?v=64Zp3tzNbpE。本页面是视频的文本转录,添加了段落分隔和时间戳;创作者与 Vidleaf 无关联,也不为其背书。Vidleaf 添加了 AI 标点和 AI 生成的摘要;原始措辞仍可在本页面上找到。

您是创作者或权利持有人吗?请求更正或移除:copyright@vidleaf.app(参见关于这些页面)。

最后更新于