tinykv proj2ab about overwrite RaftLog test

对raft_test.go 里的这个测试函数有疑问。在newStorage的时候,所有Raft的RaftLog会存在一个empty的Log,也就是Index和Term都是0。那么初始化之后sm1会存在2个Log,一个是(0, 0), 一个是(1, 1) [左边数字代表Index, 右边代表Term]。在sm1成功当选Leader后,应该要append一个新的Log(2, 3),并把RaftLog广播给所有节点。那这样的话,len(sm.entries) 不应该等于3吗?为什么等于2?如果我的理解错了,请麻烦指出,谢谢。

func TestLeaderElectionOverwriteNewerLogs2AB(t *testing.T) {
	cfg := func(c *Config) {
		c.peers = idsBySize(5)
	}
	// This network represents the results of the following sequence of
	// events:
	// - Node 1 won the election in term 1.
	// - Node 1 replicated a log entry to node 2 but died before sending
	//   it to other nodes.
	// - Node 3 won the second election in term 2.
	// - Node 3 wrote an entry to its logs but died without sending it
	//   to any other nodes.
	//
	// At this point, nodes 1, 2, and 3 all have uncommitted entries in
	// their logs and could win an election at term 3. The winner's log
	// entry overwrites the losers'. (TestLeaderSyncFollowerLog tests
	// the case where older log entries are overwritten, so this test
	// focuses on the case where the newer entries are lost).
	n := newNetworkWithConfig(cfg,
		entsWithConfig(cfg, 1),     // Node 1: Won first election
		entsWithConfig(cfg, 1),     // Node 2: Got logs from node 1
		entsWithConfig(cfg, 2),     // Node 3: Won second election
		votedWithConfig(cfg, 3, 2), // Node 4: Voted but didn't get logs
		votedWithConfig(cfg, 3, 2)) // Node 5: Voted but didn't get logs
	
	// Node 1 campaigns. The election fails because a quorum of nodes
	// know about the election that already happened at term 2. Node 1's
	// term is pushed ahead to 2.
	n.send(pb.Message{From: 1, To: 1, MsgType: pb.MessageType_MsgHup})
	sm1 := n.peers[1].(*Raft)
	if sm1.State != StateFollower {
		t.Errorf("state = %s, want StateFollower", sm1.State)
	}
	if sm1.Term != 2 {
		t.Errorf("term = %d, want 2", sm1.Term)
	}

	// Node 1 campaigns again with a higher term. This time it succeeds.
	n.send(pb.Message{From: 1, To: 1, MsgType: pb.MessageType_MsgHup})
	if sm1.State != StateLeader {
		t.Errorf("state = %s, want StateLeader", sm1.State)
	}
	if sm1.Term != 3 {
		t.Errorf("term = %d, want 3", sm1.Term)
	}
	// fmt.Printf("sm2: %d %d\
", sm2.RaftLog.entries[1].Index, sm2.RaftLog.entries[1].Term)

	// Now all nodes agree on a log entry with term 1 at index 1 (and
	// term 3 at index 2).
	for i := range n.peers {
		sm := n.peers[i].(*Raft)
		entries := sm.RaftLog.entries
		if len(entries) != 2 {
			t.Fatalf("node %d: len(entries) == %d, want 2", i, len(entries))
		}
		if entries[0].Term != 1 {
			t.Errorf("node %d: term at index 1 == %d, want 1", i, entries[0].Term)
		}
		if entries[1].Term != 3 {
			t.Errorf("node %d: term at index 2 == %d, want 3", i, entries[1].Term)
		}
	}
}

在newStorage的时候,所有Raft的RaftLog会存在一个empty的Log,也就是Index和Term都是0

这个 dummy entries 只在 MemoryStorage 内部实现使用,MemoryStorage.Entries 也是不会返回这个 entry 的,另外测试里的 sm.RaftLog.entries 已经由 sm.RaftLog.allEntries() 代替了,可以拉取最新的 course 分支查看

您好,我想问下,在下面这个测试里面

