diff options
author | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2017-11-29 17:04:40 -0500 |
---|---|---|
committer | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2018-01-12 17:11:54 -0500 |
commit | f496011618ba7b1a99e6d9e51bea6966341cfd12 (patch) | |
tree | be4f55586855c535892f138c24616cf90fe29277 /src/mongo/db | |
parent | 427d09d191ba7fd6187eff01ab522ed5b0714f28 (diff) | |
download | mongo-f496011618ba7b1a99e6d9e51bea6966341cfd12.tar.gz |
SERVER-28290 Stepping down due to a higher term seen in a heartbeat should not discard term after stepdown.
Diffstat (limited to 'src/mongo/db')
4 files changed, 25 insertions, 39 deletions
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index 0d9d3bd4580..179fefa4f19 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -3379,12 +3379,6 @@ EventHandle ReplicationCoordinatorImpl::updateTerm_forTest( EventHandle finishEvh; finishEvh = _updateTerm_inlock(term, updateResult); - if (!finishEvh) { - auto finishEvhStatus = _replExecutor->makeEvent(); - invariantOK(finishEvhStatus.getStatus()); - finishEvh = finishEvhStatus.getValue(); - _replExecutor->signalEvent(finishEvh); - } return finishEvh; } @@ -3443,6 +3437,9 @@ EventHandle ReplicationCoordinatorImpl::_updateTerm_inlock( } if (localUpdateTermResult == TopologyCoordinator::UpdateTermResult::kTriggerStepDown) { + if (!_pendingTermUpdateDuringStepDown || *_pendingTermUpdateDuringStepDown < term) { + _pendingTermUpdateDuringStepDown = term; + } if (_topCoord->prepareForUnconditionalStepDown()) { log() << "stepping down from primary, because a new term has begun: " << term; return _stepDownStart(); diff --git a/src/mongo/db/repl/replication_coordinator_impl.h b/src/mongo/db/repl/replication_coordinator_impl.h index ab3873868e6..aa53524763c 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.h +++ b/src/mongo/db/repl/replication_coordinator_impl.h @@ -386,7 +386,6 @@ public: * Returns event handle that we can use to wait for the operation to complete. * When the operation is complete (waitForEvent() returns), 'updateResult' will be set * to a status telling if the term increased or a stepdown was triggered. - */ executor::TaskExecutor::EventHandle updateTerm_forTest( long long term, TopologyCoordinator::UpdateTermResult* updateResult); @@ -1342,6 +1341,10 @@ private: // This variable must be written immediately after _term, and thus its value can lag. // Reading this value does not require the replication coordinator mutex to be locked. AtomicInt64 _termShadow; // (S) + + // When we decide to step down due to hearing about a higher term, we remember the term we heard + // here so we can update our term to match as part of finishing stepdown. + boost::optional<long long> _pendingTermUpdateDuringStepDown; // (M) }; } // namespace repl diff --git a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp index 8ddb88cb61d..b1397588543 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp @@ -403,6 +403,14 @@ void ReplicationCoordinatorImpl::_stepDownFinish( _topCoord->finishUnconditionalStepDown(); const auto action = _updateMemberStateFromTopologyCoordinator_inlock(opCtx.get()); + if (_pendingTermUpdateDuringStepDown) { + TopologyCoordinator::UpdateTermResult result; + _updateTerm_inlock(*_pendingTermUpdateDuringStepDown, &result); + // We've just stepped down due to the "term", so it's impossible to step down again + // for the same term. + invariant(result != TopologyCoordinator::UpdateTermResult::kTriggerStepDown); + _pendingTermUpdateDuringStepDown = boost::none; + } lk.unlock(); _performPostMemberStateUpdateAction(action); _replExecutor->signalEvent(finishedEvent); diff --git a/src/mongo/db/repl/replication_coordinator_impl_test.cpp b/src/mongo/db/repl/replication_coordinator_impl_test.cpp index 1d4164876f8..1666e8134b1 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_test.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_test.cpp @@ -1467,13 +1467,8 @@ TEST_F(ReplCoordTest, NodeChangesTermAndStepsDownWhenAndOnlyWhenUpdateTermSuppli // higher term, step down and change term executor::TaskExecutor::CallbackHandle cbHandle; ASSERT_EQUALS(ErrorCodes::StaleTerm, getReplCoord()->updateTerm(opCtx.get(), 2).code()); - // Term hasn't been incremented yet, as we need another try to update it after stepdown. - ASSERT_EQUALS(1, getReplCoord()->getTerm()); - ASSERT_TRUE(getReplCoord()->getMemberState().secondary()); - - // Now update term should actually update the term, as stepdown is complete. - ASSERT_EQUALS(ErrorCodes::StaleTerm, getReplCoord()->updateTerm(opCtx.get(), 2).code()); ASSERT_EQUALS(2, getReplCoord()->getTerm()); + ASSERT_TRUE(getReplCoord()->getMemberState().secondary()); } TEST_F(ReplCoordTest, ConcurrentStepDownShouldNotSignalTheSameFinishEventMoreThanOnce) { @@ -1512,41 +1507,24 @@ TEST_F(ReplCoordTest, ConcurrentStepDownShouldNotSignalTheSameFinishEventMoreTha TopologyCoordinator::UpdateTermResult termUpdated2; auto updateTermEvh2 = getReplCoord()->updateTerm_forTest(2, &termUpdated2); + ASSERT(termUpdated2 == TopologyCoordinator::UpdateTermResult::kTriggerStepDown); ASSERT(updateTermEvh2.isValid()); TopologyCoordinator::UpdateTermResult termUpdated3; auto updateTermEvh3 = getReplCoord()->updateTerm_forTest(3, &termUpdated3); - ASSERT(updateTermEvh3.isValid()); + ASSERT(termUpdated3 == TopologyCoordinator::UpdateTermResult::kTriggerStepDown); + // Although term 3 can trigger stepdown, a stepdown has already been scheduled, + // so no other stepdown can be scheduled again. Term 3 will be remembered and + // installed once stepdown finishes. + ASSERT(!updateTermEvh3.isValid()); // Unblock the tasks for updateTerm and _stepDownFinish. globalExclusiveLock.reset(); - // Both _updateTerm_incallback tasks should be scheduled. + // Wait stepdown to finish and term 3 to be installed. replExec->waitForEvent(updateTermEvh2); - ASSERT(termUpdated2 == TopologyCoordinator::UpdateTermResult::kTriggerStepDown); - - replExec->waitForEvent(updateTermEvh3); - if (termUpdated3 == TopologyCoordinator::UpdateTermResult::kTriggerStepDown) { - // Term hasn't updated yet. - ASSERT_EQUALS(1, getReplCoord()->getTerm()); - - // Update term event handles will wait for potential stepdown. - ASSERT_TRUE(getReplCoord()->getMemberState().secondary()); - - TopologyCoordinator::UpdateTermResult termUpdated4; - auto updateTermEvh4 = getReplCoord()->updateTerm_forTest(3, &termUpdated4); - ASSERT(updateTermEvh4.isValid()); - replExec->waitForEvent(updateTermEvh4); - ASSERT(termUpdated4 == TopologyCoordinator::UpdateTermResult::kUpdatedTerm); - ASSERT_EQUALS(3, getReplCoord()->getTerm()); - } else { - // Term already updated. - ASSERT(termUpdated3 == TopologyCoordinator::UpdateTermResult::kUpdatedTerm); - ASSERT_EQUALS(3, getReplCoord()->getTerm()); - - // Update term event handles will wait for potential stepdown. - ASSERT_TRUE(getReplCoord()->getMemberState().secondary()); - } + ASSERT_TRUE(getReplCoord()->getMemberState().secondary()); + ASSERT_EQUALS(3, getReplCoord()->getTerm()); } TEST_F(ReplCoordTest, DrainCompletionMidStepDown) { |