MIT6.5840-2023-Lab2C: Raft-Persistence

news2024/12/27 11:12:31

前置知识

见上一篇 Lab2A。

实验内容

实现 RAFT,分为四个 part:leader election、log、persistence、log compaction。

实验环境

OS:WSL-Ubuntu-18.04
golang:go1.17.6 linux/amd64

Part 2C: persistence

大部分的bug都与这张图有关。如果前两次lab通过了千次以上测试,这边应该问题不大。注意rpc前后的状态判断。
在这里插入图片描述

实现持久化,重启后能快速恢复。真正的实现将在每次更改时在磁盘写下 raft 的持久状态,并在重新启动后从磁盘中读取状态。lab 实现时在 Persister 中存储和恢复。currentTerm、votedFor、log[]
persist 和 readPersist:通过 labgob实现。

// save Raft's persistent state to stable storage,
// where it can later be retrieved after a crash and restart.
// see paper's Figure 2 for a description of what should be persistent.
// before you've implemented snapshots, you should pass nil as the
// second argument to persister.Save().
// after you've implemented snapshots, pass the current snapshot
// (or nil if there's not yet a snapshot).
func (rf *Raft) persist() {
	// Your code here (2C).
	// Example:
	w := new(bytes.Buffer)
	e := labgob.NewEncoder(w)
	e.Encode(rf.currentTerm)
	e.Encode(rf.votedFor)
	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
	}
	// Your code here (2C).
	// Example:
	r := bytes.NewBuffer(data)
	d := labgob.NewDecoder(r)
	var currentTerm, votedFor int
	var logs []Entry
	if d.Decode(&currentTerm) != nil ||
		d.Decode(&votedFor) != nil ||
		d.Decode(&logs) != nil {
		log.Println("decode fail")
	} else {
		rf.currentTerm = currentTerm
		rf.votedFor = votedFor
		rf.log = logs
		log.Println("restore success")
	}
}

优化:避免 leader 每次只往前移动一位;若日志很长的话在一段时间内无法达到冲突位置。若 leader 发送心跳时接收到的回复是 false,leader 会减小发送的 AppendEntriesArgs 中的 rf.nextIndex,但是这种每次仅减小 1 的方案无法在有限的时间内确定跟其他服务器发生冲突的下标位置。

func (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply) {
	// Your code here (2A, 2B).
	rf.mu.Lock()
	defer rf.mu.Unlock()
	if args.Term > rf.currentTerm { // all server
		rf.state = Follower
		rf.currentTerm = args.Term
		rf.votedFor = -1
		rf.persist()
	}

	if args.Term < rf.currentTerm {
		reply.Success = false
		reply.Term = rf.currentTerm
	} else if len(rf.log)-1 < args.PrevLogIndex ||
		rf.log[args.PrevLogIndex].Term != args.PrevLogTerm {
		log.Println(rf.me, "mismatch", "len(rf.log)", len(rf.log), "args.PrevLogIndex", args.PrevLogIndex)
		reply.Success = false
		reply.Term = rf.currentTerm

		timeout := time.Duration(250+rand.Intn(300)) * time.Millisecond
		rf.expiryTime = time.Now().Add(timeout)

		if args.PrevLogIndex > len(rf.log)-1 {
			reply.ConflictIndex = len(rf.log)
			reply.ConflictTerm = -1
		} else {
			reply.ConflictTerm = rf.log[args.PrevLogIndex].Term
			reply.ConflictIndex = 1
			for i := args.PrevLogIndex - 1; i >= 0; i-- {
				if rf.log[i].Term != reply.ConflictTerm {
					reply.ConflictIndex += i
					break
				}
			}

		}
	} else {
		reply.Success = true
		reply.Term = rf.currentTerm

		timeout := time.Duration(250+rand.Intn(300)) * time.Millisecond
		rf.expiryTime = time.Now().Add(timeout)

		// // delete
		// rf.log = rf.log[:args.PrevLogIndex+1]
		// // append
		// rf.log = append(rf.log, args.Entries...)

		insertIndex := args.PrevLogIndex + 1
		argsLogIndex := 0
		for {
			if insertIndex >= len(rf.log) ||
				argsLogIndex >= len(args.Entries) ||
				rf.log[insertIndex].Term != args.Entries[argsLogIndex].Term {
				break
			}
			insertIndex++
			argsLogIndex++
		}
		// for _, e := range rf.log {
		// 	log.Print(e)
		// }
		// for _, e := range args.Entries {
		// 	log.Print(e)
		// }
		if argsLogIndex < len(args.Entries) {
			rf.log = append(rf.log[:insertIndex], args.Entries[argsLogIndex:]...)
			rf.persist()
		}

		if args.LeaderCommit > rf.commitIndex {
			rf.commitIndex = int(math.Min(float64(args.LeaderCommit), float64(len(rf.log)-1)))
			log.Println(rf.me, "follower update commitIndex", "args.LeaderCommit", args.LeaderCommit, "len(rf.log)", len(rf.log), "rf.commitIndex", rf.commitIndex)
		}
	}

}
// 修改前
// if args.PrevLogIndex > 0 {
// 	rf.nextIndex[i] = args.PrevLogIndex
// }
// 修改后
						if reply.ConflictTerm >= 0 {
							lastIndex := -1
							for i := len(rf.log) - 1; i >= 0; i-- {
								if rf.log[i].Term == reply.ConflictTerm {
									lastIndex = i
									break
								}
							}
							if lastIndex >= 0 {
								rf.nextIndex[i] = lastIndex + 1
							} else {
								rf.nextIndex[i] = reply.ConflictIndex
							}
						} else {
							rf.nextIndex[i] = reply.ConflictIndex
						}

