@@ -430,7 +430,7 @@ func TestNRGInvalidTAVDoesntPanic(t *testing.T) {
430
430
c .waitOnAllCurrent ()
431
431
}
432
432
433
- func TestNRGCandidateStepsDownAfterAE (t * testing.T ) {
433
+ func TestNRGAssumeHighTermAfterCandidateIsolation (t * testing.T ) {
434
434
c := createJetStreamClusterExplicit (t , "R3S" , 3 )
435
435
defer c .shutdown ()
436
436
c .waitOnLeader ()
@@ -441,32 +441,38 @@ func TestNRGCandidateStepsDownAfterAE(t *testing.T) {
441
441
rg := c .createRaftGroup ("TEST" , 3 , newStateAdder )
442
442
rg .waitOnLeader ()
443
443
444
- // Pick a random follower node. Bump the term up by a considerable
444
+ // Bump the term up on one of the follower nodes by a considerable
445
445
// amount and force it into the candidate state. This is what happens
446
446
// after a period of time in isolation.
447
- n := rg .nonLeader ().node ().(* raft )
448
- n .Lock ()
449
- n .term += 100
450
- n .switchState (Candidate )
451
- n .Unlock ()
447
+ follower := rg .nonLeader ().node ().(* raft )
448
+ follower .Lock ()
449
+ follower .term += 100
450
+ follower .switchState (Candidate )
451
+ follower .Unlock ()
452
+
453
+ follower .requestVote ()
454
+ time .Sleep (time .Millisecond * 100 )
455
+
456
+ // The candidate will shortly send a vote request. When that happens,
457
+ // the rest of the nodes in the cluster should move up to that term,
458
+ // even though they will not grant the vote.
459
+ nterm := follower .term
460
+ for _ , n := range rg {
461
+ require_Equal (t , n .node ().Term (), nterm )
462
+ }
452
463
453
- // Have the leader push through something on the current term just
454
- // for good measure, although the heartbeats probably work too.
464
+ // Have the leader send out a proposal, which will force the candidate
465
+ // back into follower state.
466
+ rg .waitOnLeader ()
455
467
rg .leader ().(* stateAdder ).proposeDelta (1 )
468
+ rg .waitOnTotal (t , 1 )
456
469
457
- // Wait for the leader to receive the next append entry from the
458
- // current leader. What should happen is that the node steps down
459
- // and starts following the leader, as nothing in the log of the
460
- // follower is newer than the term of the leader.
461
- checkFor (t , time .Second , 50 * time .Millisecond , func () error {
462
- if n .State () == Candidate {
463
- return fmt .Errorf ("shouldn't still be candidate state" )
464
- }
465
- if nterm , lterm := n .Term (), rg .leader ().node ().Term (); nterm != lterm {
466
- return fmt .Errorf ("follower term %d should match leader term %d" , nterm , lterm )
467
- }
468
- return nil
469
- })
470
+ // The candidate should have switched to a follower on a term equal to
471
+ // or newer than the candidate had.
472
+ for _ , n := range rg {
473
+ require_NotEqual (t , n .node ().State (), Candidate )
474
+ require_True (t , n .node ().Term () >= nterm )
475
+ }
470
476
}
471
477
472
478
// Test to make sure this does not cause us to truncate our wal or enter catchup state.
@@ -588,3 +594,42 @@ func TestNRGLeavesObserverAfterPause(t *testing.T) {
588
594
n .ResumeApply ()
589
595
checkState (false , false )
590
596
}
597
+
598
+ func TestNRGCandidateDoesntRevertTermAfterOldAE (t * testing.T ) {
599
+ c := createJetStreamClusterExplicit (t , "R3S" , 3 )
600
+ defer c .shutdown ()
601
+
602
+ rg := c .createMemRaftGroup ("TEST" , 3 , newStateAdder )
603
+ rg .waitOnLeader ()
604
+
605
+ // Bump the term up a few times.
606
+ for i := 0 ; i < 3 ; i ++ {
607
+ rg .leader ().node ().StepDown ()
608
+ time .Sleep (time .Millisecond * 50 ) // Needed because stepdowns not synchronous
609
+ rg .waitOnLeader ()
610
+ }
611
+
612
+ leader := rg .leader ().node ().(* raft )
613
+ follower := rg .nonLeader ().node ().(* raft )
614
+
615
+ // Sanity check that we are where we expect to be.
616
+ require_Equal (t , leader .term , 4 )
617
+ require_Equal (t , follower .term , 4 )
618
+
619
+ // At this point the active term is 4 and pterm is 4, force the
620
+ // term up to 9. This won't bump the pterm.
621
+ rg .lockAll ()
622
+ for _ , n := range rg {
623
+ n .node ().(* raft ).term += 5
624
+ }
625
+ rg .unlockAll ()
626
+
627
+ // Build an AE that has a term newer than the pterm but older than
628
+ // the term. Give it to the follower in candidate state.
629
+ ae := newAppendEntry (leader .id , 6 , leader .commit , leader .pterm , leader .pindex , nil )
630
+ follower .switchToCandidate ()
631
+ follower .processAppendEntry (ae , nil )
632
+
633
+ // The candidate must not have reverted back to term 6.
634
+ require_NotEqual (t , follower .term , 6 )
635
+ }
0 commit comments