Lab2D

Snapshot()

状态机保存快照之后会调用Raft的这个函数,丢弃已经快照过的日志

按照官方提示首先实现Snapshot(index),因为日志修剪之后log[0]的index并不是0,要修改脚本把用到log[i]的地方都要减去log[0]的index,这个index是快照保存的最后一个index,而不是剪切之后的第一个index

A good place to start is to modify your code to so that it is able to store just the part of the log starting at some index X. Initially you can set X to zero and run the 2B/2C tests. Then make Snapshot(index) discard the log before index, and set X equal to index. If all goes well you should now pass the first 2D test.

func (rf *Raft) Snapshot(index int, snapshot []byte) {
	// Your code here (2D).
	rf.mu.Lock()
	defer rf.mu.Unlock()
	PrettyDebug(dSnap, "S%d Snapshot1 index:%d,len(rf.log):%d",
		rf.me, index, len(rf.log))
	baseIndex := rf.log[0].Index
	if index < baseIndex {
		return
	}

	rf.discardLog(index)

	PrettyDebug(dSnap, "S%d Snapshot2 index:%d,len(rf.log):%d",
		rf.me, index, len(rf.log))
	rf.persister.SaveStateAndSnapshot(rf.encodeState(), snapshot)
}

func (rf *Raft) discardLog(index int) {
	baseIndex := rf.log[0].Index
	log := make([]LogEntry, 1)
	log[0].Index = rf.log[index-baseIndex].Index
	log[0].Term = rf.log[index-baseIndex].Term
	log = append(log, rf.log[index-baseIndex+1:]...)

	rf.log = log
}

sendInstallSnapshot

Leader向peer发送快照

func (rf *Raft) sendInstallSnapshot(server int, args *InstallSnapshotArgs, reply *InstallSnapshotReply) bool {
	PrettyDebug(dSnap, "S%d -> S%d,sendInstallSnapshot", rf.me, server)
	ok := rf.peers[server].Call("Raft.InstallSnapshot", args, reply)

	rf.mu.Lock()
	defer rf.mu.Unlock()
	if !ok || rf.currentTerm != args.Term || rf.state != Leader {
		return ok
	}

	if reply.Term > rf.currentTerm {
		rf.state = Follow
		rf.currentTerm = reply.Term
		rf.votedFor = -1
		rf.persist()
		return ok
	}
	rf.nextIndex[server] = args.LastIncludedIndex + 1
	rf.matchIndex[server] = args.LastIncludedIndex
	PrettyDebug(dSnap, "S%d -> S%d,sendInstallSnapshot rf.nextIndex:%d,matchIndex:%d", rf.me, server, rf.nextIndex[server], rf.matchIndex[server])
	return ok
}

InstallSnapshot

peer安装Leader发来的快照,并向状态机提交快照

func (rf *Raft) InstallSnapshot(args *InstallSnapshotArgs, reply *InstallSnapshotReply) {
	rf.mu.Lock()
	defer rf.mu.Unlock()
	PrettyDebug(dSnap, "S%d <- S%d,InstallSnapshot LastIndex:%d,LastTerm:%d,Term:%d,currentTerm:%d,commitIndex:%d",
		rf.me, args.LeaderId, args.LastIncludedIndex, args.LastIncludedTerm, args.Term, rf.currentTerm, rf.commitIndex)
	reply.Term = rf.currentTerm
	if args.Term < rf.currentTerm {
		return
	}

	if args.Term > rf.currentTerm {
		rf.currentTerm = args.Term
		rf.votedFor = -1
		rf.state = Follow
		rf.persist()
	}

	rf.chanHeartbeat <- true

	// Leader传过来的快照比本地旧
	if args.LastIncludedIndex <= rf.commitIndex {
		return
	}

	go func() {
		PrettyDebug(dSnap, "S%d <- S%d,InstallSnapshot apply LastIndex:%d,LastTerm:%d",
			rf.me, args.LeaderId, args.LastIncludedIndex, args.LastIncludedTerm)
		rf.applyCh <- ApplyMsg{
			SnapshotValid: true,
			Snapshot:      args.Data,
			SnapshotTerm:  args.LastIncludedTerm,
			SnapshotIndex: args.LastIncludedIndex,
		}
	}()
}

CondInstallSnapshot

状态机调用peer的这个函数,Raft判断是否要安装快照

func (rf *Raft) CondInstallSnapshot(lastIncludedTerm int, lastIncludedIndex int, snapshot []byte) bool {

	// Your code here (2D).
	rf.mu.Lock()
	defer rf.mu.Unlock()
	PrettyDebug(dSnap, "S%d,CondInstallSnapshot lastIncludedTerm:%d,lastIncludedIndex:%d,rf.commitIndex:%d",
		rf.me, lastIncludedTerm, lastIncludedIndex, rf.commitIndex)

	if lastIncludedIndex <= rf.commitIndex {
		return false
	}
	baseIndex := rf.log[0].Index
	if lastIncludedIndex > rf.getLastIndex() {
		rf.log = make([]LogEntry, 1)
		rf.log[0].Index, rf.log[0].Term = lastIncludedIndex, lastIncludedTerm
	} else {
		log := make([]LogEntry, 1)
		log[0].Index, log[0].Term = lastIncludedIndex, lastIncludedTerm
		log = append(log, rf.log[lastIncludedIndex-baseIndex+1:]...)
		rf.log = log
	}

	rf.lastApplied = lastIncludedIndex
	rf.commitIndex = lastIncludedIndex

	PrettyDebug(dSnap, "S%d,CondInstallSnapshot rf.log:%d",
		rf.me, rf.log)
	rf.persister.SaveStateAndSnapshot(rf.encodeState(), snapshot)

	return true
}

Leader丢弃了还未同步到peer的日之后,日志同步的时候由于有些peer落后过多,只能接受Leader的快照才能跟上同步,修改sendheartbeat函数.

if rf.nextIndex[server] < baseIndex {
    args := &InstallSnapshotArgs{}
    args.LastIncludedIndex = rf.log[0].Index
    args.LastIncludedTerm = rf.log[0].Term
    args.LeaderId = rf.me
    args.Term = rf.currentTerm
    args.data = rf.persister.ReadSnapshot() 
    go rf.sendInstallSnapshot(server, args, &InstallSnapshotReply{})
}

还有一个坑就是重启机器后调用make函数会把lastApplied置0,需要readPersist然后置为LastIncludedIndex

rf.readPersist(persister.ReadRaftState())
rf.lastApplied = rf.log[0].Index
rf.commitIndex = rf.log[0].Index
rf.persist()

img

posted @ 2023-03-11 10:12  autumn814  阅读(37)  评论(0)    收藏  举报