diff options
author | William Schultz <william.schultz@mongodb.com> | 2020-04-10 17:01:34 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-04-10 21:13:58 +0000 |
commit | 880dd3e91f5b38105d909072d8d036378575d93d (patch) | |
tree | 7aec1aff2dc2be59b72659e7794d365ad9b6a564 | |
parent | d71b8cbccdd8bb2cf547989c630229d4c73b2856 (diff) | |
download | mongo-880dd3e91f5b38105d909072d8d036378575d93d.tar.gz |
SERVER-47189 Do a reconfig on upgrade to FCV 4.4 to add a config term
3 files changed, 101 insertions, 1 deletions
diff --git a/jstests/multiVersion/add_config_term_on_replset_upgrade.js b/jstests/multiVersion/add_config_term_on_replset_upgrade.js new file mode 100644 index 00000000000..6ea1cb4b498 --- /dev/null +++ b/jstests/multiVersion/add_config_term_on_replset_upgrade.js @@ -0,0 +1,66 @@ +/** + * Test the upgrade of a replica set from last-stable to latest version succeeds and adds the "term" + * field of the config document. + */ + +(function() { +'use strict'; + +load('jstests/multiVersion/libs/multi_rs.js'); +load('jstests/libs/test_background_ops.js'); + +const newVersion = "latest"; +const oldVersion = "last-stable"; + +let nodes = { + n1: {binVersion: oldVersion}, + n2: {binVersion: oldVersion}, + n3: {binVersion: oldVersion} +}; +let replicaSetName = 'add_config_term_on_replset_upgrade'; +let rst = new ReplSetTest({name: replicaSetName, nodes: nodes}); +let nodenames = rst.nodeList(); +rst.startSet(); +rst.initiate({ + "_id": replicaSetName, + "members": [ + {"_id": 0, "host": nodenames[0]}, + {"_id": 1, "host": nodenames[1]}, + {"_id": 2, "host": nodenames[2]} + ] +}); + +// Upgrade the set. +jsTest.log("Upgrading replica set.."); +rst.upgradeSet({binVersion: newVersion}); +jsTest.log("Upgrade complete."); + +let config = rst.getPrimary().getDB("local").getCollection("system.replset").findOne(); +assert(!config.hasOwnProperty("term")); + +let configInOldVersion = rst.getReplSetConfigFromNode(); + +jsTest.log("Upgrading FCV to 4.4"); +let primary = rst.getPrimary(); +assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: latestFCV})); + +// Check that the term field exists in the config document on all nodes. The term of the config +// should match the node's current term. +rst.nodes.forEach(function(node) { + reconnect(node); + jsTestLog("Checking the config term on node " + tojson(node.host) + " after binary upgrade."); + let config = node.getDB("local").getCollection("system.replset").findOne(); + let currentTerm = node.adminCommand({replSetGetStatus: 1}).term; + // Config should have a term field that matches the node's current term. + assert(config.hasOwnProperty("term"), tojson(config)); + assert.eq(config.term, currentTerm, tojson(config)); + // The configs can only differ in config versions and terms. Versions differ because of the + // force reconfig on upgrade. + configInOldVersion.term = config.term; + assert.eq(config.version, configInOldVersion.version + 1); + config.version = configInOldVersion.version; + assert.docEq(configInOldVersion, config); +}); + +rst.stopSet(); +})(); diff --git a/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js b/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js index 02e6fbb9d40..9f820eb407b 100644 --- a/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js +++ b/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js @@ -221,7 +221,7 @@ secondaryAdminDB = secondary.getDB("admin"); // Rig the election so that the first node running latest version remains the primary after the // 'lastStable' secondary is added to the replica set. replSetConfig = rst.getReplSetConfig(); -replSetConfig.version = 4; +replSetConfig.version = rst.getReplSetConfigFromNode().version + 1; replSetConfig.members[2].priority = 0; reconfig(rst, replSetConfig); diff --git a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp index df49d4c6c57..3ebd8e2def3 100644 --- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp +++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp @@ -206,6 +206,40 @@ public: FeatureCompatibilityVersion::setTargetUpgrade(opCtx); + // Force reconfig to add a 'term' field to the config. + auto replCoord = repl::ReplicationCoordinator::get(opCtx); + const bool isReplSet = + replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet; + if (isReplSet) { + auto getNewConfig = [&](const repl::ReplSetConfig& oldConfig, long long term) { + auto newConfig = oldConfig; + newConfig.setConfigTerm(term); + newConfig.setConfigVersion(newConfig.getConfigVersion() + 1); + return newConfig; + }; + + // "force" reconfig in order to skip safety checks. This is safe since the content + // of config is the same. + LOGV2(4718900, "Upgrading replica set config."); + auto status = replCoord->doReplSetReconfig(opCtx, getNewConfig, true /* force */); + uassertStatusOKWithContext(status, "Failed to upgrade the replica set config"); + + LOGV2(4718901, + "Waiting for the upgraded replica set config to propagate to a majority"); + // If a write concern is given, we'll use its wTimeout. It's kNoTimeout by default. + WriteConcernOptions writeConcern( + repl::ReplSetConfig::kConfigMajorityWriteConcernModeName, + WriteConcernOptions::SyncMode::NONE, + opCtx->getWriteConcern().wTimeout); + writeConcern.checkCondition = WriteConcernOptions::CheckCondition::Config; + repl::OpTime fakeOpTime(Timestamp(1, 1), replCoord->getTerm()); + uassertStatusOKWithContext( + replCoord->awaitReplication(opCtx, fakeOpTime, writeConcern).status, + "Failed to wait for the upgraded replica set config to propagate to a " + "majority"); + LOGV2(4718902, "The upgraded replica set config has been propagated to a majority"); + } + { // Take the global lock in S mode to create a barrier for operations taking the // global IX or X locks. This ensures that either |