From 79bf3b0df4eb4d2545b3c566ec0c1f8ae47f2b76 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Tue, 6 Sep 2022 13:04:27 +0200 Subject: [PATCH] TestRawNodeJointAutoLeave This now needs an additional Ready cycle to apply the previous conf change, so the finalizing conf change does too. Signed-off-by: Tobias Grieger --- raft/rawnode_test.go | 227 ++++++++++++++++++++++--------------------- 1 file changed, 115 insertions(+), 112 deletions(-) diff --git a/raft/rawnode_test.go b/raft/rawnode_test.go index 413f232df..567227f61 100644 --- a/raft/rawnode_test.go +++ b/raft/rawnode_test.go @@ -388,122 +388,125 @@ func TestRawNodeJointAutoLeave(t *testing.T) { } exp2Cs := pb.ConfState{Voters: []uint64{1}, Learners: []uint64{2}} - t.Run("", func(t *testing.T) { - s := newTestMemoryStorage(withPeers(1)) - rawNode, err := NewRawNode(newTestConfig(1, 10, 1, s)) - if err != nil { - t.Fatal(err) - } + s := newTestMemoryStorage(withPeers(1)) + rawNode, err := NewRawNode(newTestConfig(1, 10, 1, s)) + if err != nil { + t.Fatal(err) + } - rawNode.Campaign() - proposed := false - var ( - lastIndex uint64 - ccdata []byte - ) - // Propose the ConfChange, wait until it applies, save the resulting - // ConfState. - var cs *pb.ConfState - for cs == nil { - rd := rawNode.Ready() - s.Append(rd.Entries) - for _, ent := range rd.CommittedEntries { - var cc pb.ConfChangeI - if ent.Type == pb.EntryConfChangeV2 { - var ccc pb.ConfChangeV2 - if err = ccc.Unmarshal(ent.Data); err != nil { - t.Fatal(err) - } - cc = &ccc - } - if cc != nil { - // Force it step down. - rawNode.Step(pb.Message{Type: pb.MsgHeartbeatResp, From: 1, Term: rawNode.raft.Term + 1}) - cs = rawNode.ApplyConfChange(cc) - } - } - rawNode.Advance(rd) - // Once we are the leader, propose a command and a ConfChange. - if !proposed && rd.SoftState.Lead == rawNode.raft.id { - if err = rawNode.Propose([]byte("somedata")); err != nil { - t.Fatal(err) - } - ccdata, err = testCc.Marshal() - if err != nil { - t.Fatal(err) - } - rawNode.ProposeConfChange(testCc) - proposed = true - } - } - - // Check that the last index is exactly the conf change we put in, - // down to the bits. Note that this comes from the Storage, which - // will not reflect any unstable entries that we'll only be presented - // with in the next Ready. - lastIndex, err = s.LastIndex() - if err != nil { - t.Fatal(err) - } - - entries, err := s.Entries(lastIndex-1, lastIndex+1, noLimit) - if err != nil { - t.Fatal(err) - } - if len(entries) != 2 { - t.Fatalf("len(entries) = %d, want %d", len(entries), 2) - } - if !bytes.Equal(entries[0].Data, []byte("somedata")) { - t.Errorf("entries[0].Data = %v, want %v", entries[0].Data, []byte("somedata")) - } - if entries[1].Type != pb.EntryConfChangeV2 { - t.Fatalf("type = %v, want %v", entries[1].Type, pb.EntryConfChangeV2) - } - if !bytes.Equal(entries[1].Data, ccdata) { - t.Errorf("data = %v, want %v", entries[1].Data, ccdata) - } - - if !reflect.DeepEqual(&expCs, cs) { - t.Fatalf("exp:\n%+v\nact:\n%+v", expCs, cs) - } - - if rawNode.raft.pendingConfIndex != 0 { - t.Fatalf("pendingConfIndex: expected %d, got %d", 0, rawNode.raft.pendingConfIndex) - } - - // Move the RawNode along. It should not leave joint because it's follower. - rd := rawNode.readyWithoutAccept() - // Check that the right ConfChange comes out. - if len(rd.Entries) != 0 { - t.Fatalf("expected zero entry, got %+v", rd) - } - - // Make it leader again. It should leave joint automatically after moving apply index. - rawNode.Campaign() - rd = rawNode.Ready() + rawNode.Campaign() + proposed := false + var ( + lastIndex uint64 + ccdata []byte + ) + // Propose the ConfChange, wait until it applies, save the resulting + // ConfState. + var cs *pb.ConfState + for cs == nil { + rd := rawNode.Ready() s.Append(rd.Entries) + for _, ent := range rd.CommittedEntries { + var cc pb.ConfChangeI + if ent.Type == pb.EntryConfChangeV2 { + var ccc pb.ConfChangeV2 + if err = ccc.Unmarshal(ent.Data); err != nil { + t.Fatal(err) + } + cc = &ccc + } + if cc != nil { + // Force it step down. + rawNode.Step(pb.Message{Type: pb.MsgHeartbeatResp, From: 1, Term: rawNode.raft.Term + 1}) + cs = rawNode.ApplyConfChange(cc) + } + } rawNode.Advance(rd) - rd = rawNode.Ready() - s.Append(rd.Entries) + // Once we are the leader, propose a command and a ConfChange. + if !proposed && rd.SoftState.Lead == rawNode.raft.id { + if err = rawNode.Propose([]byte("somedata")); err != nil { + t.Fatal(err) + } + ccdata, err = testCc.Marshal() + if err != nil { + t.Fatal(err) + } + rawNode.ProposeConfChange(testCc) + proposed = true + } + } - // Check that the right ConfChange comes out. - if len(rd.Entries) != 1 || rd.Entries[0].Type != pb.EntryConfChangeV2 { - t.Fatalf("expected exactly one more entry, got %+v", rd) - } - var cc pb.ConfChangeV2 - if err := cc.Unmarshal(rd.Entries[0].Data); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(cc, pb.ConfChangeV2{Context: nil}) { - t.Fatalf("expected zero ConfChangeV2, got %+v", cc) - } - // Lie and pretend the ConfChange applied. It won't do so because now - // we require the joint quorum and we're only running one node. - cs = rawNode.ApplyConfChange(cc) - if exp := exp2Cs; !reflect.DeepEqual(&exp, cs) { - t.Fatalf("exp:\n%+v\nact:\n%+v", exp, cs) - } - }) + // Check that the last index is exactly the conf change we put in, + // down to the bits. Note that this comes from the Storage, which + // will not reflect any unstable entries that we'll only be presented + // with in the next Ready. + lastIndex, err = s.LastIndex() + if err != nil { + t.Fatal(err) + } + + entries, err := s.Entries(lastIndex-1, lastIndex+1, noLimit) + if err != nil { + t.Fatal(err) + } + if len(entries) != 2 { + t.Fatalf("len(entries) = %d, want %d", len(entries), 2) + } + if !bytes.Equal(entries[0].Data, []byte("somedata")) { + t.Errorf("entries[0].Data = %v, want %v", entries[0].Data, []byte("somedata")) + } + if entries[1].Type != pb.EntryConfChangeV2 { + t.Fatalf("type = %v, want %v", entries[1].Type, pb.EntryConfChangeV2) + } + if !bytes.Equal(entries[1].Data, ccdata) { + t.Errorf("data = %v, want %v", entries[1].Data, ccdata) + } + + if !reflect.DeepEqual(&expCs, cs) { + t.Fatalf("exp:\n%+v\nact:\n%+v", expCs, cs) + } + + if rawNode.raft.pendingConfIndex != 0 { + t.Fatalf("pendingConfIndex: expected %d, got %d", 0, rawNode.raft.pendingConfIndex) + } + + // Move the RawNode along. It should not leave joint because it's follower. + rd := rawNode.readyWithoutAccept() + // Check that the right ConfChange comes out. + if len(rd.Entries) != 0 { + t.Fatalf("expected zero entry, got %+v", rd) + } + + // Make it leader again. It should leave joint automatically after moving apply index. + rawNode.Campaign() + rd = rawNode.Ready() + t.Log(DescribeReady(rd, nil)) + s.Append(rd.Entries) + rawNode.Advance(rd) + rd = rawNode.Ready() + t.Log(DescribeReady(rd, nil)) + s.Append(rd.Entries) + rawNode.Advance(rd) + rd = rawNode.Ready() + t.Log(DescribeReady(rd, nil)) + s.Append(rd.Entries) + // Check that the right ConfChange comes out. + if len(rd.Entries) != 1 || rd.Entries[0].Type != pb.EntryConfChangeV2 { + t.Fatalf("expected exactly one more entry, got %+v", rd) + } + var cc pb.ConfChangeV2 + if err := cc.Unmarshal(rd.Entries[0].Data); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(cc, pb.ConfChangeV2{Context: nil}) { + t.Fatalf("expected zero ConfChangeV2, got %+v", cc) + } + // Lie and pretend the ConfChange applied. It won't do so because now + // we require the joint quorum and we're only running one node. + cs = rawNode.ApplyConfChange(cc) + if exp := exp2Cs; !reflect.DeepEqual(&exp, cs) { + t.Fatalf("exp:\n%+v\nact:\n%+v", exp, cs) + } } // TestRawNodeProposeAddDuplicateNode ensures that two proposes to add the same node should