Skip to content

Commit

Permalink
raft: add more learner tests
Browse files Browse the repository at this point in the history
  • Loading branch information
siddontang committed Nov 7, 2017
1 parent 530b89b commit beb8931
Showing 1 changed file with 110 additions and 0 deletions.
110 changes: 110 additions & 0 deletions raft/raft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,19 @@ func TestLearnerPromotion(t *testing.T) {
}
}

// TestLearnerCannotVote checks that a learner can't vote even it receives a valid Vote request.
func TestLearnerCannotVote(t *testing.T) {
n2 := newTestLearnerRaft(2, []uint64{1}, []uint64{2}, 10, 1, NewMemoryStorage())

n2.becomeFollower(1, None)

n2.Step(pb.Message{From: 1, To: 2, Term: 2, Type: pb.MsgVote, LogTerm: 11, Index: 11})

if len(n2.msgs) != 0 {
t.Error("n2 is learner, can't vote")
}
}

func TestLeaderCycle(t *testing.T) {
testLeaderCycle(t, false)
}
Expand Down Expand Up @@ -672,6 +685,48 @@ func TestLogReplication(t *testing.T) {
}
}

// TestLearnerLogReplication tests that a learner can receive entries from the leader.
func TestLearnerLogReplication(t *testing.T) {
n1 := newTestLearnerRaft(1, []uint64{1}, []uint64{2}, 10, 1, NewMemoryStorage())
n2 := newTestLearnerRaft(2, []uint64{1}, []uint64{2}, 10, 1, NewMemoryStorage())

nt := newNetwork(n1, n2)

n1.becomeFollower(1, None)
n2.becomeFollower(1, None)

setRandomizedElectionTimeout(n1, n1.electionTimeout)
for i := 0; i < n1.electionTimeout; i++ {
n1.tick()
}

nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgBeat})

// n1 is leader and n2 is learner
if n1.state != StateLeader {
t.Errorf("peer 1 state: %s, want %s", n1.state, StateLeader)
}

if !n2.isLearner {
t.Error("peer 2 is not learner, want yes")
}

nextCommitted := n1.raftLog.committed + 1
nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
if n1.raftLog.committed != nextCommitted {
t.Errorf("peer 1 wants committed to %d, but still %d", nextCommitted, n1.raftLog.committed)
}

if n1.raftLog.committed != n2.raftLog.committed {
t.Error("peer 2 must receive the entry from leader, but not")
}

match := n1.getProgress(2).Match
if match != n2.raftLog.committed {
t.Errorf("progresss 2 of leader 1 wants match %d, but got %d", n2.raftLog.committed, match)
}
}

func TestSingleNodeCommit(t *testing.T) {
tt := newNetwork(nil)
tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
Expand Down Expand Up @@ -2489,6 +2544,39 @@ func TestRestoreLearnerPromotion(t *testing.T) {
}
}

// TestLearnerReceiveSnapshot tests that a learner can receive a snpahost from leader
func TestLearnerReceiveSnapshot(t *testing.T) {
// restore the state machine from a snapshot so it has a compacted log and a snapshot
s := pb.Snapshot{
Metadata: pb.SnapshotMetadata{
Index: 11, // magic number
Term: 11, // magic number
ConfState: pb.ConfState{Nodes: []uint64{1}, Learners: []uint64{2}},
},
}

n1 := newTestLearnerRaft(1, []uint64{1}, []uint64{2}, 10, 1, NewMemoryStorage())
n2 := newTestLearnerRaft(2, []uint64{1}, []uint64{2}, 10, 1, NewMemoryStorage())

n1.restore(s)

// Force set n1 appplied index.
n1.raftLog.appliedTo(n1.raftLog.committed)

nt := newNetwork(n1, n2)

setRandomizedElectionTimeout(n1, n1.electionTimeout)
for i := 0; i < n1.electionTimeout; i++ {
n1.tick()
}

nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgBeat})

if n2.raftLog.committed != n1.raftLog.committed {
t.Errorf("peer 2 must commit to %d, but %d", n1.raftLog.committed, n2.raftLog.committed)
}
}

func TestRestoreIgnoreSnapshot(t *testing.T) {
previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3}}
commit := uint64(1)
Expand Down Expand Up @@ -2732,6 +2820,7 @@ func TestAddNode(t *testing.T) {
}
}

// TestAddLearner tests that addLearner could update pendingConf and nodes correctly.
func TestAddLearner(t *testing.T) {
r := newTestRaft(1, []uint64{1}, 10, 1, NewMemoryStorage())
r.pendingConf = true
Expand Down Expand Up @@ -2806,6 +2895,27 @@ func TestRemoveNode(t *testing.T) {
}
}

// TestRemoveLearner tests that removeNode could update pendingConf, nodes and
// and removed list correctly.
func TestRemoveLearner(t *testing.T) {
r := newTestLearnerRaft(1, []uint64{1}, []uint64{2}, 10, 1, NewMemoryStorage())
r.pendingConf = true
r.removeNode(2)
if r.pendingConf {
t.Errorf("pendingConf = %v, want false", r.pendingConf)
}
w := []uint64{1}
if g := r.nodes(); !reflect.DeepEqual(g, w) {
t.Errorf("nodes = %v, want %v", g, w)
}

// remove all nodes from cluster
r.removeNode(1)
w = []uint64{}
if g := r.nodes(); !reflect.DeepEqual(g, w) {
t.Errorf("nodes = %v, want %v", g, w)
}
}
func TestPromotable(t *testing.T) {
id := uint64(1)
tests := []struct {
Expand Down

0 comments on commit beb8931

Please sign in to comment.