MIT6.824 lab2C2D实验总结

news2024/9/28 3:22:08

2C

就是持久化一些变量,日志,任期,投票给谁,2D(lastincludeterm, lastincludeindex, snapshot)。同时最难受的是Figure8Unreliable这个测试点,总是几百次出现一两个错误。最后发现是对论文一句话的歧义。这里讲解一下figure8

 这张图主要是发现了一个问题,就是在b阶段,1是term3,然后减s3, s4也提到3,变成leader,得到一个command,c图s1超时(s2是leader)然后1,2,3获得选票,变成leader。得到一个term4的log。这个时候可以提交,但是如果我们提交只是到第二个log的话,根据选举约束,S5可能是leadr。但是你可能会问,s1,s2,s3的term都更大啊!是的,但是因为s5前s3要求选票的话,会将自己变成大term,变成follower,然后超时,又开始新一轮选举,变为leader。然后就覆盖了之前的提交。解决办法是在改变term的时候看看是不是最后一个log的term==rf.currentterm。这样就保障了大多数日志是最新的,强有力的保障了选举约束(日志最新)之前误解成apply提交阶段要这样,结果debug了快一天。。。还是英文比较好点。

2D

就是做快照,减少日志,如果前面的日志是下标的话,这个地方就要大改了。。。

snapshot持久化日志,对日志进行减少,但是要注意把第0个term要变成lastincludeterm,因为选举约束判断日志最新需要。

func (rf *Raft) Snapshot(index int, snapshot []byte) {
	// Your code here (2D).
	DPrintf("Node{%v} snapshot index{%v} snapshot{%v} 1", rf.me, index, snapshot)
	rf.mu.Lock()
	defer rf.mu.Unlock()
	DPrintf("Node{%v} snapshot index{%v} snapshot{%v}", rf.me, index, snapshot)
	if rf.lastIncludeIdx >= index {
		DPrintf("snapshot invaild index{%v} preindex{%v} term{%v}", index, rf.lastIncludeIdx, rf.CurrentTerm)
		return
	}
	snapshot_array := make([]LogEntry, 1)
	snapshot_array[0].Log_Term = rf.Log_Array[index-rf.lastIncludeIdx].Log_Term
	snapshot_array = append(snapshot_array, rf.Log_Array[index+1-rf.lastIncludeIdx:]...)
	rf.Log_Array = snapshot_array
	rf.lastIncludeIdx = index
	rf.lastIncludeTerm = snapshot_array[0].Log_Term
	rf.persist()
	rf.persister.Save(rf.persister.raftstate, snapshot)

}

然后就是向严重落后的follower发送快照rpc(preidx < rf.lastinclude_idx)

func (rf *Raft) InstallSnapshot(args *SnapArgs, reply *SnapReply) {
	DPrintf("install rpc Node{%v} 1", rf.me)
	rf.mu.Lock()
	defer rf.mu.Unlock()
	DPrintf("install rpc Node{%v}", rf.me)
	reply.Term = rf.CurrentTerm
	// my add
	reply.LastIncludedIndex = args.LastIncludedIndex
	if rf.CurrentTerm > args.Leader_Term {
		DPrintf("InstallSnapshot node{%v} term{%v} large leader_term{%v} id{%v}", rf.me, rf.CurrentTerm, args.Leader_Term, args.Leader_Id)
		return
	}
	rf.ToFollower()
	rf.ResetElection()
	if rf.CurrentTerm < args.Leader_Term {
		rf.CurrentTerm = args.Leader_Term
		rf.VoteFor = args.Leader_Id
		rf.persist()
	}
	if rf.Committed_Idx >= args.LastIncludedIndex {
		DPrintf("InstallSnapshot commit{%v} >= lastidx{%v} node{%v} term{%v} eader_term{%v} id{%v}", rf.Committed_Idx, args.LastIncludedIndex, rf.me, rf.CurrentTerm, args.Leader_Term, args.Leader_Id)
		return
	}
	// may be
	// if args.Data == nil {
	// 	DPrintf("Node{%v} data nil", rf.me)
	// 	return
	// }
	// DPrintf("Node{%v} snapshot index{%v} snapshot{%v}", rf.me, index, snapshot)
	if rf.lastIncludeIdx >= args.LastIncludedIndex {
		DPrintf("install snapshot invaild index{%v} preindex{%v} term{%v}", args.LastIncludedIndex, rf.lastIncludeIdx, rf.CurrentTerm)
		return
	}
	snapshot_array := make([]LogEntry, 1)
	snapshot_array[0].Log_Term = args.LastIncludedTerm
	if args.LastIncludedIndex < rf.GetLastLog().Index {
		snapshot_array = append(snapshot_array, rf.Log_Array[args.LastIncludedIndex+1-rf.lastIncludeIdx:]...)
	}
	rf.Log_Array = snapshot_array
	rf.lastIncludeIdx = args.LastIncludedIndex
	rf.lastIncludeTerm = args.LastIncludedTerm
	reply.Success = true
	reply.LastIncludedIndex = rf.lastIncludeIdx
	reply.LastIncludedTerm = rf.lastIncludeTerm
	if rf.Last_Applied_Idx < rf.lastIncludeIdx {
		rf.Last_Applied_Idx = rf.lastIncludeIdx
	}
	if rf.Committed_Idx < rf.lastIncludeIdx {
		rf.Committed_Idx = rf.lastIncludeIdx
	}
	rf.persist()
	rf.persister.Save(rf.persister.raftstate, args.Data)
	applyMsg := ApplyMsg{
		SnapshotValid: true,
		Snapshot:      args.Data,
		SnapshotTerm:  args.LastIncludedTerm,
		SnapshotIndex: args.LastIncludedIndex,
	}

	rf.ApplyChanTemp <- applyMsg
}

