|
|
@@ -803,9 +803,69 @@ func TestHandleHeartbeat(t *testing.T) {
|
|
|
t.Errorf("#%d: committed = %d, want %d", i, sm.raftLog.committed, tt.wCommit)
|
|
|
}
|
|
|
m := sm.readMessages()
|
|
|
- if len(m) != 0 {
|
|
|
- t.Fatalf("#%d: msg = nil, want 0", i)
|
|
|
+ if len(m) != 1 {
|
|
|
+ t.Fatalf("#%d: msg = nil, want 1", i)
|
|
|
}
|
|
|
+ if m[0].Type != pb.MsgHeartbeatResp {
|
|
|
+ t.Errorf("#%d: type = %v, want MsgHeartbeatResp", i, m[0].Type)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// TestHandleHeartbeatResp ensures that we re-send log entries when we get a heartbeat response.
|
|
|
+func TestHandleHeartbeatResp(t *testing.T) {
|
|
|
+ storage := NewMemoryStorage()
|
|
|
+ storage.Append([]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}})
|
|
|
+ sm := newRaft(1, []uint64{1, 2}, 5, 1, storage)
|
|
|
+ sm.becomeCandidate()
|
|
|
+ sm.becomeLeader()
|
|
|
+ sm.raftLog.commitTo(sm.raftLog.lastIndex())
|
|
|
+
|
|
|
+ // A heartbeat response from a node that is behind; re-send MsgApp
|
|
|
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
|
|
|
+ msgs := sm.readMessages()
|
|
|
+ if len(msgs) != 1 {
|
|
|
+ t.Fatalf("len(msgs) = %d, want 1", len(msgs))
|
|
|
+ }
|
|
|
+ if msgs[0].Type != pb.MsgApp {
|
|
|
+ t.Errorf("type = %v, want MsgApp", msgs[0].Type)
|
|
|
+ }
|
|
|
+
|
|
|
+ // A second heartbeat response with no AppResp does not re-send because we are in the wait state.
|
|
|
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
|
|
|
+ msgs = sm.readMessages()
|
|
|
+ if len(msgs) != 0 {
|
|
|
+ t.Fatalf("len(msgs) = %d, want 0", len(msgs))
|
|
|
+ }
|
|
|
+
|
|
|
+ // Send a heartbeat to reset the wait state; next heartbeat will re-send MsgApp.
|
|
|
+ sm.bcastHeartbeat()
|
|
|
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
|
|
|
+ msgs = sm.readMessages()
|
|
|
+ if len(msgs) != 2 {
|
|
|
+ t.Fatalf("len(msgs) = %d, want 2", len(msgs))
|
|
|
+ }
|
|
|
+ if msgs[0].Type != pb.MsgHeartbeat {
|
|
|
+ t.Errorf("type = %v, want MsgHeartbeat", msgs[0].Type)
|
|
|
+ }
|
|
|
+ if msgs[1].Type != pb.MsgApp {
|
|
|
+ t.Errorf("type = %v, want MsgApp", msgs[1].Type)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Once we have an MsgAppResp, heartbeats no longer send MsgApp.
|
|
|
+ sm.Step(pb.Message{
|
|
|
+ From: 2,
|
|
|
+ Type: pb.MsgAppResp,
|
|
|
+ Index: msgs[1].Index + uint64(len(msgs[1].Entries)),
|
|
|
+ })
|
|
|
+ sm.bcastHeartbeat() // reset wait state
|
|
|
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
|
|
|
+ msgs = sm.readMessages()
|
|
|
+ if len(msgs) != 1 {
|
|
|
+ t.Fatalf("len(msgs) = %d, want 1", len(msgs))
|
|
|
+ }
|
|
|
+ if msgs[0].Type != pb.MsgHeartbeat {
|
|
|
+ t.Errorf("type = %v, want MsgHeartbeat", msgs[0].Type)
|
|
|
}
|
|
|
}
|
|
|
|