Skip to content

Commit a11b5b9

Browse files
NRG: Fix single node election (#7642)
This commit fixes single node election: previously, a single node would simply store its vote, and never check if it already reached a majority. So it would never transition to the leader state. Signed-off-by: Daniele Sciascia <[email protected]>
2 parents daa61e2 + 9c5fc82 commit a11b5b9

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

server/raft.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3396,9 +3396,9 @@ func (n *raft) runAsCandidate() {
33963396
n.requestVote()
33973397

33983398
// We vote for ourselves.
3399-
votes := map[string]struct{}{
3400-
n.ID(): {},
3401-
}
3399+
n.votes.push(&voteResponse{term: n.term, peer: n.ID(), granted: true})
3400+
3401+
votes := map[string]struct{}{}
34023402
emptyVotes := map[string]struct{}{}
34033403

34043404
for n.State() == Candidate {
@@ -4206,6 +4206,9 @@ func (n *raft) sendAppendEntryLocked(entries []*Entry, checkLeader bool) error {
42064206
if !shouldStore {
42074207
ae.returnToPool()
42084208
}
4209+
if n.csz == 1 {
4210+
n.tryCommit(n.pindex)
4211+
}
42094212
return nil
42104213
}
42114214

server/raft_helpers_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,9 @@ func (rg smGroup) waitOnTotal(t *testing.T, expected int64) {
402402
checkFor(t, 5*time.Second, 200*time.Millisecond, func() error {
403403
var err error
404404
for _, sm := range rg {
405+
if sm.node().State() == Closed {
406+
continue
407+
}
405408
asm := sm.(*stateAdder)
406409
if total := asm.total(); total != expected {
407410
err = errors.Join(err, fmt.Errorf("Adder on %v has wrong total: %d vs %d",

server/raft_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4571,3 +4571,59 @@ func TestNRGAppendEntryResurrectsLeader(t *testing.T) {
45714571
require_Equal(t, len(n.peers), 1)
45724572
require_Equal(t, n.ClusterSize(), 1)
45734573
}
4574+
4575+
func TestNRGSingleNodeElection(t *testing.T) {
4576+
c := createJetStreamClusterExplicit(t, "R3S", 3)
4577+
defer c.shutdown()
4578+
4579+
rg := c.createMemRaftGroup("TEST", 3, newStateAdder)
4580+
4581+
// Remove the cluster leader, and then again
4582+
for range 2 {
4583+
leader := rg.waitOnLeader().node()
4584+
require_NoError(t, leader.ProposeRemovePeer(leader.ID()))
4585+
checkFor(t, 1*time.Second, 10*time.Millisecond, func() error {
4586+
if leader.State() == Leader {
4587+
return errors.New("Removed node is still leader")
4588+
}
4589+
return nil
4590+
})
4591+
require_False(t, leader.MembershipChangeInProgress())
4592+
}
4593+
4594+
// The remaining follower must be able to become leader
4595+
// on its own
4596+
newLeader := rg.waitOnLeader()
4597+
require_Equal(t, len(newLeader.node().Peers()), 1)
4598+
require_Equal(t, newLeader.node().ClusterSize(), 1)
4599+
require_False(t, newLeader.node().MembershipChangeInProgress())
4600+
4601+
adder := newLeader.(*stateAdder)
4602+
adder.proposeDelta(1)
4603+
adder.proposeDelta(10)
4604+
adder.proposeDelta(100)
4605+
4606+
rg.waitOnTotal(t, 111)
4607+
4608+
// Add two nodes back
4609+
rg = append(rg, c.addMemRaftNode("TEST", newStateAdder))
4610+
rg = append(rg, c.addMemRaftNode("TEST", newStateAdder))
4611+
4612+
checkFor(t, 1*time.Second, 10*time.Millisecond, func() error {
4613+
if newLeader.node().ClusterSize() != 3 {
4614+
return errors.New("node additions still in progress")
4615+
}
4616+
return nil
4617+
})
4618+
4619+
checkFor(t, 1*time.Second, 10*time.Millisecond, func() error {
4620+
if newLeader.node().MembershipChangeInProgress() {
4621+
return errors.New("membership still in progress")
4622+
}
4623+
return nil
4624+
})
4625+
4626+
rg.waitOnTotal(t, 111)
4627+
require_Equal(t, newLeader.node().ClusterSize(), 3)
4628+
require_False(t, newLeader.node().MembershipChangeInProgress())
4629+
}

0 commit comments

Comments
 (0)