mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
etcdserver: fix quorum calculation when promoting a learner member
When promoting a learner member we should not count already a voting member, but take only into account the number of existing voting members and their current status (started, unstarted) when taking the decision whether a learner member can be promoted. Before this change, it was impossible to grow from a quorum N to a N+1 through promoting a learning member. Fixes: #11633
This commit is contained in:
parent
cdb2dc11b8
commit
257319fb18
@ -657,8 +657,8 @@ func (c *RaftCluster) IsReadyToRemoveVotingMember(id uint64) bool {
|
||||
}
|
||||
|
||||
func (c *RaftCluster) IsReadyToPromoteMember(id uint64) bool {
|
||||
nmembers := 1
|
||||
nstarted := 0
|
||||
nmembers := 1 // We count the learner to be promoted for the future quorum
|
||||
nstarted := 1 // and we also count it as started.
|
||||
|
||||
for _, member := range c.VotingMembers() {
|
||||
if member.IsStarted() {
|
||||
|
@ -859,3 +859,90 @@ func TestIsReadyToRemoveVotingMember(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsReadyToPromoteMember(t *testing.T) {
|
||||
tests := []struct {
|
||||
members []*Member
|
||||
promoteID uint64
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
// 1/1 members ready, should succeed (quorum = 1, new quorum = 2)
|
||||
[]*Member{
|
||||
newTestMember(1, nil, "1", nil),
|
||||
newTestMemberAsLearner(2, nil, "2", nil),
|
||||
},
|
||||
2,
|
||||
true,
|
||||
},
|
||||
{
|
||||
// 0/1 members ready, should fail (quorum = 1)
|
||||
[]*Member{
|
||||
newTestMember(1, nil, "", nil),
|
||||
newTestMemberAsLearner(2, nil, "2", nil),
|
||||
},
|
||||
2,
|
||||
false,
|
||||
},
|
||||
{
|
||||
// 2/2 members ready, should succeed (quorum = 2)
|
||||
[]*Member{
|
||||
newTestMember(1, nil, "1", nil),
|
||||
newTestMember(2, nil, "2", nil),
|
||||
newTestMemberAsLearner(3, nil, "3", nil),
|
||||
},
|
||||
3,
|
||||
true,
|
||||
},
|
||||
{
|
||||
// 1/2 members ready, should succeed (quorum = 2)
|
||||
[]*Member{
|
||||
newTestMember(1, nil, "1", nil),
|
||||
newTestMember(2, nil, "", nil),
|
||||
newTestMemberAsLearner(3, nil, "3", nil),
|
||||
},
|
||||
3,
|
||||
true,
|
||||
},
|
||||
{
|
||||
// 1/3 members ready, should fail (quorum = 2)
|
||||
[]*Member{
|
||||
newTestMember(1, nil, "1", nil),
|
||||
newTestMember(2, nil, "", nil),
|
||||
newTestMember(3, nil, "", nil),
|
||||
newTestMemberAsLearner(4, nil, "4", nil),
|
||||
},
|
||||
4,
|
||||
false,
|
||||
},
|
||||
{
|
||||
// 2/3 members ready, should succeed (quorum = 2, new quorum = 3)
|
||||
[]*Member{
|
||||
newTestMember(1, nil, "1", nil),
|
||||
newTestMember(2, nil, "2", nil),
|
||||
newTestMember(3, nil, "", nil),
|
||||
newTestMemberAsLearner(4, nil, "4", nil),
|
||||
},
|
||||
4,
|
||||
true,
|
||||
},
|
||||
{
|
||||
// 2/4 members ready, should succeed (quorum = 3)
|
||||
[]*Member{
|
||||
newTestMember(1, nil, "1", nil),
|
||||
newTestMember(2, nil, "2", nil),
|
||||
newTestMember(3, nil, "", nil),
|
||||
newTestMember(4, nil, "", nil),
|
||||
newTestMemberAsLearner(5, nil, "5", nil),
|
||||
},
|
||||
5,
|
||||
true,
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
c := newTestCluster(tt.members)
|
||||
if got := c.IsReadyToPromoteMember(tt.promoteID); got != tt.want {
|
||||
t.Errorf("%d: isReadyToPromoteMember returned %t, want %t", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user