From aa2faa36250d818ae54287323ec5d72cfa00b7ef Mon Sep 17 00:00:00 2001 From: Jason Chan Date: Wed, 9 Sep 2020 19:50:15 +0000 Subject: SERVER-50706 Add fromConfigServer parameter to allow transitioning a shard server from lastLTS FCV to lastContinuousFCV (cherry picked from commit 3a6bde797e31d9e06e982aa20b89a856cfeef95c) (cherry picked from commit c8a178cbacb46b3d44cc0a4bb8ba77645bc42c6b) --- buildscripts/linter/simplecpplint.py | 2 + .../set_feature_compatibility_version.js | 85 ++++++++++++++--- .../noPassthrough/feature_compatibility_version.js | 18 +++- .../fcv_changes_close_connections.js | 4 + jstests/replsets/awaitable_ismaster_fcv_change.js | 87 +++++++++++++++++- .../db/commands/feature_compatibility_version.cpp | 101 ++++++++++++++------- .../db/commands/feature_compatibility_version.h | 7 +- .../feature_compatibility_version_parser.cpp | 61 +++++++------ .../feature_compatibility_version_parser.h | 55 +++++++---- .../commands/set_feature_compatibility_version.idl | 5 + .../set_feature_compatibility_version_command.cpp | 67 +++++++++----- src/mongo/db/fcv_op_observer.cpp | 12 ++- src/mongo/db/s/SConscript | 1 + .../sharding_catalog_manager_add_shard_test.cpp | 12 ++- .../sharding_catalog_manager_shard_operations.cpp | 91 +++++++++++-------- src/mongo/db/server_options.h | 15 ++- 16 files changed, 452 insertions(+), 171 deletions(-) diff --git a/buildscripts/linter/simplecpplint.py b/buildscripts/linter/simplecpplint.py index 5ca79216de0..a1cd50f94d3 100644 --- a/buildscripts/linter/simplecpplint.py +++ b/buildscripts/linter/simplecpplint.py @@ -69,6 +69,8 @@ GENERIC_FCV = [ r'::kDowngradingFromLatestToLastLTS', r'::kDowngradingFromLatestToLastContinuous', r'\.isUpgradingOrDowngrading', + r'::kDowngradingFromLatestToLastContinuous', + r'::kUpgradingFromLastLTSToLastContinuous', ] _RE_GENERIC_FCV_REF = re.compile(r'(' + '|'.join(GENERIC_FCV) + r')\b') diff --git a/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js b/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js index 75ef8ad1841..badce28a6b2 100644 --- a/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js +++ b/jstests/multiVersion/genericSetFCVUsage/set_feature_compatibility_version.js @@ -69,8 +69,8 @@ function runStandaloneTest(downgradeVersion) { {setFeatureCompatibilityVersion: downgradeFCV, downgradeOnDiskChanges: false}), ErrorCodes.IllegalOperation); } else { - jsTestLog( - "Test that setFeatureCompatibilityVersion succeeds with downgradeOnDiskChanges parameter when FCV is last-continuous"); + jsTestLog("Test that setFeatureCompatibilityVersion succeeds with downgradeOnDiskChanges " + + "parameter when FCV is last-continuous"); assert.commandWorked(adminDB.runCommand( {setFeatureCompatibilityVersion: downgradeFCV, downgradeOnDiskChanges: true})); checkFCV(adminDB, downgradeFCV); @@ -132,18 +132,38 @@ function runStandaloneTest(downgradeVersion) { checkFCV(adminDB, downgradeFCV); // setFeatureCompatibilityVersion does not support upgrading/downgrading between last-lts and - // last-continuous FCV. + // last-continuous FCV by default. Upgrading from last-lts to last-continuous is allowed if + // fromConfigServer is set to true. if (lastContinuousFCV !== lastLTSFCV) { if (downgradeFCV === lastContinuousFCV) { // Attempt to downgrade FCV from last-continuous to last-lts. assert.commandFailedWithCode( adminDB.runCommand({setFeatureCompatibilityVersion: lastLTSFCV}), ErrorCodes.IllegalOperation); + // Downgrading from last-continuous to last-lts is not allowed even with + // fromConfigServer: true. + assert.commandFailedWithCode( + adminDB.runCommand( + {setFeatureCompatibilityVersion: lastLTSFCV, fromConfigServer: true}), + ErrorCodes.IllegalOperation); } else { // Attempt to upgrade FCV from last-lts to last-continuous. assert.commandFailedWithCode( - adminDB.runCommand({setFeatureCompatibilityVersion: lastContinuousFCV}), - ErrorCodes.IllegalOperation); + adminDB.runCommand({setFeatureCompatibilityVersion: lastContinuousFCV}), 5070603); + + assert.commandFailedWithCode( + adminDB.runCommand( + {setFeatureCompatibilityVersion: lastContinuousFCV, fromConfigServer: false}), + 5070603); + assert.commandWorked(adminDB.runCommand( + {setFeatureCompatibilityVersion: lastContinuousFCV, fromConfigServer: true})); + checkFCV(adminDB, lastContinuousFCV); + + // Reset the FCV back to last-lts. + assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: latestFCV})); + checkFCV(adminDB, latestFCV); + assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + checkFCV(adminDB, lastLTSFCV); } } @@ -210,12 +230,12 @@ function runStandaloneTest(downgradeVersion) { MongoRunner.stopMongod(conn); // A 'latest' binary mongod started with --shardsvr and clean data files defaults to - // 'downgradeFCV'. + // lastLTSFCV. conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: latest, shardsvr: ""}); assert.neq( null, conn, "mongod was unable to start up with version=" + latest + " and no data files"); adminDB = conn.getDB("admin"); - checkFCV(adminDB, downgradeFCV); + checkFCV(adminDB, lastLTSFCV); MongoRunner.stopMongod(conn); } @@ -292,6 +312,16 @@ function runReplicaSetTest(downgradeVersion) { primary.adminCommand({setFeatureCompatibilityVersion: downgradeFCV}), ErrorCodes.IllegalOperation); + if (downgradeFCV === lastLTSFCV && lastLTSFCV !== lastContinuousFCV) { + // Upgrading to last-continuous should fail if we are in the middle of upgrading to latest. + assert.commandFailedWithCode( + primary.adminCommand({setFeatureCompatibilityVersion: lastContinuousFCV}), 5070602); + assert.commandFailedWithCode( + primary.adminCommand( + {setFeatureCompatibilityVersion: lastContinuousFCV, fromConfigServer: true}), + 5070602); + } + // Because the failed upgrade command left the primary in an intermediary state, complete the // upgrade. assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: latestFCV})); @@ -319,6 +349,32 @@ function runReplicaSetTest(downgradeVersion) { // Complete the downgrade. assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: downgradeFCV})); + if (downgradeFCV === lastLTSFCV && lastContinuousFCV !== lastLTSFCV) { + // The command should fail because wtimeout expires before a majority responds. + stopServerReplication(secondary); + res = primary.adminCommand({ + setFeatureCompatibilityVersion: lastContinuousFCV, + fromConfigServer: true, + writeConcern: {wtimeout: 1000} + }); + assert.eq(0, res.ok); + assert.commandFailedWithCode(res, ErrorCodes.WriteConcernFailed); + restartServerReplication(secondary); + + // Upgrading the FCV to latest should fail if a previous upgrade to lastContinuous has not + // yet completed. + assert.commandFailedWithCode( + primary.adminCommand({setFeatureCompatibilityVersion: latestFCV}), 5070602); + + // Complete the upgrade to last-continuous. + assert.commandWorked(primary.adminCommand( + {setFeatureCompatibilityVersion: lastContinuousFCV, fromConfigServer: true})); + + // Reset the FCV back to last-lts. + assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: latestFCV})); + assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + } + secondary = rst.add({binVersion: downgradeVersion}); secondaryAdminDB = secondary.getDB("admin"); @@ -439,7 +495,7 @@ function runShardingTest(downgradeVersion) { checkFCV(configPrimaryAdminDB, downgradeFCV); checkFCV(shardPrimaryAdminDB, downgradeFCV); - // A 'latest' binary replica set started as a shard server defaults to 'downgradeFCV'. + // A 'latest' binary replica set started as a shard server defaults to 'lastLTSFCV'. let latestShard = new ReplSetTest({ name: "latestShard", nodes: [{binVersion: latest}, {binVersion: latest}], @@ -449,7 +505,7 @@ function runShardingTest(downgradeVersion) { latestShard.startSet(); latestShard.initiate(); let latestShardPrimaryAdminDB = latestShard.getPrimary().getDB("admin"); - checkFCV(latestShardPrimaryAdminDB, downgradeFCV); + checkFCV(latestShardPrimaryAdminDB, lastLTSFCV); assert.commandWorked(mongosAdminDB.runCommand({addShard: latestShard.getURL()})); checkFCV(latestShardPrimaryAdminDB, downgradeFCV); @@ -489,12 +545,13 @@ function runShardingTest(downgradeVersion) { downgradedShard.stopSet(); } -runStandaloneTest('last-continuous'); runStandaloneTest('last-lts'); - -runReplicaSetTest('last-continuous'); runReplicaSetTest('last-lts'); - -runShardingTest('last-continuous'); runShardingTest('last-lts'); + +if (lastLTSFCV != lastContinuousFCV) { + runStandaloneTest('last-continuous'); + runReplicaSetTest('last-continuous'); + runShardingTest('last-continuous'); +} })(); diff --git a/jstests/noPassthrough/feature_compatibility_version.js b/jstests/noPassthrough/feature_compatibility_version.js index fa02e5eaada..2ee5627d2ed 100644 --- a/jstests/noPassthrough/feature_compatibility_version.js +++ b/jstests/noPassthrough/feature_compatibility_version.js @@ -20,7 +20,7 @@ for (let oldVersion of [lastLTSFCV, lastContinuousFCV]) { {$set: {version: oldVersion}})); checkFCV(adminDB, oldVersion); - // Upgrading to lastest. + // Upgrading to latest. assert.commandWorked( adminDB.system.version.update({_id: "featureCompatibilityVersion"}, {$set: {version: oldVersion, targetVersion: latestFCV}})); @@ -51,6 +51,22 @@ for (let oldVersion of [lastLTSFCV, lastContinuousFCV]) { checkFCV(adminDB, latestFCV); } +if (lastLTSFCV !== lastContinuousFCV) { + // Test that we can update from last-lts to last-continuous when the two versions are not equal. + // This upgrade path is exposed to users through the setFeatureCompatibilityVersion command with + // fromConfigServer: true. + assert.commandWorked(adminDB.system.version.update( + {_id: "featureCompatibilityVersion"}, + {$set: {version: lastLTSFCV, targetVersion: lastContinuousFCV}})); + checkFCV(adminDB, lastLTSFCV, lastContinuousFCV); + + // Reset to latestFCV. + assert.commandWorked(adminDB.system.version.update( + {_id: "featureCompatibilityVersion"}, + {$set: {version: latestFCV}, $unset: {targetVersion: true, previousVersion: true}})); + checkFCV(adminDB, latestFCV); +} + // Updating the featureCompatibilityVersion document with an invalid version fails. assert.writeErrorWithCode( adminDB.system.version.update({_id: "featureCompatibilityVersion"}, {$set: {version: "3.2"}}), diff --git a/jstests/noPassthroughWithMongod/fcv_changes_close_connections.js b/jstests/noPassthroughWithMongod/fcv_changes_close_connections.js index 552f13dcc20..f78d3fcf41e 100644 --- a/jstests/noPassthroughWithMongod/fcv_changes_close_connections.js +++ b/jstests/noPassthroughWithMongod/fcv_changes_close_connections.js @@ -98,6 +98,10 @@ function runTest(oldVersion) { // 'last-lts'. if (lastContinuousFCV !== lastLTSFCV) { runTest(lastContinuousFCV); + + // Upgrading from last-lts to last-continuous. This FCV transition is allowed through the + // setFeatureCompatibilityVersion command with fromConfigServer: true. + testFCVChange({fcvDoc: {version: lastLTSFCV, targetVersion: lastContinuousFCV}}); } // Test upgrade/downgrade between 'latest' and 'last-lts'. diff --git a/jstests/replsets/awaitable_ismaster_fcv_change.js b/jstests/replsets/awaitable_ismaster_fcv_change.js index 41ed644e5e6..3a99851bba7 100644 --- a/jstests/replsets/awaitable_ismaster_fcv_change.js +++ b/jstests/replsets/awaitable_ismaster_fcv_change.js @@ -165,17 +165,94 @@ function runTest(downgradeFCV) { assert.eq(0, numAwaitingTopologyChangeOnSecondary); // Get the new topologyVersion. - const primaryResponseAfterDowngrade = isMasterAsInternalClient(); + let primaryResponseAfterDowngrade = isMasterAsInternalClient(); assert(primaryResponseAfterDowngrade.hasOwnProperty("topologyVersion"), tojson(primaryResponseAfterDowngrade)); - const primaryTopologyVersionAfterDowngrade = primaryResponseAfterDowngrade.topologyVersion; - const minWireVersionAfterDowngrade = primaryResponseAfterDowngrade.minWireVersion; + let primaryTopologyVersionAfterDowngrade = primaryResponseAfterDowngrade.topologyVersion; + let minWireVersionAfterDowngrade = primaryResponseAfterDowngrade.minWireVersion; - const secondaryResponseAfterDowngrade = + let secondaryResponseAfterDowngrade = assert.commandWorked(secondaryAdminDB.runCommand({isMaster: 1})); assert(secondaryResponseAfterDowngrade.hasOwnProperty("topologyVersion"), tojson(secondaryResponseAfterDowngrade)); - const secondaryTopologyVersionAfterDowngrade = secondaryResponseAfterDowngrade.topologyVersion; + let secondaryTopologyVersionAfterDowngrade = secondaryResponseAfterDowngrade.topologyVersion; + + if (downgradeFCV === lastLTSFCV && lastLTSFCV !== lastContinuousFCV) { + // Test upgrading from last-lts to last-continuous FCV. We allow this upgrade path via the + // setFeatureCompatibilityVersion command with fromConfigServer: true. + + // Reconfigure the failpoint to refresh the number of times the failpoint has been entered. + primaryFailPoint = configureFailPoint(primary, "waitForIsMasterResponse"); + secondaryFailPoint = configureFailPoint(secondary, "waitForIsMasterResponse"); + let awaitIsMasterBeforeUpgradeOnPrimary = + startParallelShell(funWithArgs(runAwaitableIsMasterBeforeFCVChange, + primaryTopologyVersionAfterDowngrade, + lastContinuousFCV, + true /* isPrimary */, + minWireVersionAfterDowngrade, + maxWireVersion), + primary.port); + let awaitIsMasterBeforeUpgradeOnSecondary = + startParallelShell(funWithArgs(runAwaitableIsMasterBeforeFCVChange, + secondaryTopologyVersionAfterDowngrade, + lastContinuousFCV, + false /* isPrimary */, + minWireVersionAfterDowngrade, + maxWireVersion), + secondary.port); + primaryFailPoint.wait(); + secondaryFailPoint.wait(); + + // Each node has one isMaster request waiting on a topology change. + numAwaitingTopologyChangeOnPrimary = + primaryAdminDB.serverStatus().connections.awaitingTopologyChanges; + numAwaitingTopologyChangeOnSecondary = + secondaryAdminDB.serverStatus().connections.awaitingTopologyChanges; + assert.eq(1, numAwaitingTopologyChangeOnPrimary); + assert.eq(1, numAwaitingTopologyChangeOnSecondary); + + // Upgrade the FCV to last-continuous. + assert.commandWorked(primaryAdminDB.runCommand( + {setFeatureCompatibilityVersion: lastContinuousFCV, fromConfigServer: true})); + awaitIsMasterBeforeUpgradeOnPrimary(); + awaitIsMasterBeforeUpgradeOnSecondary(); + + // Ensure the featureCompatibilityVersion document update has been replicated. + rst.awaitReplication(); + checkFCV(primaryAdminDB, lastContinuousFCV); + checkFCV(secondaryAdminDB, lastContinuousFCV); + + // All isMaster requests should have been responded to after the FCV change. + numAwaitingTopologyChangeOnPrimary = + primaryAdminDB.serverStatus().connections.awaitingTopologyChanges; + numAwaitingTopologyChangeOnSecondary = + secondaryAdminDB.serverStatus().connections.awaitingTopologyChanges; + assert.eq(0, numAwaitingTopologyChangeOnPrimary); + assert.eq(0, numAwaitingTopologyChangeOnSecondary); + + // Reset the FCV back to last-lts and the get the new isMaster parameters. + // We must upgrade to latestFCV first since downgrading from last-continuous to last-stable + // is forbidden. + assert.commandWorked( + primaryAdminDB.runCommand({setFeatureCompatibilityVersion: latestFCV})); + assert.commandWorked( + primaryAdminDB.runCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + rst.awaitReplication(); + checkFCV(primaryAdminDB, lastLTSFCV); + checkFCV(secondaryAdminDB, lastLTSFCV); + + primaryResponseAfterDowngrade = isMasterAsInternalClient(); + assert(primaryResponseAfterDowngrade.hasOwnProperty("topologyVersion"), + tojson(primaryResponseAfterDowngrade)); + primaryTopologyVersionAfterDowngrade = primaryResponseAfterDowngrade.topologyVersion; + minWireVersionAfterDowngrade = primaryResponseAfterDowngrade.minWireVersion; + + secondaryResponseAfterDowngrade = + assert.commandWorked(secondaryAdminDB.runCommand({isMaster: 1})); + assert(secondaryResponseAfterDowngrade.hasOwnProperty("topologyVersion"), + tojson(secondaryResponseAfterDowngrade)); + secondaryTopologyVersionAfterDowngrade = secondaryResponseAfterDowngrade.topologyVersion; + } // Reconfigure the failpoint to refresh the number of times the failpoint has been entered. primaryFailPoint = configureFailPoint(primary, "waitForIsMasterResponse"); diff --git a/src/mongo/db/commands/feature_compatibility_version.cpp b/src/mongo/db/commands/feature_compatibility_version.cpp index 4e4ddb99af7..ce27ffd1ae1 100644 --- a/src/mongo/db/commands/feature_compatibility_version.cpp +++ b/src/mongo/db/commands/feature_compatibility_version.cpp @@ -118,17 +118,64 @@ void runUpdateCommand(OperationContext* opCtx, const FeatureCompatibilityVersion client.runCommand(nss.db().toString(), updateCmd.obj(), updateResult); uassertStatusOK(getStatusFromWriteCommandReply(updateResult)); } + +/** + * Returns the expected value of the 'targetVersion' field in the FCV document based on the + * in-memory FCV value. Returns boost::none if current FCV is not currently upgrading or + * downgrading. + */ +boost::optional getFcvDocTargetVersionField() { + if (!serverGlobalParams.featureCompatibility.isUpgradingOrDowngrading()) { + return boost::none; + } + const auto currentFcv = serverGlobalParams.featureCompatibility.getVersion(); + if (currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest || + currentFcv == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest) { + return FeatureCompatibilityParams::kLatest; + } else if (currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous || + currentFcv == FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous) { + return FeatureCompatibilityParams::kLastContinuous; + } else { + invariant(currentFcv == FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS); + return FeatureCompatibilityParams::kLastLTS; + } +} + +/** + * Returns the expected value of the 'version' field in the FCV document based on the in-memory FCV + * value. + */ +FeatureCompatibilityParams::Version getFcvDocVersionField() { + if (!serverGlobalParams.featureCompatibility.isUpgradingOrDowngrading()) { + return serverGlobalParams.featureCompatibility.getVersion(); + } + const auto currentFcv = serverGlobalParams.featureCompatibility.getVersion(); + if (currentFcv == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest || + currentFcv == FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous) { + return FeatureCompatibilityParams::kLastContinuous; + } else { + invariant(currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous || + currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest || + currentFcv == FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS); + return FeatureCompatibilityParams::kLastLTS; + } +} } // namespace void FeatureCompatibilityVersion::setTargetUpgradeFrom( - OperationContext* opCtx, FeatureCompatibilityParams::Version fromVersion) { + OperationContext* opCtx, + FeatureCompatibilityParams::Version fromVersion, + FeatureCompatibilityParams::Version newVersion) { + invariant(fromVersion < newVersion); + FeatureCompatibilityParams::Version version; // It is possible that we did not fully complete a previous upgrade. In that case, we // must set the source version to be the fully downgraded version as the FCV document // serializer does not recognize upgrading/downgrading states. if (fromVersion == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest) { version = FeatureCompatibilityParams::kLastContinuous; - } else if (fromVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest) { + } else if (fromVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest || + fromVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous) { version = FeatureCompatibilityParams::kLastLTS; } else { version = fromVersion; @@ -136,9 +183,9 @@ void FeatureCompatibilityVersion::setTargetUpgradeFrom( // Sets both 'version' and 'targetVersion' fields. FeatureCompatibilityVersionDocument fcvDoc; fcvDoc.setVersion(version); - fcvDoc.setTargetVersion(FeatureCompatibilityParams::kLatest); + fcvDoc.setTargetVersion(newVersion); runUpdateCommand(opCtx, fcvDoc); -} +} // namespace mongo void FeatureCompatibilityVersion::setTargetDowngrade(OperationContext* opCtx, FeatureCompatibilityParams::Version version) { @@ -211,25 +258,24 @@ bool FeatureCompatibilityVersion::isCleanStartUp() { void FeatureCompatibilityVersion::updateMinWireVersion() { WireSpec& wireSpec = WireSpec::instance(); - - if (serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo( - FeatureCompatibilityParams::kLatest) || - serverGlobalParams.featureCompatibility.isUpgradingOrDowngrading()) { - // FCV == kLatest or FCV is upgrading/downgrading. + const auto currentFcv = serverGlobalParams.featureCompatibility.getVersion(); + if (currentFcv == FeatureCompatibilityParams::kLatest || + (serverGlobalParams.featureCompatibility.isUpgradingOrDowngrading() && + currentFcv != FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous)) { + // FCV == kLatest or FCV is upgrading/downgrading to or from kLatest. WireSpec::Specification newSpec = *wireSpec.get(); newSpec.incomingInternalClient.minWireVersion = LATEST_WIRE_VERSION; newSpec.outgoing.minWireVersion = LATEST_WIRE_VERSION; wireSpec.reset(std::move(newSpec)); - } else if (serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo( - FeatureCompatibilityParams::kLastContinuous)) { - // FCV == kLastContinuous + } else if (currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous || + currentFcv == FeatureCompatibilityParams::kLastContinuous) { + // FCV == kLastContinuous or upgrading to kLastContinuous. WireSpec::Specification newSpec = *wireSpec.get(); newSpec.incomingInternalClient.minWireVersion = LAST_CONT_WIRE_VERSION; newSpec.outgoing.minWireVersion = LAST_CONT_WIRE_VERSION; wireSpec.reset(std::move(newSpec)); } else { - // FCV == kLastLTS - invariant(serverGlobalParams.featureCompatibility.isVersionInitialized()); + invariant(currentFcv == FeatureCompatibilityParams::kLastLTS); WireSpec::Specification newSpec = *wireSpec.get(); newSpec.incomingInternalClient.minWireVersion = LAST_LTS_WIRE_VERSION; newSpec.outgoing.minWireVersion = LAST_LTS_WIRE_VERSION; @@ -337,23 +383,16 @@ void FeatureCompatibilityVersionParameter::append(OperationContext* opCtx, FeatureCompatibilityVersionDocument fcvDoc; BSONObjBuilder featureCompatibilityVersionBuilder(b.subobjStart(name)); auto version = serverGlobalParams.featureCompatibility.getVersion(); - switch (version) { - case ServerGlobalParams::FeatureCompatibility::kLatest: - case ServerGlobalParams::FeatureCompatibility::kLastLTS: - fcvDoc.setVersion(version); - break; - case ServerGlobalParams::FeatureCompatibility::kUpgradingFromLastLTSToLatest: - fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS); - fcvDoc.setTargetVersion(ServerGlobalParams::FeatureCompatibility::kLatest); - break; - case ServerGlobalParams::FeatureCompatibility::kDowngradingFromLatestToLastLTS: - fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS); - fcvDoc.setTargetVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS); - fcvDoc.setPreviousVersion(ServerGlobalParams::FeatureCompatibility::kLatest); - break; - case ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault44Behavior: - // getVersion() does not return this value. - MONGO_UNREACHABLE; + if (serverGlobalParams.featureCompatibility.isUpgradingOrDowngrading()) { + fcvDoc.setVersion(getFcvDocVersionField()); + fcvDoc.setTargetVersion(getFcvDocTargetVersionField()); + if (version == FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous || + version == FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS) { + // We only support downgrading from the latest FCV. + fcvDoc.setPreviousVersion(FeatureCompatibilityParams::kLatest); + } + } else { + fcvDoc.setVersion(version); } featureCompatibilityVersionBuilder.appendElements(fcvDoc.toBSON().removeField("_id")); } diff --git a/src/mongo/db/commands/feature_compatibility_version.h b/src/mongo/db/commands/feature_compatibility_version.h index 3f458066614..57f39cd7d60 100644 --- a/src/mongo/db/commands/feature_compatibility_version.h +++ b/src/mongo/db/commands/feature_compatibility_version.h @@ -67,12 +67,13 @@ public: static void fassertInitializedAfterStartup(OperationContext* opCtx); /** - * Records intent to perform a currentVersion -> kLatest upgrade by updating the on-disk + * Records intent to perform a fromVersion -> newVersion upgrade by updating the on-disk * feature compatibility version document to have 'version'=currentVersion, - * 'targetVersion'=kLatest. Should be called before schemas are modified. + * 'targetVersion'=newVersion. Should be called before schemas are modified. */ static void setTargetUpgradeFrom(OperationContext* opCtx, - ServerGlobalParams::FeatureCompatibility::Version fromVersion); + ServerGlobalParams::FeatureCompatibility::Version fromVersion, + ServerGlobalParams::FeatureCompatibility::Version newVersion); /** * Records intent to perform a downgrade from the latest version by updating the on-disk feature diff --git a/src/mongo/db/commands/feature_compatibility_version_parser.cpp b/src/mongo/db/commands/feature_compatibility_version_parser.cpp index 650557ca644..76fa31fa030 100644 --- a/src/mongo/db/commands/feature_compatibility_version_parser.cpp +++ b/src/mongo/db/commands/feature_compatibility_version_parser.cpp @@ -39,22 +39,24 @@ namespace mongo { +using FeatureCompatibilityParams = ServerGlobalParams::FeatureCompatibility; + constexpr StringData FeatureCompatibilityVersionParser::kParameterName; constexpr StringData FeatureCompatibilityVersionParser::kLastLTS; constexpr StringData FeatureCompatibilityVersionParser::kLastContinuous; constexpr StringData FeatureCompatibilityVersionParser::kLatest; -ServerGlobalParams::FeatureCompatibility::Version FeatureCompatibilityVersionParser::parseVersion( +FeatureCompatibilityParams::Version FeatureCompatibilityVersionParser::parseVersion( StringData versionString) { if (versionString == kLastLTS) { - return ServerGlobalParams::FeatureCompatibility::kLastLTS; + return FeatureCompatibilityParams::kLastLTS; } if (versionString == kLastContinuous) { - return ServerGlobalParams::FeatureCompatibility::kLastContinuous; + return FeatureCompatibilityParams::kLastContinuous; } if (versionString == kLatest) { - return ServerGlobalParams::FeatureCompatibility::kLatest; + return FeatureCompatibilityParams::kLatest; } uasserted(4926900, str::stream() << "Invalid value for " << kParameterName << "document in " @@ -66,14 +68,14 @@ ServerGlobalParams::FeatureCompatibility::Version FeatureCompatibilityVersionPar } StringData FeatureCompatibilityVersionParser::serializeVersion( - ServerGlobalParams::FeatureCompatibility::Version version) { - if (version == ServerGlobalParams::FeatureCompatibility::kLastLTS) { + FeatureCompatibilityParams::Version version) { + if (version == FeatureCompatibilityParams::kLastLTS) { return kLastLTS; } - if (version == ServerGlobalParams::FeatureCompatibility::kLastContinuous) { + if (version == FeatureCompatibilityParams::kLastContinuous) { return kLastContinuous; } - if (version == ServerGlobalParams::FeatureCompatibility::kLatest) { + if (version == FeatureCompatibilityParams::kLatest) { return kLatest; } // It is a bug if we hit here. @@ -82,16 +84,16 @@ StringData FeatureCompatibilityVersionParser::serializeVersion( } Status FeatureCompatibilityVersionParser::validatePreviousVersionField( - ServerGlobalParams::FeatureCompatibility::Version version) { - if (version == ServerGlobalParams::FeatureCompatibility::kLatest) { + FeatureCompatibilityParams::Version version) { + if (version == FeatureCompatibilityParams::kLatest) { return Status::OK(); } return Status(ErrorCodes::Error(4926901), "when present, 'previousVersion' field must be the latest binary version"); } -StatusWith -FeatureCompatibilityVersionParser::parse(const BSONObj& featureCompatibilityVersionDoc) { +StatusWith FeatureCompatibilityVersionParser::parse( + const BSONObj& featureCompatibilityVersionDoc) { try { auto fcvDoc = FeatureCompatibilityVersionDocument::parse( IDLParserErrorContext("FeatureCompatibilityVersionParser"), @@ -101,8 +103,8 @@ FeatureCompatibilityVersionParser::parse(const BSONObj& featureCompatibilityVers auto previousVersion = fcvDoc.getPreviousVersion(); // Downgrading FCV. - if ((version == ServerGlobalParams::FeatureCompatibility::kLastLTS || - version == ServerGlobalParams::FeatureCompatibility::kLastContinuous) && + if ((version == FeatureCompatibilityParams::kLastLTS || + version == FeatureCompatibilityParams::kLastContinuous) && version == targetVersion) { // Downgrading FCV must have a "previousVersion" field. if (!previousVersion) { @@ -117,12 +119,11 @@ FeatureCompatibilityVersionParser::parse(const BSONObj& featureCompatibilityVers << feature_compatibility_version_documentation::kCompatibilityLink << "."); } - if (version == ServerGlobalParams::FeatureCompatibility::kLastLTS) { + if (version == FeatureCompatibilityParams::kLastLTS) { // Downgrading to last-lts. - return ServerGlobalParams::FeatureCompatibility::kDowngradingFromLatestToLastLTS; + return FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS; } else { - return ServerGlobalParams::FeatureCompatibility:: - kDowngradingFromLatestToLastContinuous; + return FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous; } } @@ -142,9 +143,9 @@ FeatureCompatibilityVersionParser::parse(const BSONObj& featureCompatibilityVers // Upgrading FCV. if (targetVersion) { - // For upgrading FCV, "targetVersion" must be kLatest and "version" must be - // kLastContinuous or kLastLTS. - if (targetVersion != ServerGlobalParams::FeatureCompatibility::kLatest || + // For upgrading FCV, "targetVersion" must be kLatest or kLastContinuous and "version" + // must be kLastContinuous or kLastLTS. + if (targetVersion == FeatureCompatibilityParams::kLastLTS || version == ServerGlobalParams::FeatureCompatibility::kLatest) { return Status(ErrorCodes::Error(4926904), str::stream() @@ -155,12 +156,20 @@ FeatureCompatibilityVersionParser::parse(const BSONObj& featureCompatibilityVers << "."); } - if (version == ServerGlobalParams::FeatureCompatibility::kLastLTS) { - return ServerGlobalParams::FeatureCompatibility::kUpgradingFromLastLTSToLatest; + if (version == FeatureCompatibilityParams::kLastLTS) { + return targetVersion == FeatureCompatibilityParams::kLastContinuous + ? FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous + : FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest; } else { - invariant(version == ServerGlobalParams::FeatureCompatibility::kLastContinuous); - return ServerGlobalParams::FeatureCompatibility:: - kUpgradingFromLastContinuousToLatest; + uassert(5070601, + str::stream() + << "Invalid " << kParameterName << " document in " + << NamespaceString::kServerConfigurationNamespace.toString() << ": " + << featureCompatibilityVersionDoc << ". See " + << feature_compatibility_version_documentation::kCompatibilityLink + << ".", + version == ServerGlobalParams::FeatureCompatibility::kLastContinuous); + return FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest; } } diff --git a/src/mongo/db/commands/feature_compatibility_version_parser.h b/src/mongo/db/commands/feature_compatibility_version_parser.h index 6c9f9ab4364..d1e90016ab3 100644 --- a/src/mongo/db/commands/feature_compatibility_version_parser.h +++ b/src/mongo/db/commands/feature_compatibility_version_parser.h @@ -32,6 +32,7 @@ #include "mongo/db/server_options.h" namespace mongo { +using FeatureCompatibilityParams = ServerGlobalParams::FeatureCompatibility; /** * Helpers to parse featureCompatibilityVersion document BSON objects into @@ -51,40 +52,56 @@ public: static constexpr StringData kLastLTS = kVersion44; static constexpr StringData kLastContinuous = kVersion44; static constexpr StringData kLatest = kVersion47; + static constexpr StringData kUpgradingFromLastLTSToLatest = kVersionUpgradingFrom44To47; + static constexpr StringData kUpgradingFromLastContinuousToLatest = kVersionUpgradingFrom44To47; + // kVersionUpgradingFromLastLTSToLastContinuous should assigned kVersionUnset when kLastLTS and + // kLastContinuous are equal. + static constexpr StringData kVersionUpgradingFromLastLTSToLastContinuous = kVersionUnset; + static constexpr StringData kDowngradingFromLatestToLastLTS = kVersionDowngradingFrom47To44; + static constexpr StringData kDowngradingFromLatestToLastContinuous = + kVersionDowngradingFrom47To44; - static ServerGlobalParams::FeatureCompatibility::Version parseVersion(StringData versionString); + static FeatureCompatibilityParams::Version parseVersion(StringData versionString); - static StringData serializeVersion(ServerGlobalParams::FeatureCompatibility::Version version); + static StringData serializeVersion(FeatureCompatibilityParams::Version version); - static Status validatePreviousVersionField( - ServerGlobalParams::FeatureCompatibility::Version version); + static Status validatePreviousVersionField(FeatureCompatibilityParams::Version version); /** * Parses the featureCompatibilityVersion document from the server configuration collection * (admin.system.version), and returns the state represented by the combination of the * targetVersion and version. */ - static StatusWith parse( + static StatusWith parse( const BSONObj& featureCompatibilityVersionDoc); /** * Useful for message logging. */ - static StringData toString(ServerGlobalParams::FeatureCompatibility::Version version) { - switch (version) { - case ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault44Behavior: - return kVersionUnset; - case ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo44: - return kVersion44; - case ServerGlobalParams::FeatureCompatibility::Version::kUpgradingFrom44To47: - return kVersionUpgradingFrom44To47; - case ServerGlobalParams::FeatureCompatibility::Version::kDowngradingFrom47To44: - return kVersionDowngradingFrom47To44; - case ServerGlobalParams::FeatureCompatibility::Version::kVersion47: - return kVersion47; - default: - MONGO_UNREACHABLE; + static StringData toString(FeatureCompatibilityParams::Version version) { + if (version == FeatureCompatibilityParams::Version::kUnsetDefault44Behavior) { + return kVersionUnset; + } else if (version == FeatureCompatibilityParams::kLastLTS) { + return kLastLTS; + } else if (version == FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS) { + return kDowngradingFromLatestToLastLTS; + } else if (version == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous) { + // kUpgradingFromLastLTSToLastContinuous is only a valid FCV state when last-continuous + // and last-lts are not equal. Otherwise, it is set to kInvalid. + invariant(version != FeatureCompatibilityParams::Version::kInvalid); + return kVersionUpgradingFromLastLTSToLastContinuous; + } else if (version == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest) { + return kUpgradingFromLastLTSToLatest; + } else if (version == FeatureCompatibilityParams::kLastContinuous) { + return kLastContinuous; + } else if (version == FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous) { + return kDowngradingFromLatestToLastContinuous; + } else if (version == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest) { + return kUpgradingFromLastContinuousToLatest; + } else if (version == FeatureCompatibilityParams::kLatest) { + return kLatest; } + MONGO_UNREACHABLE; } }; diff --git a/src/mongo/db/commands/set_feature_compatibility_version.idl b/src/mongo/db/commands/set_feature_compatibility_version.idl index b1c7df315d7..793fa587098 100644 --- a/src/mongo/db/commands/set_feature_compatibility_version.idl +++ b/src/mongo/db/commands/set_feature_compatibility_version.idl @@ -49,3 +49,8 @@ commands: downgraded featureCompatibilityVersion." type: safeBool optional: true + fromConfigServer: + description: "A boolean that indicates whether the command is being requested by a + config server." + type: safeBool + optional: true 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 48b3c04c5b7..0be6e6b0e5f 100644 --- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp +++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp @@ -135,18 +135,44 @@ Status validateDowngradeRequest(FeatureCompatibilityParams::Version actualVersio } Status validateUpgradeRequest(FeatureCompatibilityParams::Version actualVersion, - FeatureCompatibilityParams::Version requestedVersion) { + FeatureCompatibilityParams::Version requestedVersion, + boost::optional fromConfigServer) { + invariant(actualVersion < requestedVersion); + if (actualVersion == FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS || actualVersion == FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous) { return Status(ErrorCodes::IllegalOperation, str::stream() << "cannot initiate featureCompatibilityVersion upgrade to " - << FCVP::kLatest + << FCVP::toString(requestedVersion) << " while a previous featureCompatibilityVersion downgrade to " << FCVP::kLastLTS << " or " << FCVP::kLastContinuous << " has not completed. Finish downgrade then upgrade to " << FCVP::kLatest); } + if ((actualVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest && + requestedVersion == FeatureCompatibilityParams::kLastContinuous) || + (actualVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous && + requestedVersion == FeatureCompatibilityParams::kLatest)) { + auto incompleteUpgradeVersionString = + actualVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest + ? FCVP::kLatest + : FCVP::kLastContinuous; + return Status(ErrorCodes::Error(5070602), + str::stream() + << "cannot initiate featureCompatibilityVersion upgrade to " + << FCVP::toString(requestedVersion) << " while a previous upgrade to " + << incompleteUpgradeVersionString + << " has not yet completed. Finish upgrade then try again."); + } + + if (requestedVersion == FeatureCompatibilityParams::kLastContinuous && + !fromConfigServer.get_value_or(false)) { + return Status(ErrorCodes::Error(5070603), + str::stream() << "cannot initiate featureCompatibilityVersion upgrade from " + << FCVP::kLastLTS << " to " << FCVP::kLastContinuous << "."); + } + return Status::OK(); } @@ -243,9 +269,9 @@ public: const auto requestedVersionString = FCVP::serializeVersion(requestedVersion); FeatureCompatibilityParams::Version actualVersion = serverGlobalParams.featureCompatibility.getVersion(); - if (request.getDowngradeOnDiskChanges() && - requestedVersion != FeatureCompatibilityParams::kLastContinuous) { + (requestedVersion != FeatureCompatibilityParams::kLastContinuous || + actualVersion < requestedVersion)) { std::stringstream downgradeOnDiskErrorSS; downgradeOnDiskErrorSS << "cannot set featureCompatibilityVersion to " << requestedVersionString @@ -255,18 +281,19 @@ public: uasserted(ErrorCodes::IllegalOperation, downgradeOnDiskErrorSS.str()); } - if (requestedVersion == FeatureCompatibilityParams::kLatest) { - uassertStatusOK(validateUpgradeRequest(actualVersion, requestedVersion)); - if (actualVersion == FeatureCompatibilityParams::kLatest) { - // Set the client's last opTime to the system last opTime so no-ops wait for - // writeConcern. - repl::ReplClientInfo::forClient(opCtx->getClient()) - .setLastOpToSystemLastOpTime(opCtx); - return true; - } + if (actualVersion == requestedVersion) { + // Set the client's last opTime to the system last opTime so no-ops wait for + // writeConcern. + repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx); + return true; + } - FeatureCompatibilityVersion::setTargetUpgradeFrom(opCtx, actualVersion); + if (actualVersion < requestedVersion) { + uassertStatusOK(validateUpgradeRequest( + actualVersion, requestedVersion, request.getFromConfigServer())); + FeatureCompatibilityVersion::setTargetUpgradeFrom( + opCtx, actualVersion, requestedVersion); { // Take the global lock in S mode to create a barrier for operations taking the // global IX or X locks. This ensures that either @@ -298,19 +325,9 @@ public: hangWhileUpgrading.pauseWhileSet(opCtx); FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(opCtx, requestedVersion); - } else if (requestedVersion == FeatureCompatibilityParams::kLastLTS || - requestedVersion == FeatureCompatibilityParams::kLastContinuous) { + } else { uassertStatusOK(validateDowngradeRequest(actualVersion, requestedVersion)); - if (actualVersion == FeatureCompatibilityParams::kLastLTS || - actualVersion == FeatureCompatibilityParams::kLastContinuous) { - // Set the client's last opTime to the system last opTime so no-ops wait for - // writeConcern. - repl::ReplClientInfo::forClient(opCtx->getClient()) - .setLastOpToSystemLastOpTime(opCtx); - return true; - } - auto replCoord = repl::ReplicationCoordinator::get(opCtx); const bool isReplSet = replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet; diff --git a/src/mongo/db/fcv_op_observer.cpp b/src/mongo/db/fcv_op_observer.cpp index bb70616821c..73d8b4b23ee 100644 --- a/src/mongo/db/fcv_op_observer.cpp +++ b/src/mongo/db/fcv_op_observer.cpp @@ -52,6 +52,11 @@ using FeatureCompatibilityParams = ServerGlobalParams::FeatureCompatibility; void FcvOpObserver::_setVersion(OperationContext* opCtx, ServerGlobalParams::FeatureCompatibility::Version newVersion) { + boost::optional prevVersion; + + if (serverGlobalParams.featureCompatibility.isVersionInitialized()) { + prevVersion = serverGlobalParams.featureCompatibility.getVersion(); + } serverGlobalParams.mutableFeatureCompatibility.setVersion(newVersion); FeatureCompatibilityVersion::updateMinWireVersion(); @@ -92,9 +97,12 @@ void FcvOpObserver::_setVersion(OperationContext* opCtx, // (Generic FCV reference): This FCV check should exist across LTS binary versions. const auto shouldIncrementTopologyVersion = newVersion == FeatureCompatibilityParams::kLastLTS || - newVersion == FeatureCompatibilityParams::kLastContinuous || + (prevVersion && + prevVersion.get() == FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous) || newVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest || - newVersion == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest; + newVersion == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest || + newVersion == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous; + if (isReplSet && shouldIncrementTopologyVersion) { replCoordinator->incrementTopologyVersion(); } diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index 4f97f81b966..cf207a09d28 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -531,6 +531,7 @@ env.CppUnitTest( ], LIBDEPS=[ '$BUILD_DIR/mongo/db/auth/authmocks', + '$BUILD_DIR/mongo/db/commands/set_feature_compatibility_version_idl', '$BUILD_DIR/mongo/db/pipeline/document_source_mock', '$BUILD_DIR/mongo/db/read_write_concern_defaults_mock', '$BUILD_DIR/mongo/db/repl/replication_info', diff --git a/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp index 08c2dced011..777b23622ee 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp @@ -37,6 +37,7 @@ #include "mongo/client/remote_command_targeter_factory_mock.h" #include "mongo/client/remote_command_targeter_mock.h" #include "mongo/db/commands.h" +#include "mongo/db/commands/set_feature_compatibility_version_gen.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/repl/replication_coordinator_mock.h" #include "mongo/db/s/add_shard_cmd_gen.h" @@ -134,13 +135,16 @@ protected: void expectSetFeatureCompatibilityVersion(const HostAndPort& target, StatusWith response, BSONObj writeConcern) { + // (Generic FCV reference): This FCV reference should exist across LTS binary versions. + SetFeatureCompatibilityVersion fcvCmd(ServerGlobalParams::FeatureCompatibility::kLatest); + fcvCmd.setFromConfigServer(true); + fcvCmd.setDbName(NamespaceString::kAdminDb); + const auto setFcvObj = fcvCmd.toBSON(BSON("writeConcern" << writeConcern)); + onCommandForAddShard([&, target, response](const RemoteCommandRequest& request) { ASSERT_EQ(request.target, target); ASSERT_EQ(request.dbname, "admin"); - ASSERT_BSONOBJ_EQ(request.cmdObj, - BSON("setFeatureCompatibilityVersion" - << "4.7" - << "writeConcern" << writeConcern)); + ASSERT_BSONOBJ_EQ(request.cmdObj, setFcvObj); return response; }); diff --git a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp index f03ee603838..d10ba0288d3 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp @@ -84,6 +84,7 @@ using CallbackHandle = executor::TaskExecutor::CallbackHandle; using CallbackArgs = executor::TaskExecutor::CallbackArgs; using RemoteCommandCallbackArgs = executor::TaskExecutor::RemoteCommandCallbackArgs; using RemoteCommandCallbackFn = executor::TaskExecutor::RemoteCommandCallbackFn; +using FeatureCompatibilityParams = ServerGlobalParams::FeatureCompatibility; const ReadPreferenceSetting kConfigReadSelector(ReadPreference::Nearest, TagSet{}); @@ -338,23 +339,24 @@ StatusWith ShardingCatalogManager::_validateHostAsShard( << "field when attempting to add " << connectionString.toString() << " as a shard"); } + const auto currentFcv = serverGlobalParams.featureCompatibility.getVersion(); // (Generic FCV reference): These FCV checks should exist across LTS binary versions. - if (serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo( - ServerGlobalParams::FeatureCompatibility::kLatest) || - serverGlobalParams.featureCompatibility.isUpgradingOrDowngrading()) { - // If the cluster's FCV is kLatest, or upgrading to / downgrading from, the node being added - // must be a version kLatest binary. + if (currentFcv == FeatureCompatibilityParams::kLatest || + currentFcv == FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous || + currentFcv == FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS || + currentFcv == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest || + currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest) { + // If the cluster's FCV is kLatest, or upgrading to / downgrading from kLatest, the node + // being added must be a version kLatest binary. invariant(maxWireVersion == WireVersion::LATEST_WIRE_VERSION); - } else if (serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo( - ServerGlobalParams::FeatureCompatibility::kLastContinuous)) { - // If we are using the kLastContinuous FCV, the node being added must be of the - // last-continuous or latest binary version. + } else if (currentFcv == FeatureCompatibilityParams::kLastContinuous || + currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous) { + // If we are using the kLastContinuous or upgrading to kLastContinuous FCV, the node being + // added must be of the last-continuous or latest binary version. invariant(maxWireVersion >= WireVersion::LAST_CONT_WIRE_VERSION); } else { - // If we are using the kLastLTS FCV, the node being added must be of the last-lts or latest - // binary version. - invariant(maxWireVersion == WireVersion::LAST_LTS_WIRE_VERSION || - maxWireVersion == WireVersion::LATEST_WIRE_VERSION); + // (Generic FCV reference): These FCV checks should exist across LTS binary versions. + invariant(currentFcv == FeatureCompatibilityParams::kLastLTS); } // Check whether there is a master. If there isn't, the replica set may not have been @@ -656,32 +658,49 @@ StatusWith ShardingCatalogManager::addShard( invariant(!opCtx->lockState()->isLocked()); Lock::SharedLock lk(opCtx->lockState(), FeatureCompatibilityVersion::fcvLock); - BSONObjBuilder setFCVBuilder; - // (Generic FCV reference): These FCV checks should exist across LTS binary versions. - switch (serverGlobalParams.featureCompatibility.getVersion()) { - case ServerGlobalParams::FeatureCompatibility::kLatest: - case ServerGlobalParams::FeatureCompatibility::Version::kUpgradingFrom44To47: { - SetFeatureCompatibilityVersion setLatestCmd( - ServerGlobalParams::FeatureCompatibility::kLatest); - // The serialize function generated by IDL requires the DB name to be set. - setLatestCmd.setDbName(NamespaceString::kAdminDb); - setLatestCmd.serialize({}, &setFCVBuilder); - break; - } - default: + // Get the target version that the newly added shard should be set to. + const FeatureCompatibilityParams::Version setVersion = [] { + const auto currentFcv = serverGlobalParams.featureCompatibility.getVersion(); + // (Generic FCV reference): These FCV checks should exist across LTS binary versions. + if (currentFcv == FeatureCompatibilityParams::kLatest || + currentFcv == FeatureCompatibilityParams::kUpgradingFromLastContinuousToLatest || + currentFcv == FeatureCompatibilityParams::kUpgradingFromLastLTSToLatest) { + return FeatureCompatibilityParams::kLatest; + } else if (currentFcv == FeatureCompatibilityParams::kLastContinuous || + currentFcv == + FeatureCompatibilityParams::kDowngradingFromLatestToLastContinuous || + currentFcv == + FeatureCompatibilityParams::kUpgradingFromLastLTSToLastContinuous) { + // (Generic FCV reference): These FCV checks should exist across LTS binary + // versions. + return FeatureCompatibilityParams::kLastContinuous; + } else { // (Generic FCV reference): This FCV reference should exist across LTS binary // versions. - SetFeatureCompatibilityVersion setLastLTSCmd( - ServerGlobalParams::FeatureCompatibility::kLastLTS); - // The serialize function generated by IDL requires the DB name to be set. - setLastLTSCmd.setDbName(NamespaceString::kAdminDb); - setLastLTSCmd.serialize({}, &setFCVBuilder); - break; + invariant(currentFcv == + FeatureCompatibilityParams::kDowngradingFromLatestToLastLTS || + currentFcv == FeatureCompatibilityParams::kLastLTS); + return FeatureCompatibilityParams::kLastLTS; + } + }(); + + SetFeatureCompatibilityVersion setFcvCmd(setVersion); + setFcvCmd.setDbName(NamespaceString::kAdminDb); + // TODO (SERVER-50954): Remove this FCV check once 4.4 is no longer the last LTS + // version. + if (serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo( + FeatureCompatibilityParams::Version::kVersion47)) { + // fromConfigServer is a new parameter added to 4.8 with intention to be backported + // to 4.7. + setFcvCmd.setFromConfigServer(true); } - setFCVBuilder.append(WriteConcernOptions::kWriteConcernField, - opCtx->getWriteConcern().toBSON()); - auto versionResponse = _runCommandForAddShard( - opCtx, targeter.get(), NamespaceString::kAdminDb, setFCVBuilder.obj()); + + auto versionResponse = + _runCommandForAddShard(opCtx, + targeter.get(), + NamespaceString::kAdminDb, + setFcvCmd.toBSON(BSON(WriteConcernOptions::kWriteConcernField + << opCtx->getWriteConcern().toBSON()))); if (!versionResponse.isOK()) { return versionResponse.getStatus(); } diff --git a/src/mongo/db/server_options.h b/src/mongo/db/server_options.h index dcdc09967bb..ae3a87ec5e1 100644 --- a/src/mongo/db/server_options.h +++ b/src/mongo/db/server_options.h @@ -193,11 +193,12 @@ struct ServerGlobalParams { // The order of these enums matter, higher upgrades having higher values, so that // features can be active or inactive if the version is higher than some minimum or // lower than some maximum, respectively. - kUnsetDefault44Behavior = 0, - kFullyDowngradedTo44 = 1, - kDowngradingFrom47To44 = 2, - kUpgradingFrom44To47 = 3, - kVersion47 = 4, + kInvalid = 0, + kUnsetDefault44Behavior = 1, + kFullyDowngradedTo44 = 2, + kDowngradingFrom47To44 = 3, + kUpgradingFrom44To47 = 4, + kVersion47 = 5, }; // These constants should only be used for generic FCV references. Generic references are @@ -215,6 +216,10 @@ struct ServerGlobalParams { static constexpr Version kDowngradingFromLatestToLastLTS = Version::kDowngradingFrom47To44; static constexpr Version kDowngradingFromLatestToLastContinuous = Version::kDowngradingFrom47To44; + // kUpgradingFromLastLTSToLastContinuous is only ever set to a valid FCV when + // kLastLTS and kLastContinuous are not equal. Otherwise, this value should be set to + // kInvalid. + static constexpr Version kUpgradingFromLastLTSToLastContinuous = Version::kInvalid; /** * On startup, the featureCompatibilityVersion may not have been explicitly set yet. This -- cgit v1.2.1