MIT6.824(2024春)Raft-lab3C代码分析

lab3C--持久化

在这个实验中,我们需要实现持久化这一内容。还是在这里给出一些参考资料:

任务简介

在分布式系统中,节点可能会因为各种原因(如硬件故障、网络问题、系统崩溃等)而重启。如果没有持久化机制,节点重启后将丢失所有状态信息,这会导致严重的问题。具体来说:

  • 如果节点重启后丢失了currentTerm,它可能会错误地接受来自旧Leader的消息。如果丢失了votedFor,它可能会在同一个任期内重复投票,违反Raft的"每个节点每个任期最多投一票"的规则。如果丢失了log,它可能会接受错误的日志条目,导致数据不一致。
  • 没有持久化时,重启的节点会从初始状态开始,这可能导致它接受过期的日志。其他节点可能已经提交了某些日志,但重启的节点完全不知道这些日志的存在。这会导致集群中出现数据不一致的情况。
  • 如果Leader节点重启后丢失了日志,它可能会覆盖其他节点的正确日志。如果Follower节点重启后丢失了投票信息,它可能会在同一个任期内给不同的候选者投票。如果节点丢失了任期信息,它可能会错误地认为自己是Leader,导致出现多个Leader

在Raft论文中,有且仅有三个数据是需要持久化的。它们分别是Log、currentTerm、votedFor。Log是所有的Log条目。当某个服务器刚刚重启,在它加入到Raft集群之前,它必须要检查并确保这些数据有效的存储在它的磁盘上。

Log需要被持久化存储的原因是,这是唯一记录了应用程序状态的地方。当服务器重启时,唯一能用来重建应用程序状态的信息就是存储在Log中的一系列操作,所以Log必须要被持久化存储。

currentTerm和votedFor持久化的原因都是用来确保每个任期只有最多一个Leader。在一个故障的场景中,如果一个服务器收到了一个RequestVote请求,并且为服务器1投票了,之后它故障。如果它没有存储它为哪个服务器投过票,当它故障重启之后,收到了来自服务器2的同一个任期的另一个RequestVote请求,那么它还是会投票给服务器2,因为它发现自己的votedFor是空的,因此它认为自己还没投过票。现在这个服务器,在同一个任期内同时为服务器1和服务器2投了票。因为服务器1和服务器2都会为自己投票,它们都会认为自己有过半选票(3票中的2票),那它们都会成为Leader。现在同一个任期里面有了两个Leader。这就是为什么votedFor必须被持久化存储。

代码设计

这个实验的内容相较于上两个实验十分简单,主要是需要我们填写persist函数和readPersist函数,然后在需要持久化和读取持久化信息的地方调用对应函数即可。

根据注释信息完成persist函数和readPersist函数:

go 复制代码
func (rf *Raft) persist() {
	// DPrintf("server %v 开始持久化, 最后一个持久化的log为: %v:%v", rf.me, len(rf.log)-1, rf.log[len(rf.log)-1].Cmd)

	w := new(bytes.Buffer)
	e := labgob.NewEncoder(w)
	e.Encode(rf.votedFor)
	e.Encode(rf.currentTerm)
	e.Encode(rf.log)
	raftstate := w.Bytes()
	rf.persister.Save(raftstate, nil)
}

// restore previously persisted state.
func (rf *Raft) readPersist(data []byte) {
	if data == nil || len(data) < 1 { // bootstrap without any state?
		return
	}
	if data == nil || len(data) == 0 {
		return
	}
	r := bytes.NewBuffer(data)
	d := labgob.NewDecoder(r)

	var votedFor int
	var currentTerm int
	var log []Entry
	if d.Decode(&votedFor) != nil ||
		d.Decode(&currentTerm) != nil ||
		d.Decode(&log) != nil {
		DPrintf("readPersist failed\n")
	} else {
		rf.votedFor = votedFor
		rf.currentTerm = currentTerm
		rf.log = log
	}
}

在Raft代码实现中,只要修改了votedFor, currentTerm, log中的任意一个,则进行持久化,因此只需要在相应位置调用persist即可,这里不进行过多赘述,可以去参考代码仓库。

执行测试:

相关推荐
UIU1141 天前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
论文复现现场1 天前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi1 天前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构
秋天的怀念_David1 天前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
碧海银沙音频科技研究院1 天前
在PC电脑上用Whisper→Marian算法实现离线语音翻译功能
算法·whisper
niyongsheng1 天前
3D 装箱算法工程化:数据结构、放置启发式与支撑规则
数据结构·算法·rust
j7~1 天前
【穷举、深搜与回溯算法】一棵决策树讲透全排列与子集
数据结构·c++·算法·leetcode·回溯·递归
傅科摆 _ py1 天前
DP,贪心,堆和栈,二分力扣复习
开发语言·python·算法
Shaoshing1 天前
LeetCode100 42.接雨水
java·算法·leetcode
域限奇点访客1 天前
用AI生成问卷的网站怎么选?看题目录入结构、题型映射和生成后的校验
算法