raft: Ignore proposals if not a current member.
Fixes another panic in MultiNode.Propose.
This commit is contained in:
@ -227,6 +227,54 @@ func TestProposeUnknownGroup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestProposeAfterRemoveLeader ensures that we gracefully handle
|
||||||
|
// proposals that are attempted after a leader has been removed from
|
||||||
|
// the active configuration, but before that leader has called
|
||||||
|
// MultiNode.RemoveGroup.
|
||||||
|
func TestProposeAfterRemoveLeader(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mn := newMultiNode(1)
|
||||||
|
go mn.run()
|
||||||
|
defer mn.Stop()
|
||||||
|
|
||||||
|
storage := NewMemoryStorage()
|
||||||
|
if err := mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, storage),
|
||||||
|
[]Peer{{ID: 1}}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := mn.Campaign(ctx, 1); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mn.ProposeConfChange(ctx, 1, raftpb.ConfChange{
|
||||||
|
Type: raftpb.ConfChangeRemoveNode,
|
||||||
|
NodeID: 1,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
gs := <-mn.Ready()
|
||||||
|
g := gs[1]
|
||||||
|
if err := storage.Append(g.Entries); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, e := range g.CommittedEntries {
|
||||||
|
if e.Type == raftpb.EntryConfChange {
|
||||||
|
var cc raftpb.ConfChange
|
||||||
|
if err := cc.Unmarshal(e.Data); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
mn.ApplyConfChange(1, cc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mn.Advance(gs)
|
||||||
|
|
||||||
|
if err := mn.Propose(ctx, 1, []byte("somedata")); err != nil {
|
||||||
|
t.Errorf("err = %v, want nil", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestNodeTick from node_test.go has no equivalent in multiNode because
|
// TestNodeTick from node_test.go has no equivalent in multiNode because
|
||||||
// it reaches into the raft object which is not exposed.
|
// it reaches into the raft object which is not exposed.
|
||||||
|
|
||||||
|
@ -522,6 +522,12 @@ func stepLeader(r *raft, m pb.Message) {
|
|||||||
if len(m.Entries) == 0 {
|
if len(m.Entries) == 0 {
|
||||||
r.logger.Panicf("%x stepped empty MsgProp", r.id)
|
r.logger.Panicf("%x stepped empty MsgProp", r.id)
|
||||||
}
|
}
|
||||||
|
if _, ok := r.prs[r.id]; !ok {
|
||||||
|
// If we are not currently a member of the range (i.e. this node
|
||||||
|
// was removed from the configuration while serving as leader),
|
||||||
|
// drop any new proposals.
|
||||||
|
return
|
||||||
|
}
|
||||||
for i, e := range m.Entries {
|
for i, e := range m.Entries {
|
||||||
if e.Type == pb.EntryConfChange {
|
if e.Type == pb.EntryConfChange {
|
||||||
if r.pendingConf {
|
if r.pendingConf {
|
||||||
|
Reference in New Issue
Block a user