summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSiyuan Zhou <siyuan.zhou@mongodb.com>2017-11-29 17:04:40 -0500
committerSiyuan Zhou <siyuan.zhou@mongodb.com>2018-01-12 17:11:54 -0500
commitf496011618ba7b1a99e6d9e51bea6966341cfd12 (patch)
treebe4f55586855c535892f138c24616cf90fe29277 /src
parent427d09d191ba7fd6187eff01ab522ed5b0714f28 (diff)
downloadmongo-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')
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp9
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.h5
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp8
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_test.cpp42
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) {