第一个遇到的问题就是死锁问题。这里的死锁居然是因为和测试代码死锁了。

嗯,无语。解决办法就是把这个锁和这个chan分开,如果解锁的话,会导致不一致。但应该可以用一些额外条件解决。但我选择的是用一个中间chan

func (rf *Raft) applier() {
	for msg := range rf.ApplyChanTemp {
		rf.ApplyChan <- msg
	}
}

 第二个问题就是发现一些边界条件没能很好的判断。导致了can't reach agreement。

这里记录两个典型思考

第一个就是如果我们一直发送给他的是小于lastincludeIndex的话,就一直被拒绝,所以我们应该在这个情况下发给他lastincludeidx。

if reply.LastIdx == 0 {
			rf.Next_Idx[peer] = rf.GetIdxPreTermLogEntry(reply.PrevLogIndex)
			if rf.Next_Idx[peer] < rf.Match_Idx[peer]+1 {
				rf.Next_Idx[peer] = rf.Match_Idx[peer] + 1
			}
			DPrintf("leader node{%v} log{%v}", rf.me, rf.Log_Array)
		} else {
			rf.Next_Idx[peer] = reply.LastIdx + 1
			DPrintf("Node{%v} to peer{%v} conflict and to the last_idx{%v} + 1", rf.me, peer, reply.LastIdx)
		}

还有就是match_idx的含义。match_idx应该是保守的,commit_idx不代表match_idx,代表的是大部分match_idx

func (rf *Raft) AppendNewEntries(command interface{}) LogEntry {
	new_entries := LogEntry{Command: command, Log_Term: rf.CurrentTerm, Index: rf.lastIncludeIdx + len(rf.Log_Array)}
	rf.Log_Array = append(rf.Log_Array, new_entries)
	rf.persist()
	for i := range rf.peers {
		// rf.Match_Idx[i] = rf.Committed_Idx
		rf.Next_Idx[i] = rf.GetLastLog().Index
	}
	rf.Match_Idx[rf.me] = rf.GetLastLog().Index
	return new_entries

}

虽然nextIndex和matchIndex通常同时更新为相似的值(具体来说,nextIndex = matchIndex + 1),但两者的用途却截然不同。nextIndex是关于领导者与给定追随者共享什么前缀的猜测。它通常是相当乐观的(我们分享一切),并且只有在出现负面反应时才会倒退。例如,当一个领导者刚刚被选举出来时, nextIndex在日志的末尾设置为索引索引。在某种程度上, nextIndex用于性能——你只需要将这些东西发送给这个对等点。 matchIndex用于安全。它是对领导者与给定追随者共享的日志前缀的保守测量 。matchIndex永远不能设置为太高的值,因为这可能会导致commitIndex向前移动太远。这就是为什么 matchIndex初始化为 -1(即,我们同意没有前缀),并且仅在跟随者肯定确认RPC 时才更新AppendEntries。么前缀的猜测。它通常是相当乐观的(我们分享一切),并且只有在出现负面反应时才会倒退。例如,当一个领导者刚刚被选举出来时, nextIndex在日志的末尾设置为索引索引。在某种程度上, nextIndex用于性能——你只需要将这些东西发送给这个对等点。