实验结果

在这里插入图片描述
需要更多次实验,由于电脑性能原因无法开更多个线程跑,10次测试是通过的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1307702.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go EASY游戏框架 之 RPC Guide 03

1 Overview easy解决服务端通信问题&#xff0c;同样使用了RPC技术。easy使用的ETCDGRPC&#xff0c;直接将它们打包组合在了一起。随着服务发现的成熟&#xff0c;稳定&#xff0c;简单&#xff0c;若是不用&#xff0c;甚至你也并不需要RPC来分解你的架构。 GRPC 有默认res…

产品入门第三讲:Axure产品流程图绘制

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​​​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Axure》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还…

电子学会C/C++编程等级考试2022年12月(五级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:漫漫回国路 2020年5月,国际航班机票难求。一位在美国华盛顿的中国留学生,因为一些原因必须在本周内回到北京。现在已知各个机场之间的航班情况,求问他回不回得来(不考虑转机次数和机票价格)。 时间限制:1000 内存限制:655…

排序算法---希尔排序

1. 基本思想 希尔排序是插入排序的一种&#xff0c;它与直接插入排序不同的是&#xff0c;它会优先比较距离较远的元素&#xff0c;因此希尔排序又被称为“缩小增量排序”。希尔排序的实现思路是&#xff1a;先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序&…

鸿蒙OS应用开发者高级认证题库

一、判断题 云函数打包完成后&#xff0c;需要到AppGallery Connect创建对应函数的触发器才可以在端侧中调用&#xff08;错&#xff09; 在column和Row容器组件中&#xff0c;aligntems用于设置子组件在主轴方向上的对齐格式&#xff0c;justifycontent用于设置子组件在交叉轴…

Nature 确认:大语言模型只是没有感情的「学人精」

DeepMind、EleutherAI 科学家提出&#xff0c;大模型只是在角色扮演。 ChatGPT 爆火后&#xff0c;大语言模型一跃而至&#xff0c;成为了行业与资本的宠儿。而在人们或是猎奇、或是探究地一次次对话中&#xff0c;大语言模型所表现出的过度拟人化也引起了越来越多的关注。 其实…

Linux常用命令----pgrep 命令

文章目录 介绍语法常用选项用法示例结论 介绍 pgrep 是一个在 Linux 系统上用于查找进程 ID&#xff08;PID&#xff09;的常用命令。通过提供进程名或者其他选择性的标志&#xff0c;pgrep 可以快速地检索与之匹配的进程 ID&#xff0c;并将其输出到标准输出。 语法 pgrep …

java-sec-code中的sql注入

java-sec-code 用于学习java漏洞代码 环境部署 直接在idea中git 运行即可 sql注入 环境中主要是两个 分别为jdbc和mybatis jdbc 存在问题的写法 直接获取用户传入的数据&#xff0c;拼接执行 String sql "select * from users where username " request.getP…

python源码,在线读取传奇列表,并解析为需要的JSON格式

python源码&#xff0c;在线读取传奇列表&#xff0c;并解析为需要的JSON格式 [Server] ; 使用“/”字符分开颜色&#xff0c;也可以不使用颜色&#xff0c;支持以前的旧格式&#xff0c;只有标题和服务器标题支持颜色 ; 标题/颜色代码(0-255)|服务器标题/颜色代码(0-255)|服务…

小航助学2023年9月电子学会Scratch二级真题(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09; 单选题2.00分 删除编辑附件图文 答案:D 第1题点击绿旗&#xff0c;运行程序后&#xff0c;舞台上的图形是&#xff1f;&#xff08; &#xff09; A、画笔粗细…

小企业必备:实用邮件营销软件推荐

通俗来讲&#xff0c;邮件营销是指通过邮件&#xff0c;与新老客户保持长久互动&#xff0c;把潜在客户转变为企业客户&#xff0c;为宣传企业品牌&#xff0c;提高品牌知名度和影响力&#xff0c;保持长久客户关系的一种简单有效的营销方式。 相较于其他营销方式&#xff0c;…

【FPGA/verilog -入门学习10】verilog 查表法实现正弦波形发生器

0&#xff0c;需求 用查找表设计实现一个正弦波形发生器 寻址的位宽是10位&#xff0c;数据量是1024个&#xff0c;输出的数据是16位 1&#xff0c;需求分析 数据量是1024个&#xff1a; x linspace(0,2*pi,1024) 输出数据是16位: y范围&#xff1a;0~2^16 -1 0~65535…

防火墙访问控制、安全审计、网络设备防护检查表

1、访问控制类检查 2、安全审计类检查 3、网络设备防护类检查 原件&#xff1a; 防火墙标准检查表 分类 测评项 预期结果 访问控制 应在网络边界部署访问控制设备&#xff0c;启用访问控制功能 启用了访问控制规则 应能根据会话状态信息为数据流提供明确的允许/拒绝访…

【JMeter】使用nmon进行性能资源监控

一、前言 ​ 在工作中可能会遇到需要在压测的时候对Linux服务器进行性能资源监控的情况。这时可以用nmon来对服务器进行监控。 二、nmon的下载安装 1.查看系统信息 shell cat /etc/os-release结果为 shell PRETTY_NAME"Debian GNU/Linux 12 (bookworm)" NAME&qu…

《HumanGaussian: Text-Driven 3D Human Generation with Gaussian Splatting》

文章目录 前置知识&#xff1a;一、正文&#xff1a;二、方法 前置知识&#xff1a; \quad 1&#xff09;SMPL&#xff08;Skinned Multi-Person Linear&#xff09;模型 \quad SMPL&#xff08;Skinned Multi-Person Linear&#xff09;模型是一种用于表示人体形状和姿势的三维…

TikTok与虚拟现实的完美交融:全新娱乐时代的开启

TikTok&#xff0c;这个风靡全球的短视频平台&#xff0c;与虚拟现实&#xff08;VR&#xff09;技术的深度结合&#xff0c;为用户呈现了一场全新的娱乐盛宴。虚拟现实技术为TikTok带来了更丰富、更沉浸的用户体验&#xff0c;标志着全新娱乐时代的开启。本文将深入探讨TikTok…

matlab信号分选系统算法-完整算法结构

matlab信号分选系统算法 针对得到的脉冲流PDW进行信号分选&#xff0c;包括重频恒定、重频抖动、重频参差和重频滑变四种脉间调制类型。   这里我们先进行数据的仿真&#xff0c;后续边仿真边分享思路&#xff1a;首先根据信号类型&#xff0c;分别产生重频恒定、重频抖动、重…

陪玩系统最新上线!APP小程序H5三端源码交付,支持二开!开发者必看!

首先&#xff0c;陪玩系统需要提供实时匹配功能。用户输入自己的游戏信息和陪玩需求后&#xff0c;系统需要根据这些信息实时匹配合适的陪玩者。匹配算法应该考虑到游戏类型、玩家水平、价格等因素&#xff0c;以确保匹配结果符合用户期望。 其次&#xff0c;陪玩系统需要提供…

常见的计算机图片格式

左rgb &#xff08;光源色彩&#xff09; 右cmyk &#xff08;印刷色彩&#xff09; 缺点&#xff0c;不能保存&#xff0c;储存空间太大

专为中小培训机构精心打造的教务管理系统

随着互联网的普及和线上教育的兴起&#xff0c;教育机构纷纷开发出自己的小程序管理系统&#xff0c;以满足广大学生和家长的需求。本文将详细介绍如何使用乔拓云平台&#xff0c;一键开发出自己的教育机构小程序管理系统。 一、进入乔拓云后台 在浏览器搜索乔拓云&#xff0c…