Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions server/raft.go
Original file line number Diff line number Diff line change
Expand Up @@ -3483,6 +3483,7 @@ func (n *raft) resetWAL() {

// Lock should be held
func (n *raft) updateLeader(newLeader string) {
wasLeader := n.leader == n.id
n.leader = newLeader
n.hasleader.Store(newLeader != _EMPTY_)
if !n.pleader.Load() && newLeader != noLeader {
Expand All @@ -3499,9 +3500,9 @@ func (n *raft) updateLeader(newLeader string) {
}
}
// Reset last seen timestamps.
// If we're the leader we track everyone, and don't reset.
// If we are (or were) the leader we track(ed) everyone, and don't reset.
// But if we're a follower we only track the leader, and reset all others.
if newLeader != n.id {
if newLeader != n.id && !wasLeader {
for peer, ps := range n.peers {
if peer == newLeader {
continue
Expand Down
61 changes: 61 additions & 0 deletions server/raft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3803,6 +3803,67 @@ func TestNRGSendSnapshotInstallsSnapshot(t *testing.T) {
require_True(t, n.DrainAndReplaySnapshot())
}

func TestNRGQuorumAfterLeaderStepdown(t *testing.T) {
origMinTimeout, origMaxTimeout, origHBInterval := minElectionTimeout, maxElectionTimeout, hbInterval
minElectionTimeout, maxElectionTimeout, hbInterval = minElectionTimeoutDefault, maxElectionTimeoutDefault, hbIntervalDefault
defer func() {
minElectionTimeout, maxElectionTimeout, hbInterval = origMinTimeout, origMaxTimeout, origHBInterval
}()

n, cleanup := initSingleMemRaftNode(t)
defer cleanup()

nats0 := "S1Nunr6R" // "nats-0"
nats1 := "yrzKKRBu" // "nats-1"

// Become leader.
n.switchToCandidate()
n.switchToLeader()

// Should not report having quorum, unless at least 1 peer is seen.
require_False(t, n.Quorum())
n.s.nodeToInfo.Store(nats0, nodeInfo{})
require_NoError(t, n.trackPeer(nats0))
require_True(t, n.Quorum())
n.s.nodeToInfo.Store(nats1, nodeInfo{})
require_NoError(t, n.trackPeer(nats1))
require_True(t, n.Quorum())
require_Len(t, len(n.peers), 3)
for _, ps := range n.peers {
ps.kp = true
}

// If we hand off leadership to another server, we should
// still be reporting we have quorum.
require_NoError(t, n.StepDown())
require_True(t, n.Quorum())
require_Len(t, len(n.peers), 3)
for peer, ps := range n.peers {
if peer == n.id {
continue
}
// All peer timestamps should be preserved.
require_False(t, ps.ts.IsZero())
}

// After our new leader comes online, we should still have quorum,
// but the other follower's timestamp should be cleared.
n.updateLeader(nats0)
require_Equal(t, n.leader, nats0)
require_True(t, n.Quorum())
require_Len(t, len(n.peers), 3)
for peer, ps := range n.peers {
if peer == n.id {
continue
}
if peer == nats0 {
require_False(t, ps.ts.IsZero()) // Leader is preserved.
} else {
require_True(t, ps.ts.IsZero()) // Other follower is cleared.
}
}
}

// This is a RaftChainOfBlocks test where a block is proposed and then we wait for all replicas to apply it before
// proposing the next one.
// The test may fail if:
Expand Down
Loading