matchIndex用于安全。它是对领导者与给定追随者共享的日志前缀的保守测量 matchIndex永远不能设置为太高的值,因为这可能会导致commitIndex向前移动太远。这就是为什么 matchIndex初始化为 -1(即,我们同意没有前缀),并且仅在跟随者肯定确认RPC 时才更新AppendEntries

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

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

相关文章

Linux(DHCP原理与配置)

文章目录一 、什么是DHCP1.1DHCP定义1.2DHCP好处1.3DHCP的分配方式二 、DHCP 的工作过程三 、DHCP中的设置3.1 DHCP参数3.2 相关操作步骤一 、什么是DHCP 1.1DHCP定义 DHCP&#xff08;动态主机配置协议&#xff09;是一个局域网的网络协议。指的是由服务器控制一段IP地址范围…

swagger文件上传接口没有选择文件按钮问题解决 使用@RequestPart注解

在使用文件上传API时&#xff0c;swagger没有选择文件按钮 在MultipartFile前面加上RequestPart注解 PostMapping("/importFile")ApiOperation(value "文件上传API")public Object importFile(ApiParam(value "文件流", required true) Reque…

超越ChatGpt,最近爆火的AutoGPT 究竟是什么

一、AutoGPT是什么 最近几天&#xff0c;一款基于GPT-4的最强语言模型AutoGPT火遍了整个AI圈。众所周知&#xff0c;此前爆火AI圈的ChatGPT&#xff0c;唯一不太好用的地方就是需要人类不断的prompt。因此&#xff0c;如果你想要ChatGPT帮你去做一件复杂的事情&#xff0c;那么…

第九章 法律责任与法律制裁

第九章 法律责任与法律制裁_副本 目录 第一节 法律责任的概念 一 法律责任的含义二 法律责任的特点 第二节 法律责任的分类与竞合 一 法律责任的分类 &#xff08;一&#xff09;根据责任行为所违反的法律的性质 民事责任&#xff1a;刑事责任行政责任违宪责任 &#xff08;二…

【云原生 • Docker】cAdvisor+Prometheus+Grafana 10分钟搞定Docker容器监控平台

文章目录cAdvisorPrometheusGrafana 10分钟搞定Docker容器监控平台cAdvisor部署Prometheus部署Grafana部署cAdvisorPrometheusGrafana 10分钟搞定Docker容器监控平台 cAdvisor(Container Advisor) 是 Google 开源的一个容器监控工具&#xff0c;可用于对容器资源的使用情况和性…

企业级信息系统开发讲课笔记3.1 基于配置文件整合SSM框架实现用户登录

文章目录零、本节学习目标一、采用MVC架构二、用户登录运行效果三、基于XML配置方式整合SSM框架实现用户登录&#xff08;一&#xff09;创建数据库与表1、创建数据库2、创建用户表3、在用户表里插入记录&#xff08;二&#xff09;创建Maven项目&#xff08;三&#xff09;项目…

【手把手刷CCF】202303-2-垦田计划100分(超简单思路,含详细解释注释与代码)

文章目录&#xff1a;故事的开头总是极尽温柔&#xff0c;故事会一直温柔……&#x1f49c;一、&#x1f333;代码如下&#xff1a;二、&#x1f335;解题思路❤️❤️❤️忙碌的敲代码也不要忘了浪漫鸭&#xff01;故事的开头总是极尽温柔&#xff0c;故事会一直温柔……&…

vector使用+模拟实现

目录 vector介绍 常见接口 构造函数 迭代器 容量操作 元素访问 增删查改 模拟实现 模拟实现要点图解 整体代码 迭代器失效问题 内部失效 外部失效 深浅拷贝问题 vector介绍 vector是表示可变大小数组的序列式容器。vector采用连续的空间存储元素&#xff0c;大小…

HTML5 <meta> 标签

HTML5 <meta> 标签 实例 描述 HTML 文档的元数据&#xff1a; <head> <meta name"description" content"免费在线教程"> <meta name"keywords" content"HTML,CSS,XML,JavaScript"> <meta name"auth…

全志v851s 在 eyesee-mpp 中添加一个hello_world sample 的流程

