Преглед на файлове

raft: no-op instead of panic for Campaigning while leader

We need to be able to force an election (on one node) after creating a
new group (cockroachdb/cockroach#1384), but it is difficult to ensure
that our call to Campaign does not race with an election that may be
started by raft itself. A redundant call to Campaign should be a no-op
instead of a panic. (But the panic in becomeCandidate remains, because
we don't want to update the term or change the committed index in this
case)
Ben Darnell преди 10 години
родител
ревизия
fbeb58d265
променени са 2 файла, в които са добавени 28 реда и са изтрити 3 реда
  1. 7 3
      raft/raft.go
  2. 21 0
      raft/raft_test.go

+ 7 - 3
raft/raft.go

@@ -485,9 +485,13 @@ func (r *raft) poll(id uint64, v bool) (granted int) {
 
 func (r *raft) Step(m pb.Message) error {
 	if m.Type == pb.MsgHup {
-		r.logger.Infof("%x is starting a new election at term %d", r.id, r.Term)
-		r.campaign()
-		r.Commit = r.raftLog.committed
+		if r.state != StateLeader {
+			r.logger.Infof("%x is starting a new election at term %d", r.id, r.Term)
+			r.campaign()
+			r.Commit = r.raftLog.committed
+		} else {
+			r.logger.Debugf("%x ignoring MsgHup because already leader", r.id)
+		}
 		return nil
 	}
 

+ 21 - 0
raft/raft_test.go

@@ -1751,6 +1751,27 @@ func TestRaftNodes(t *testing.T) {
 	}
 }
 
+func TestCampaignWhileLeader(t *testing.T) {
+	r := newTestRaft(1, []uint64{1}, 5, 1, NewMemoryStorage())
+	if r.state != StateFollower {
+		t.Errorf("expected new node to be follower but got %s", r.state)
+	}
+	// We don't call campaign() directly because it comes after the check
+	// for our current state.
+	r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+	if r.state != StateLeader {
+		t.Errorf("expected single-node election to become leader but got %s", r.state)
+	}
+	term := r.Term
+	r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+	if r.state != StateLeader {
+		t.Errorf("expected to remain leader but got %s", r.state)
+	}
+	if r.Term != term {
+		t.Errorf("expected to remain in term %v but got %v", term, r.Term)
+	}
+}
+
 func ents(terms ...uint64) *raft {
 	storage := NewMemoryStorage()
 	for i, term := range terms {