summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2020-11-18 22:10:23 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-19 03:29:03 +0000
commit73ab98a9094de18b82e596e8d1d0bf311858548b (patch)
treeeb359d174b72b8ff8a9233a58ddc9b4d2a09b09d
parent06b3d3fe18b301e04f185d67e862c1d06536312b (diff)
downloadmongo-73ab98a9094de18b82e596e8d1d0bf311858548b.tar.gz
SERVER-52680 Start replication when leaving REMOVED state
-rw-r--r--jstests/replsets/single_node_set_new_hostname.js50
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp16
2 files changed, 63 insertions, 3 deletions
diff --git a/jstests/replsets/single_node_set_new_hostname.js b/jstests/replsets/single_node_set_new_hostname.js
new file mode 100644
index 00000000000..484cf65a1cf
--- /dev/null
+++ b/jstests/replsets/single_node_set_new_hostname.js
@@ -0,0 +1,50 @@
+/*
+ * When a one-node set is restarted on a different port and reconfigured with the new port, it
+ * should re-elect itself.
+ *
+ * @tags: [
+ * requires_persistence,
+ * ]
+ */
+(function() {
+"use strict";
+
+load("jstests/replsets/rslib.js");
+
+const replTest = new ReplSetTest({nodes: 1});
+replTest.startSet();
+replTest.initiate();
+replTest.getPrimary();
+
+/*
+ * Prepare to restart the sole node on a new port, it no longer finds itself in the old config.
+ */
+const config = replTest.getReplSetConfigFromNode(0);
+const newPort = replTest.getPort(0) + 1;
+const hostname = config.members[0].host.split(":")[0];
+const newHostAndPort = `${hostname}:${newPort}`;
+
+jsTestLog("Restarting the sole node on a new port: " + newPort);
+replTest.restart(0, {port: newPort});
+let restartedNode;
+assert.soonNoExcept(() => {
+ restartedNode = new Mongo(newHostAndPort);
+ return true;
+}, `Couldn't connect to restarted node "${newHostAndPort}`);
+waitForState(restartedNode, ReplSetTest.State.REMOVED);
+
+/*
+ * Update the config to match the node's new port.
+ */
+jsTestLog("Reconfiguring the set to change the sole node's port.");
+jsTestLog(`Original config: ${tojson(config)}`);
+config.version++;
+config.members[0].host = newHostAndPort;
+jsTestLog(`New config: ${tojson(config)}`);
+// Force reconfig since the restarted node is in REMOVED state, not PRIMARY.
+assert.commandWorked(
+ restartedNode.getDB("admin").runCommand({replSetReconfig: config, force: true}));
+waitForState(restartedNode, ReplSetTest.State.PRIMARY);
+
+replTest.stopSet();
+}());
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index 2dddbe0dc86..3eb248231b3 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -742,12 +742,11 @@ void ReplicationCoordinatorImpl::_stopDataReplication(OperationContext* opCtx) {
void ReplicationCoordinatorImpl::_startDataReplication(OperationContext* opCtx,
std::function<void()> startCompleted) {
- if (_startedSteadyStateReplication.load()) {
+ if (_startedSteadyStateReplication.swap(true)) {
+ // This is not the first call.
return;
}
- _startedSteadyStateReplication.store(true);
-
// Check to see if we need to do an initial sync.
const auto lastOpTime = getMyLastAppliedOpTime();
const auto needsInitialSync =
@@ -4112,6 +4111,17 @@ ReplicationCoordinatorImpl::_updateMemberStateFromTopologyCoordinator(WithLock l
_cancelPriorityTakeover_inlock();
}
+ // Ensure replication is running if we are no longer REMOVED.
+ if (_memberState.removed() && !newState.arbiter()) {
+ LOGV2(5268000, "Scheduling a task to begin or continue replication");
+ _scheduleWorkAt(_replExecutor->now(),
+ [=](const mongo::executor::TaskExecutor::CallbackArgs& cbData) {
+ _externalState->startThreads();
+ auto opCtx = cc().makeOperationContext();
+ _startDataReplication(opCtx.get());
+ });
+ }
+
LOGV2(21358,
"transition to {newState} from {oldState}",
"Replica set state transition",