这里的wents并不包含初始化的日志项(0, 0),但是下面的r.RaftLog.entries就会包含(0,0),是不是存在问题呢?:pray:

 func TestFollowerAppendEntries2AB(t *testing.T) {
	tests := []struct {
		index, term uint64
		ents        []*pb.Entry
		wents       []*pb.Entry
		wunstable   []*pb.Entry
	}{
		{
			2, 2,
			[]*pb.Entry{{Term: 3, Index: 3}},
			[]*pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}, {Term: 3, Index: 3}},
			[]*pb.Entry{{Term: 3, Index: 3}},
		},
		{
			1, 1,
			[]*pb.Entry{{Term: 3, Index: 2}, {Term: 4, Index: 3}},
			[]*pb.Entry{{Term: 1, Index: 1}, {Term: 3, Index: 2}, {Term: 4, Index: 3}},
			[]*pb.Entry{{Term: 3, Index: 2}, {Term: 4, Index: 3}},
		},
		{
			0, 0,
			[]*pb.Entry{{Term: 1, Index: 1}},
			[]*pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}},
			[]*pb.Entry{},
		},
		{
			0, 0,
			[]*pb.Entry{{Term: 3, Index: 1}},
			[]*pb.Entry{{Term: 3, Index: 1}},
			[]*pb.Entry{{Term: 3, Index: 1}},
		},
	}
	for i, tt := range tests {
		storage := NewMemoryStorage()
		storage.Append([]pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}})
		r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, storage)
		r.becomeFollower(2, 2)

		r.Step(pb.Message{From: 2, To: 1, MsgType: pb.MessageType_MsgAppend, Term: 2, LogTerm: tt.term, Index: tt.index, Entries: tt.ents})
                
     
  // 这里的wents并不包含初始化的日志项(0, 0),但是下面的r.RaftLog.entries就会包含(0,0),是不是存在问题呢?
		wents := make([]pb.Entry, 0, len(tt.wents))
		for _, ent := range tt.wents {
			wents = append(wents, *ent)
		}
                
		if g := r.RaftLog.entries; !reflect.DeepEqual(g, wents) {
			t.Errorf("#%d: ents = %+v, want %+v", i, g, wents)
		}
		var wunstable []pb.Entry
		if tt.wunstable != nil {
			wunstable = make([]pb.Entry, 0, len(tt.wunstable))
		}
		for _, ent := range tt.wunstable {
			wunstable = append(wunstable, *ent)
		}
		if g := r.RaftLog.unstableEntries(); !reflect.DeepEqual(g, wunstable) {
			t.Errorf("#%d: unstableEnts = %+v, want %+v", i, g, wunstable)
		}
	}
}

和上面一样,(0, 0) 这个 dummy entries 只在 MemoryStorage 内部实现使用,MemoryStorage.Entries 是不会返回这个 (0, 0) 的,所以 r.RaftLog.entries 里不应该包含 (0, 0)

1 个赞

不好意思,我还有问题想请教:pleading_face:
在上面的测试中,就拿#1, i = 1, tt = tests[1]来举例。
(1)节点创建的时候,storage里面有两个日志项(1, 1), (2, 2)。那么此时的committed不就应该是2吗?(且既然已经持久化存储了,那么不就是已经提交了吗?已经committed的日志项不是应该不能再次改变了吗?)
(2)r.Step(pb.Message{From: 2, To: 1, MsgType: pb.MessageType_MsgAppend, Term: 2, LogTerm: tt.term, Index: tt.index, Entries: tt.ents}) 这是在Term==2的时候,发送了Term=3,Term=4的日志项吗?这个是合理的吗?

持久化了的日志并不代表已经 commit 了,比如 follower 需要持久化了日志才能给 leader 响应 MsgAppendResp ,但这时 follower 上的日志并没有 commit

感谢指出,这里确实有问题,可以帮忙提个 PR 修复么 :rofl:

十分感谢,我明白了:heart:
关于提pr,我比较菜,我想要等把实验理解了才能做到:joy:,不过我会试试的,总得锻炼一下~

好的,期待~

此话题已在最后回复的 1 分钟后被自动关闭。不再允许新回复。