【Mit6.824】实验-Lab2D-Raft 日志压缩

实验-Lab2D-Raft 日志压缩

对于一个长期运行的服务来说,永远记住完整的 Raft 日志是不切实际的。如果没有压缩⽇志的⽅法,最终将导致可⽤性问题:即服务器存储空间不⾜,或者 启动时间太⻓。因此,任何实际系统都需要某种形式的⽇志压缩。

实现

  1. 当Raft认为 Log 的体积过于庞大时,Raft 会要求应用程序在 Log 的某个特定位置,对其状态做一次快照,之后会丢弃这个点之前的 Log,Snapshot()方法即为执行快照的方法,为了将快照点之前的 Log 删掉并且将切片的这部分空间占用的内存回收掉,对切片进行内存优化在方法shrinkEntriesArray()中实现。
func (rf *Raft) Snapshot(index int, snapshot []byte) {
	rf.mu.Lock()
	defer rf.mu.Unlock()
	snapshotIndex := rf.getFirstLog().Index
	if index <= snapshotIndex {
		return
	}
	//丢弃index 之前的log
	rf.logs = shrinkEntriesArray(rf.logs[index-snapshotIndex:])
	rf.logs[0].Command = nil
	rf.persister.SaveStateAndSnapshot(rf.encodeState(), snapshot)
}
  1. 当follower的Log过于落后,和leader相比,部分缺少的log在leader那儿都已经进快照了,那么leader就需要给follower调用InstallSnapshot RPC。Follower在接到 InstallSnapshot RPC 后,会将应用 Snapshot 的信息发送到 ApplyCh 进行异步处理,所以此 Rpc 的入口即为 在 AppendEntries 处理日志冲突时,节点的 prevLogIndex 小于 Leader 的 FirstLogIndex,这个时候 Leader 就需要发送 快照信息给 Follower。
func (rf *Raft) InstallSnapshot(request *InstallSnapshotRequest, response *InstallSnapshotResponse) {
	rf.mu.Lock()
	defer rf.mu.Unlock()
	response.Term = rf.currentTerm
	if request.Term < rf.currentTerm {
		return
	}
	if request.Term > rf.currentTerm {
		rf.currentTerm = request.Term
		rf.votedFor = -1
		rf.persist()
	}
	rf.ChangeState(StateFollower)
	rf.electionTimer.Reset(RandomizedElectionTimeout())
	//快照太旧了
	if request.LastIncludedIndex <= rf.commitIndex {
		return
	}
	go func() {
		rf.applyCh <- ApplyMsg{
			SnapshotValid: true,
			Snapshot:      request.Data,
			SnapshotTerm:  request.LastIncludedTerm,
			SnapshotIndex: request.LastIncludedIndex,
		}
	}()
}
  1. 在 config.go 中的applierSnap()方法中我们可以看到,它会监听 applyCh 中的消息,发现 ApplyMsg 的 SnapshotValid 为 true 时,就会调用此 Raft 节点的 CondInstallSnapshot()方法,此方法和 Snapshot()方法大同小异
func (rf *Raft) CondInstallSnapshot(lastIncludedTerm int, lastIncludedIndex int, snapshot []byte) bool {
	rf.mu.Lock()
	defer rf.mu.Unlock()
	// outdated snapshot
	if lastIncludedIndex <= rf.commitIndex {
		return false
	}
	if lastIncludedIndex > rf.getLastLog().Index {
		rf.logs = make([]Entry, 1)
	} else {
		rf.logs = shrinkEntriesArray(rf.logs[lastIncludedIndex-rf.getFirstLog().Index:])
		rf.logs[0].Command = nil
	}
	// update dummy entry with lastIncludedTerm and lastIncludedIndex
	rf.logs[0].Term, rf.logs[0].Index = lastIncludedTerm, lastIncludedIndex
	rf.lastApplied, rf.commitIndex = lastIncludedIndex, lastIncludedIndex

	rf.persister.SaveStateAndSnapshot(rf.encodeState(), snapshot)
	return true
}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值