当前位置: 首页 > news >正文

MIT6.5840-2023-Lab2C: Raft-Persistence

前置知识

见上一篇 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 intvar logs []Entryif d.Decode(&currentTerm) != nil ||d.Decode(&votedFor) != nil ||d.Decode(&logs) != nil {log.Println("decode fail")} else {rf.currentTerm = currentTermrf.votedFor = votedForrf.log = logslog.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 serverrf.state = Followerrf.currentTerm = args.Termrf.votedFor = -1rf.persist()}if args.Term < rf.currentTerm {reply.Success = falsereply.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 = falsereply.Term = rf.currentTermtimeout := time.Duration(250+rand.Intn(300)) * time.Millisecondrf.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].Termreply.ConflictIndex = 1for i := args.PrevLogIndex - 1; i >= 0; i-- {if rf.log[i].Term != reply.ConflictTerm {reply.ConflictIndex += ibreak}}}} else {reply.Success = truereply.Term = rf.currentTermtimeout := time.Duration(250+rand.Intn(300)) * time.Millisecondrf.expiryTime = time.Now().Add(timeout)// // delete// rf.log = rf.log[:args.PrevLogIndex+1]// // append// rf.log = append(rf.log, args.Entries...)insertIndex := args.PrevLogIndex + 1argsLogIndex := 0for {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 := -1for i := len(rf.log) - 1; i >= 0; i-- {if rf.log[i].Term == reply.ConflictTerm {lastIndex = ibreak}}if lastIndex >= 0 {rf.nextIndex[i] = lastIndex + 1} else {rf.nextIndex[i] = reply.ConflictIndex}} else {rf.nextIndex[i] = reply.ConflictIndex}

实验结果

在这里插入图片描述

相关文章:

  • 深入理解网络 I/O:单 Group 混杂模式|多 Group 主从模式
  • 设计模式——策略模式(Strategy Pattern)
  • 设计模式—策略模式
  • 张正友相机标定法原理与实现
  • 如何将xlsx中的数据通过datagrep导入到mysql数据库表中
  • Spring概述
  • 使用Go实现一个百行聊天服务器
  • leetcode
  • v-md-editor高级使用之自定义目录
  • SpringBoot 3.0 升级之 Swagger 升级
  • 鸿蒙小车之多任务调度实验
  • jetpack compose 学习(2)
  • Linux操作系统:开源的计算机革命
  • Ray RLlib User Guides:模型,处理器和动作分布
  • Java之方法引用
  • ➹使用webpack配置多页面应用(MPA)
  • golang中接口赋值与方法集
  • JavaScript 一些 DOM 的知识点
  • JavaScript创建对象的四种方式
  • JavaScript设计模式系列一:工厂模式
  • npx命令介绍
  • PAT A1017 优先队列
  • PAT A1092
  • Promise初体验
  • Redux 中间件分析
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • Spring Cloud中负载均衡器概览
  • SQLServer之创建数据库快照
  • Yeoman_Bower_Grunt
  • 创建一种深思熟虑的文化
  • 理清楚Vue的结构
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 漂亮刷新控件-iOS
  • 前端攻城师
  • 区块链共识机制优缺点对比都是什么
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 算法-插入排序
  • 我的面试准备过程--容器(更新中)
  • 一文看透浏览器架构
  • 优化 Vue 项目编译文件大小
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • ​2021半年盘点,不想你错过的重磅新书
  • ​学习一下,什么是预包装食品?​
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • #include
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (poj1.2.1)1970(筛选法模拟)
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (力扣)1314.矩阵区域和
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET Core 中的路径问题
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式