1. 为什么要在eyesee-mpp 中添加sample&#xff1f; 1&#xff09;保持整个openwrt 应用程序编写的完成性&#xff1b; 2&#xff09;eyesee-mpp 中包含了几乎所有全志视频音频模块的sample 以及 头文件&#xff0c;参考以及头文件调用起来非常方便&#xff0c;而且可以学习各种…

MongoDB 聚合管道中使用数组表达式运算符合并数组($concatArrays)

数组表达式运算符主要用于文档中数组的操作&#xff0c;接上一篇&#xff1a;MongoDB 聚合管道中使用数组表达式运算符&#xff08;$slice截取数组&#xff09;https://blog.csdn.net/m1729339749/article/details/130130328本篇我们主要介绍数组表达式运算符中用于合并数组的操…

InnoSetup制作安装包(EXE)

功能描述 1.666666.war为项目war包&#xff0c;666666.bat为启动war包脚本&#xff0c;通过InnoSetup将它们打包到安装包666666.exe 2.666666.exe安装包安装过程中将666666.bat注册为自启动服务&#xff0c;安装结束自动执行脚本启动项目666666.war --------------------------…

VxLAN数据中心L2/L3互通(端到端)

VxLAN数据中心端到端方式实现L2/L3互连&#xff0c;这种实现方式可以使数据中心属于同一个EVPN-VXLAN域&#xff0c;相较于hand-off方式通过端到端实现数据中心L2互连可以满足Mac mobility、ARP suppression等特性。 实现思路 DC1的Border-Leaf和DC2的Border-Leaf之间运行EBG…

测试:腾讯云轻量4核8G12M服务器CPU流量带宽系统盘

腾讯云轻量4核8G12M应用服务器带宽&#xff0c;12M公网带宽下载速度峰值可达1536KB/秒&#xff0c;折合1.5M/s&#xff0c;每月2000GB月流量&#xff0c;折合每天66GB&#xff0c;系统盘为180GB SSD盘&#xff0c;地域节点可选上海、广州或北京&#xff0c;4核8G服务器网来详细…

MySQL 日志

错误日志(error log): error log 主要记录 MySQL 在启动、关闭或者运行过程中的错误i西南西&#xff0c;在MySQL 的配置文件 my.cnf 中&#xff0c;可以通过 log-error/var/log/mysqld.log 执行 mysql 错误日志的位置 慢查询日志(slow query log): MySQL 的慢查询日志是 MyS…

【erlang】并发篇

PID类型 在之前的语法篇中&#xff0c;我们并没有介绍 PID这个类型&#xff0c;它和并发息息相关&#xff0c;因此我们在这里来学习它。 PID是进程标识符的意思&#xff0c;用来标识一个erlang进程。在所有相连的erlang节点中&#xff0c;PID都是唯一的。但是PID会被复用&…

从零搭建一个 Level-2 快照数据的因子计算平台

因子挖掘是量化交易的基础。近年来&#xff0c;Python 是很多研究员进行数据分析和因子挖掘的主流工具。但是通过 Python 挖掘的有效因子在投产时&#xff0c;通常需要由 QUANT 团队的研究员将代码提交给 IT 团队&#xff0c;IT 团队用 C 代码转写后部署到生产环境&#xff0c;…

1. HTMLCSS

文章目录1 盒子模型&#xff1a;1.1 盒子属性导图1.2 边框属性导图1.3 定位导图&#xff1a;2 HTML常用标签2.1 基本标签① HTML基本结构② HTML常见标签③ 特殊字符④ 列表标签a 无序列表&#xff1a;b 有序列表&#xff1a;⑤ 表单3 CSS快速上手3.1 background属性① 思维导图…

自适应遗传算法求解TSP问题(Java)

1 引言 普通遗传算法&#xff08;Sample Genetic Algorithm, SGA&#xff09;存在着严重的缺点&#xff0c;它的Pc和Pm的值是固定的&#xff0c;本文采用自适应遗传算法进行求解TSP问题。不管是优良个体还是劣质个体都经过了相同概率的交叉和变异操作。这会引起两个很严重的问…

Linux驱动开发——高级I/O操作(二)

目录 proc文件操作 非阻塞型I/O 阻塞型I/O proc文件操作 proc 文件系统是一种伪文件系统&#xff0c;这种文件系统不存在于磁盘上&#xff0c;只存在于内存中只有内核运行时才会动态生成里面的内容。这个文件系统通常挂载在/proc 目录下&#xff0c;是核开发者向用户导出信息…