summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLingzhi Deng <lingzhi.deng@mongodb.com>2020-07-06 14:36:42 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-07-15 20:07:53 +0000
commit5446d926d8d21f3dd583b26980308519a2b67708 (patch)
tree9c9c05dcc1b669a587db47e57ba9aa47d84b0c10
parentc26b0c167e8db6506e547e1daaa51f09a6e6c1bd (diff)
downloadmongo-5446d926d8d21f3dd583b26980308519a2b67708.tar.gz
SERVER-49269: Add 'previousVersion' field to FCV document when in downgrading states
-rw-r--r--jstests/multiVersion/genericSetFCVUsage/repair_feature_compatibility_version.js7
-rw-r--r--jstests/noPassthrough/feature_compatibility_version.js36
-rw-r--r--jstests/noPassthroughWithMongod/isMaster_feature_compatibility_version.js8
-rw-r--r--src/mongo/db/commands/SConscript4
-rw-r--r--src/mongo/db/commands/feature_compatibility_version.cpp180
-rw-r--r--src/mongo/db/commands/feature_compatibility_version.h37
-rw-r--r--src/mongo/db/commands/feature_compatibility_version_document.idl69
-rw-r--r--src/mongo/db/commands/feature_compatibility_version_parser.cpp181
-rw-r--r--src/mongo/db/commands/feature_compatibility_version_parser.h13
-rw-r--r--src/mongo/db/commands/set_feature_compatibility_version_command.cpp12
-rw-r--r--src/mongo/db/repair_database_and_check_version.cpp18
-rw-r--r--src/mongo/db/repl/initial_syncer_test.cpp39
-rw-r--r--src/mongo/db/server_options.h10
-rw-r--r--src/mongo/shell/feature_compatibility_version.js19
14 files changed, 392 insertions, 241 deletions
diff --git a/jstests/multiVersion/genericSetFCVUsage/repair_feature_compatibility_version.js b/jstests/multiVersion/genericSetFCVUsage/repair_feature_compatibility_version.js
index e9da9ce7735..ec06f436333 100644
--- a/jstests/multiVersion/genericSetFCVUsage/repair_feature_compatibility_version.js
+++ b/jstests/multiVersion/genericSetFCVUsage/repair_feature_compatibility_version.js
@@ -66,9 +66,10 @@ assert.neq(null,
connection,
"mongod was unable to start up with version=" + latest + " and existing data files");
adminDB = connection.getDB("admin");
-assert.eq(adminDB.system.version.findOne({_id: "featureCompatibilityVersion"}).version,
- lastStableFCV);
-assert.eq(adminDB.system.version.findOne({_id: "featureCompatibilityVersion"}).targetVersion, null);
+const fcvDoc = adminDB.system.version.findOne({_id: "featureCompatibilityVersion"});
+assert.eq(fcvDoc.version, lastStableFCV);
+assert.eq(fcvDoc.targetVersion, undefined);
+assert.eq(fcvDoc.previousVersion, undefined);
MongoRunner.stopMongod(connection);
// If the featureCompatibilityVersion document is present, --repair should just return success.
diff --git a/jstests/noPassthrough/feature_compatibility_version.js b/jstests/noPassthrough/feature_compatibility_version.js
index a413ac02542..47f992f3b22 100644
--- a/jstests/noPassthrough/feature_compatibility_version.js
+++ b/jstests/noPassthrough/feature_compatibility_version.js
@@ -23,31 +23,49 @@ assert.commandWorked(
{$set: {version: lastStableFCV, targetVersion: latestFCV}}));
checkFCV(adminDB, lastStableFCV, latestFCV);
-assert.commandWorked(
- adminDB.system.version.update({_id: "featureCompatibilityVersion"},
- {$set: {version: lastStableFCV, targetVersion: lastStableFCV}}));
+assert.commandWorked(adminDB.system.version.update(
+ {_id: "featureCompatibilityVersion"},
+ {$set: {version: lastStableFCV, targetVersion: lastStableFCV, previousVersion: latestFCV}}));
checkFCV(adminDB, lastStableFCV, lastStableFCV);
-assert.commandWorked(
- adminDB.system.version.update({_id: "featureCompatibilityVersion"},
- {$set: {version: latestFCV}, $unset: {targetVersion: true}}));
+// When present, "previousVersion" will always be the latestFCV.
+assert.writeErrorWithCode(adminDB.system.version.update({_id: "featureCompatibilityVersion"},
+ {$set: {previousVersion: lastStableFCV}}),
+ 4926901);
+checkFCV(adminDB, lastStableFCV, lastStableFCV);
+
+// Downgrading FCV must have a 'previousVersion' field.
+assert.writeErrorWithCode(adminDB.system.version.update({_id: "featureCompatibilityVersion"},
+ {$unset: {previousVersion: true}}),
+ 4926902);
+checkFCV(adminDB, lastStableFCV, lastStableFCV);
+
+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"}}),
- ErrorCodes.BadValue);
+ 4926900);
checkFCV(adminDB, latestFCV);
// Updating the featureCompatibilityVersion document with an invalid targetVersion fails.
assert.writeErrorWithCode(adminDB.system.version.update({_id: "featureCompatibilityVersion"},
{$set: {targetVersion: lastStableFCV}}),
- ErrorCodes.BadValue);
+ 4926904);
checkFCV(adminDB, latestFCV);
assert.writeErrorWithCode(adminDB.system.version.update({_id: "featureCompatibilityVersion"},
{$set: {targetVersion: latestFCV}}),
- ErrorCodes.BadValue);
+ 4926904);
+checkFCV(adminDB, latestFCV);
+
+// Setting an unknown field.
+assert.writeErrorWithCode(adminDB.system.version.update({_id: "featureCompatibilityVersion"},
+ {$set: {unknownField: "unknown"}}),
+ 40415);
checkFCV(adminDB, latestFCV);
MongoRunner.stopMongod(conn);
diff --git a/jstests/noPassthroughWithMongod/isMaster_feature_compatibility_version.js b/jstests/noPassthroughWithMongod/isMaster_feature_compatibility_version.js
index 7dcc280c066..9cace458341 100644
--- a/jstests/noPassthroughWithMongod/isMaster_feature_compatibility_version.js
+++ b/jstests/noPassthroughWithMongod/isMaster_feature_compatibility_version.js
@@ -36,10 +36,10 @@ assert.eq(res.minWireVersion, res.maxWireVersion, tojson(res));
// When the featureCompatibilityVersion is downgrading, running isMaster with internalClient
// returns minWireVersion == maxWireVersion.
-assert.commandWorked(
- adminDB.system.version.update({_id: "featureCompatibilityVersion"},
- {$set: {version: lastStableFCV, targetVersion: lastStableFCV}},
- {writeConcern: {w: 1}}));
+assert.commandWorked(adminDB.system.version.update(
+ {_id: "featureCompatibilityVersion"},
+ {$set: {version: lastStableFCV, targetVersion: lastStableFCV, previousVersion: latestFCV}},
+ {writeConcern: {w: 1}}));
res = adminDB.runCommand(isMasterCommand);
assert.commandWorked(res);
assert.eq(res.minWireVersion, res.maxWireVersion, tojson(res));
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 27bafef5119..5e3591ea3f1 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -81,12 +81,16 @@ env.Library(
source=[
"feature_compatibility_version_parser.cpp",
"feature_compatibility_version_command_parser.cpp",
+ env.Idlc('feature_compatibility_version_document.idl')[0],
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/db/command_generic_argument',
'$BUILD_DIR/mongo/db/namespace_string',
],
+ LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/idl/idl_parser',
+ ],
)
# Commands available in every process that executes commands
diff --git a/src/mongo/db/commands/feature_compatibility_version.cpp b/src/mongo/db/commands/feature_compatibility_version.cpp
index d7f3aef5d42..82acfd8f676 100644
--- a/src/mongo/db/commands/feature_compatibility_version.cpp
+++ b/src/mongo/db/commands/feature_compatibility_version.cpp
@@ -35,6 +35,7 @@
#include "mongo/base/status.h"
#include "mongo/db/catalog_raii.h"
+#include "mongo/db/commands/feature_compatibility_version_document_gen.h"
#include "mongo/db/commands/feature_compatibility_version_documentation.h"
#include "mongo/db/commands/feature_compatibility_version_gen.h"
#include "mongo/db/commands/feature_compatibility_version_parser.h"
@@ -66,34 +67,70 @@ Lock::ResourceMutex FeatureCompatibilityVersion::fcvLock("featureCompatibilityVe
MONGO_FAIL_POINT_DEFINE(hangBeforeAbortingRunningTransactionsOnFCVDowngrade);
-void FeatureCompatibilityVersion::setTargetUpgrade(OperationContext* opCtx) {
- // Sets both 'version' and 'targetVersion' fields.
- _runUpdateCommand(opCtx, [](auto updateMods) {
- updateMods.append(FeatureCompatibilityVersionParser::kVersionField,
- FeatureCompatibilityVersionParser::kVersion44);
- updateMods.append(FeatureCompatibilityVersionParser::kTargetVersionField,
- FeatureCompatibilityVersionParser::kVersion451);
- });
+/**
+ * Build update command for featureCompatibilityVersion document updates.
+ */
+void runUpdateCommand(OperationContext* opCtx, const FeatureCompatibilityVersionDocument& fcvDoc) {
+ DBDirectClient client(opCtx);
+ NamespaceString nss(NamespaceString::kServerConfigurationNamespace);
+
+ BSONObjBuilder updateCmd;
+ updateCmd.append("update", nss.coll());
+ {
+ BSONArrayBuilder updates(updateCmd.subarrayStart("updates"));
+ {
+ BSONObjBuilder updateSpec(updates.subobjStart());
+ {
+ BSONObjBuilder queryFilter(updateSpec.subobjStart("q"));
+ queryFilter.append("_id", FeatureCompatibilityVersionParser::kParameterName);
+ }
+ {
+ BSONObjBuilder updateMods(updateSpec.subobjStart("u"));
+ fcvDoc.serialize(&updateMods);
+ }
+ updateSpec.appendBool("upsert", true);
+ }
+ }
+ auto timeout = opCtx->getWriteConcern().usedDefault ? WriteConcernOptions::kNoTimeout
+ : opCtx->getWriteConcern().wTimeout;
+ auto newWC = WriteConcernOptions(
+ WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, timeout);
+ updateCmd.append(WriteConcernOptions::kWriteConcernField, newWC.toBSON());
+
+ // Update the featureCompatibilityVersion document stored in the server configuration
+ // collection.
+ BSONObj updateResult;
+ client.runCommand(nss.db().toString(), updateCmd.obj(), updateResult);
+ uassertStatusOK(getStatusFromWriteCommandReply(updateResult));
}
-void FeatureCompatibilityVersion::setTargetDowngrade(OperationContext* opCtx) {
+
+void FeatureCompatibilityVersion::setTargetUpgradeFrom(
+ OperationContext* opCtx, ServerGlobalParams::FeatureCompatibility::Version fromVersion) {
// Sets both 'version' and 'targetVersion' fields.
- _runUpdateCommand(opCtx, [](auto updateMods) {
- updateMods.append(FeatureCompatibilityVersionParser::kVersionField,
- FeatureCompatibilityVersionParser::kVersion44);
- updateMods.append(FeatureCompatibilityVersionParser::kTargetVersionField,
- FeatureCompatibilityVersionParser::kVersion44);
- });
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(fromVersion);
+ fcvDoc.setTargetVersion(ServerGlobalParams::FeatureCompatibility::kLatest);
+ runUpdateCommand(opCtx, fcvDoc);
}
-void FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(OperationContext* opCtx,
- StringData version) {
- _validateVersion(version);
+void FeatureCompatibilityVersion::setTargetDowngrade(
+ OperationContext* opCtx, ServerGlobalParams::FeatureCompatibility::Version version) {
+ // Sets 'version', 'targetVersion' and 'previousVersion' fields.
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(version);
+ fcvDoc.setTargetVersion(version);
+ fcvDoc.setPreviousVersion(ServerGlobalParams::FeatureCompatibility::kLatest);
+ runUpdateCommand(opCtx, fcvDoc);
+}
- // Updates 'version' field, while also unsetting the 'targetVersion' field.
- _runUpdateCommand(opCtx, [version](auto updateMods) {
- updateMods.append(FeatureCompatibilityVersionParser::kVersionField, version);
- });
+void FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(
+ OperationContext* opCtx, ServerGlobalParams::FeatureCompatibility::Version version) {
+ // Updates 'version' field, while also unsetting the 'targetVersion' field and the
+ // 'previousVersion' field.
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(version);
+ runUpdateCommand(opCtx, fcvDoc);
}
void FeatureCompatibilityVersion::setIfCleanStartup(OperationContext* opCtx,
@@ -117,17 +154,19 @@ void FeatureCompatibilityVersion::setIfCleanStartup(OperationContext* opCtx,
uassertStatusOK(storageInterface->createCollection(opCtx, nss, options));
}
+ FeatureCompatibilityVersionDocument fcvDoc;
+ if (storeUpgradeVersion) {
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLatest);
+ } else {
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
+ }
+
// We then insert the featureCompatibilityVersion document into the server configuration
// collection. The server parameter will be updated on commit by the op observer.
uassertStatusOK(storageInterface->insertDocument(
opCtx,
nss,
- repl::TimestampedBSONObj{
- BSON("_id" << FeatureCompatibilityVersionParser::kParameterName
- << FeatureCompatibilityVersionParser::kVersionField
- << (storeUpgradeVersion ? FeatureCompatibilityVersionParser::kVersion451
- : FeatureCompatibilityVersionParser::kVersion44)),
- Timestamp()},
+ repl::TimestampedBSONObj{fcvDoc.toBSON(), Timestamp()},
repl::OpTime::kUninitializedTerm)); // No timestamp or term because this write is not
// replicated.
}
@@ -257,51 +296,6 @@ void FeatureCompatibilityVersion::onReplicationRollback(OperationContext* opCtx)
}
}
-void FeatureCompatibilityVersion::_validateVersion(StringData version) {
- uassert(40284,
- str::stream() << "featureCompatibilityVersion must be '"
- << FeatureCompatibilityVersionParser::kVersion451 << "' or '"
- << FeatureCompatibilityVersionParser::kVersion44 << "'. See "
- << feature_compatibility_version_documentation::kCompatibilityLink << ".",
- version == FeatureCompatibilityVersionParser::kVersion451 ||
- version == FeatureCompatibilityVersionParser::kVersion44);
-}
-
-void FeatureCompatibilityVersion::_runUpdateCommand(OperationContext* opCtx,
- UpdateBuilder builder) {
- DBDirectClient client(opCtx);
- NamespaceString nss(NamespaceString::kServerConfigurationNamespace);
-
- BSONObjBuilder updateCmd;
- updateCmd.append("update", nss.coll());
- {
- BSONArrayBuilder updates(updateCmd.subarrayStart("updates"));
- {
- BSONObjBuilder updateSpec(updates.subobjStart());
- {
- BSONObjBuilder queryFilter(updateSpec.subobjStart("q"));
- queryFilter.append("_id", FeatureCompatibilityVersionParser::kParameterName);
- }
- {
- BSONObjBuilder updateMods(updateSpec.subobjStart("u"));
- builder(std::move(updateMods));
- }
- updateSpec.appendBool("upsert", true);
- }
- }
- auto timeout = opCtx->getWriteConcern().usedDefault ? WriteConcernOptions::kNoTimeout
- : opCtx->getWriteConcern().wTimeout;
- auto newWC = WriteConcernOptions(
- WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, timeout);
- updateCmd.append(WriteConcernOptions::kWriteConcernField, newWC.toBSON());
-
- // Update the featureCompatibilityVersion document stored in the server configuration
- // collection.
- BSONObj updateResult;
- client.runCommand(nss.db().toString(), updateCmd.obj(), updateResult);
- uassertStatusOK(getStatusFromWriteCommandReply(updateResult));
-}
-
/**
* Read-only server parameter for featureCompatibilityVersion.
*/
@@ -318,38 +312,28 @@ void FeatureCompatibilityVersionParameter::append(OperationContext* opCtx,
str::stream() << name << " is not yet known.",
serverGlobalParams.featureCompatibility.isVersionInitialized());
+ FeatureCompatibilityVersionDocument fcvDoc;
BSONObjBuilder featureCompatibilityVersionBuilder(b.subobjStart(name));
- switch (serverGlobalParams.featureCompatibility.getVersion()) {
+ auto version = serverGlobalParams.featureCompatibility.getVersion();
+ switch (version) {
case ServerGlobalParams::FeatureCompatibility::kLatest:
- featureCompatibilityVersionBuilder.append(
- FeatureCompatibilityVersionParser::kVersionField,
- FeatureCompatibilityVersionParser::kVersion451);
- return;
- case ServerGlobalParams::FeatureCompatibility::Version::kUpgradingFrom44To451:
- featureCompatibilityVersionBuilder.append(
- FeatureCompatibilityVersionParser::kVersionField,
- FeatureCompatibilityVersionParser::kVersion44);
- featureCompatibilityVersionBuilder.append(
- FeatureCompatibilityVersionParser::kTargetVersionField,
- FeatureCompatibilityVersionParser::kVersion451);
- return;
- case ServerGlobalParams::FeatureCompatibility::Version::kDowngradingFrom451To44:
- featureCompatibilityVersionBuilder.append(
- FeatureCompatibilityVersionParser::kVersionField,
- FeatureCompatibilityVersionParser::kVersion44);
- featureCompatibilityVersionBuilder.append(
- FeatureCompatibilityVersionParser::kTargetVersionField,
- FeatureCompatibilityVersionParser::kVersion44);
- return;
case ServerGlobalParams::FeatureCompatibility::kLastLTS:
- featureCompatibilityVersionBuilder.append(
- FeatureCompatibilityVersionParser::kVersionField,
- FeatureCompatibilityVersionParser::kVersion44);
- return;
+ 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;
}
+ featureCompatibilityVersionBuilder.appendElements(fcvDoc.toBSON().removeField("_id"));
}
Status FeatureCompatibilityVersionParameter::setFromString(const std::string&) {
diff --git a/src/mongo/db/commands/feature_compatibility_version.h b/src/mongo/db/commands/feature_compatibility_version.h
index f4d0263f029..b2076f6de7b 100644
--- a/src/mongo/db/commands/feature_compatibility_version.h
+++ b/src/mongo/db/commands/feature_compatibility_version.h
@@ -52,25 +52,29 @@ public:
static Lock::ResourceMutex fcvLock;
/**
- * Records intent to perform a 4.4 -> 4.5.1 upgrade by updating the on-disk feature
- * compatibility version document to have 'version'=4.4, 'targetVersion'=4.5.1.
- * Should be called before schemas are modified.
+ * Records intent to perform a currentVersion -> kLatest upgrade by updating the on-disk
+ * feature compatibility version document to have 'version'=currentVersion,
+ * 'targetVersion'=kLatest. Should be called before schemas are modified.
*/
- static void setTargetUpgrade(OperationContext* opCtx);
+ static void setTargetUpgradeFrom(OperationContext* opCtx,
+ ServerGlobalParams::FeatureCompatibility::Version fromVersion);
/**
- * Records intent to perform a 4.5.1 -> 4.4 downgrade by updating the on-disk feature
- * compatibility version document to have 'version'=4.4, 'targetVersion'=4.4.
- * Should be called before schemas are modified.
+ * Records intent to perform a downgrade from the latest version by updating the on-disk feature
+ * compatibility version document to have 'version'=version, 'targetVersion'=version and
+ * 'previousVersion'=kLatest. Should be called before schemas are modified.
*/
- static void setTargetDowngrade(OperationContext* opCtx);
+ static void setTargetDowngrade(OperationContext* opCtx,
+ ServerGlobalParams::FeatureCompatibility::Version version);
/**
- * Records the completion of a 4.4 <-> 4.5.1 upgrade or downgrade by updating the on-disk
+ * Records the completion of a upgrade or downgrade by updating the on-disk
* feature compatibility version document to have 'version'=version and unsetting the
- * 'targetVersion' field. Should be called after schemas are modified.
+ * 'targetVersion' field and the 'previousVersion' field. Should be called after schemas are
+ * modified.
*/
- static void unsetTargetUpgradeOrDowngrade(OperationContext* opCtx, StringData version);
+ static void unsetTargetUpgradeOrDowngrade(
+ OperationContext* opCtx, ServerGlobalParams::FeatureCompatibility::Version version);
/**
* If there are no non-local databases, store the featureCompatibilityVersion document. If we
@@ -106,17 +110,6 @@ public:
private:
/**
- * Validate version. Uasserts if invalid.
- */
- static void _validateVersion(StringData version);
-
- /**
- * Build update command.
- */
- typedef std::function<void(BSONObjBuilder)> UpdateBuilder;
- static void _runUpdateCommand(OperationContext* opCtx, UpdateBuilder callback);
-
- /**
* Set the FCV to newVersion, making sure to close any outgoing connections with incompatible
* servers and closing open transactions if necessary. Increments the server TopologyVersion.
*/
diff --git a/src/mongo/db/commands/feature_compatibility_version_document.idl b/src/mongo/db/commands/feature_compatibility_version_document.idl
new file mode 100644
index 00000000000..bf4fc87bc36
--- /dev/null
+++ b/src/mongo/db/commands/feature_compatibility_version_document.idl
@@ -0,0 +1,69 @@
+#Copyright(C) 2020 - present MongoDB, Inc.
+#
+#This program is free software : you can redistribute it and / or modify
+#it under the terms of the Server Side Public License, version 1,
+#as published by MongoDB, Inc.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+#Server Side Public License for more details.
+#
+#You should have received a copy of the Server Side Public License
+#along with this program.If not, see
+# <http: //www.mongodb.com/licensing/server-side-public-license>.
+#
+#As a special exception, the copyright holders give permission to link the
+#code of portions of this program with the OpenSSL library under certain
+#conditions as described in each individual source file and distribute
+#linked combinations including the program with the OpenSSL library.You
+#must comply with the Server Side Public License in all respects for
+#all of the code used other than as permitted herein.If you modify file(s)
+#with this exception, you may extend this exception to your version of the
+#file(s), but you are not obligated to do so.If you do not wish to do so,
+#delete this exception statement from your version.If you delete this
+#exception statement from all source files in the program, then also delete
+#it in the license file.
+#
+global:
+ cpp_namespace: "mongo"
+ cpp_includes:
+ - "mongo/db/commands/feature_compatibility_version_parser.h"
+
+
+imports:
+ - "mongo/idl/basic_types.idl"
+
+types:
+ version_string:
+ bson_serialization_type: string
+ description: >-
+ Ensures that the version strings in featureCompatibilityVersion documents
+ serialize/deserialize to a fixed set of string values
+ cpp_type: "ServerGlobalParams::FeatureCompatibility::Version"
+ serializer: "::mongo::FeatureCompatibilityVersionParser::serializeVersion"
+ deserializer: "mongo::FeatureCompatibilityVersionParser::parseVersion"
+
+structs:
+ featureCompatibilityVersionDocument:
+ description: "featureCompatibilityVersion on-disk document format"
+ fields:
+ _id:
+ type: string
+ description: "Key of the featureCompatibilityVersion state singleton document"
+ default: '"featureCompatibilityVersion"'
+ version:
+ description: "Effective featureCompatibilityVersion"
+ type: version_string
+ targetVersion:
+ description: "Target featureCompatibilityVersion for upgrade/downgrade"
+ type: version_string
+ optional: true
+ previousVersion:
+ description: >-
+ Previous featureCompatibilityVersion for downgrading states, will always be kLatest when
+ present
+ type: version_string
+ optional: true
+ validator:
+ callback: "FeatureCompatibilityVersionParser::validatePreviousVersionField"
diff --git a/src/mongo/db/commands/feature_compatibility_version_parser.cpp b/src/mongo/db/commands/feature_compatibility_version_parser.cpp
index 97fc21eb67e..650557ca644 100644
--- a/src/mongo/db/commands/feature_compatibility_version_parser.cpp
+++ b/src/mongo/db/commands/feature_compatibility_version_parser.cpp
@@ -33,107 +33,150 @@
#include "mongo/base/status.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/commands/feature_compatibility_version_document_gen.h"
#include "mongo/db/commands/feature_compatibility_version_documentation.h"
#include "mongo/db/namespace_string.h"
namespace mongo {
-constexpr StringData FeatureCompatibilityVersionParser::kVersion44;
-constexpr StringData FeatureCompatibilityVersionParser::kVersion451;
-constexpr StringData FeatureCompatibilityVersionParser::kVersionDowngradingFrom451To44;
-constexpr StringData FeatureCompatibilityVersionParser::kVersionUpgradingFrom44To451;
-constexpr StringData FeatureCompatibilityVersionParser::kVersionUnset;
-
constexpr StringData FeatureCompatibilityVersionParser::kParameterName;
-constexpr StringData FeatureCompatibilityVersionParser::kVersionField;
-constexpr StringData FeatureCompatibilityVersionParser::kTargetVersionField;
+
+constexpr StringData FeatureCompatibilityVersionParser::kLastLTS;
+constexpr StringData FeatureCompatibilityVersionParser::kLastContinuous;
+constexpr StringData FeatureCompatibilityVersionParser::kLatest;
+
+ServerGlobalParams::FeatureCompatibility::Version FeatureCompatibilityVersionParser::parseVersion(
+ StringData versionString) {
+ if (versionString == kLastLTS) {
+ return ServerGlobalParams::FeatureCompatibility::kLastLTS;
+ }
+ if (versionString == kLastContinuous) {
+ return ServerGlobalParams::FeatureCompatibility::kLastContinuous;
+ }
+ if (versionString == kLatest) {
+ return ServerGlobalParams::FeatureCompatibility::kLatest;
+ }
+ uasserted(4926900,
+ str::stream() << "Invalid value for " << kParameterName << "document in "
+ << NamespaceString::kServerConfigurationNamespace.toString()
+ << ", found " << versionString << ", expected '" << kLastLTS << "' or '"
+ << kLastContinuous << "' or '" << kLatest << ". See "
+ << feature_compatibility_version_documentation::kCompatibilityLink
+ << ".");
+}
+
+StringData FeatureCompatibilityVersionParser::serializeVersion(
+ ServerGlobalParams::FeatureCompatibility::Version version) {
+ if (version == ServerGlobalParams::FeatureCompatibility::kLastLTS) {
+ return kLastLTS;
+ }
+ if (version == ServerGlobalParams::FeatureCompatibility::kLastContinuous) {
+ return kLastContinuous;
+ }
+ if (version == ServerGlobalParams::FeatureCompatibility::kLatest) {
+ return kLatest;
+ }
+ // It is a bug if we hit here.
+ invariant(false, "Invalid version value for featureCompatibilityVersion documents");
+ MONGO_UNREACHABLE
+}
+
+Status FeatureCompatibilityVersionParser::validatePreviousVersionField(
+ ServerGlobalParams::FeatureCompatibility::Version version) {
+ if (version == ServerGlobalParams::FeatureCompatibility::kLatest) {
+ return Status::OK();
+ }
+ return Status(ErrorCodes::Error(4926901),
+ "when present, 'previousVersion' field must be the latest binary version");
+}
StatusWith<ServerGlobalParams::FeatureCompatibility::Version>
FeatureCompatibilityVersionParser::parse(const BSONObj& featureCompatibilityVersionDoc) {
- ServerGlobalParams::FeatureCompatibility::Version version =
- ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault44Behavior;
- std::string versionString;
- std::string targetVersionString;
-
- for (auto&& elem : featureCompatibilityVersionDoc) {
- auto fieldName = elem.fieldNameStringData();
- if (fieldName == "_id") {
- continue;
- } else if (fieldName == kVersionField || fieldName == kTargetVersionField) {
- if (elem.type() != BSONType::String) {
- return Status(ErrorCodes::TypeMismatch,
+ try {
+ auto fcvDoc = FeatureCompatibilityVersionDocument::parse(
+ IDLParserErrorContext("FeatureCompatibilityVersionParser"),
+ featureCompatibilityVersionDoc);
+ auto version = fcvDoc.getVersion();
+ auto targetVersion = fcvDoc.getTargetVersion();
+ auto previousVersion = fcvDoc.getPreviousVersion();
+
+ // Downgrading FCV.
+ if ((version == ServerGlobalParams::FeatureCompatibility::kLastLTS ||
+ version == ServerGlobalParams::FeatureCompatibility::kLastContinuous) &&
+ version == targetVersion) {
+ // Downgrading FCV must have a "previousVersion" field.
+ if (!previousVersion) {
+ return Status(ErrorCodes::Error(4926902),
str::stream()
- << fieldName << " must be of type String, but was of type "
- << typeName(elem.type()) << ". Contents of " << kParameterName
+ << "Missing field "
+ << FeatureCompatibilityVersionDocument::kPreviousVersionFieldName
+ << " in downgrading states for " << kParameterName
<< " document in "
<< NamespaceString::kServerConfigurationNamespace.toString()
<< ": " << featureCompatibilityVersionDoc << ". See "
<< feature_compatibility_version_documentation::kCompatibilityLink
<< ".");
}
+ if (version == ServerGlobalParams::FeatureCompatibility::kLastLTS) {
+ // Downgrading to last-lts.
+ return ServerGlobalParams::FeatureCompatibility::kDowngradingFromLatestToLastLTS;
+ } else {
+ return ServerGlobalParams::FeatureCompatibility::
+ kDowngradingFromLatestToLastContinuous;
+ }
+ }
+
+ // Non-downgrading FCV must not have a "previousVersion" field.
+ if (previousVersion) {
+ return Status(ErrorCodes::Error(4926903),
+ str::stream()
+ << "Unexpected field "
+ << FeatureCompatibilityVersionDocument::kPreviousVersionFieldName
+ << " in non-downgrading states for " << kParameterName
+ << " document in "
+ << NamespaceString::kServerConfigurationNamespace.toString() << ": "
+ << featureCompatibilityVersionDoc << ". See "
+ << feature_compatibility_version_documentation::kCompatibilityLink
+ << ".");
+ }
- if (elem.String() != kVersion451 && elem.String() != kVersion44) {
- return Status(ErrorCodes::BadValue,
+ // Upgrading FCV.
+ if (targetVersion) {
+ // For upgrading FCV, "targetVersion" must be kLatest and "version" must be
+ // kLastContinuous or kLastLTS.
+ if (targetVersion != ServerGlobalParams::FeatureCompatibility::kLatest ||
+ version == ServerGlobalParams::FeatureCompatibility::kLatest) {
+ return Status(ErrorCodes::Error(4926904),
str::stream()
- << "Invalid value for " << fieldName << ", found "
- << elem.String() << ", expected '" << kVersion451 << "' or '"
- << kVersion44 << "'. Contents of " << kParameterName
- << " document in "
+ << "Invalid " << kParameterName << " document in "
<< NamespaceString::kServerConfigurationNamespace.toString()
<< ": " << featureCompatibilityVersionDoc << ". See "
<< feature_compatibility_version_documentation::kCompatibilityLink
<< ".");
}
- if (fieldName == kVersionField) {
- versionString = elem.String();
- } else if (fieldName == kTargetVersionField) {
- targetVersionString = elem.String();
+ if (version == ServerGlobalParams::FeatureCompatibility::kLastLTS) {
+ return ServerGlobalParams::FeatureCompatibility::kUpgradingFromLastLTSToLatest;
+ } else {
+ invariant(version == ServerGlobalParams::FeatureCompatibility::kLastContinuous);
+ return ServerGlobalParams::FeatureCompatibility::
+ kUpgradingFromLastContinuousToLatest;
}
- } else {
- return Status(ErrorCodes::BadValue,
- str::stream()
- << "Unrecognized field '" << fieldName << "'. Contents of "
- << kParameterName << " document in "
- << NamespaceString::kServerConfigurationNamespace.toString() << ": "
- << featureCompatibilityVersionDoc << ". See "
- << feature_compatibility_version_documentation::kCompatibilityLink
- << ".");
}
- }
- if (versionString == kVersion44) {
- if (targetVersionString == kVersion451) {
- version = ServerGlobalParams::FeatureCompatibility::Version::kUpgradingFrom44To451;
- } else if (targetVersionString == kVersion44) {
- version = ServerGlobalParams::FeatureCompatibility::Version::kDowngradingFrom451To44;
- } else {
- version = ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo44;
- }
- } else if (versionString == kVersion451) {
- if (targetVersionString == kVersion451 || targetVersionString == kVersion44) {
- return Status(ErrorCodes::BadValue,
- str::stream()
- << "Invalid state for " << kParameterName << " document in "
- << NamespaceString::kServerConfigurationNamespace.toString() << ": "
- << featureCompatibilityVersionDoc << ". See "
- << feature_compatibility_version_documentation::kCompatibilityLink
- << ".");
- } else {
- version = ServerGlobalParams::FeatureCompatibility::Version::kVersion451;
- }
- } else {
- return Status(ErrorCodes::BadValue,
- str::stream()
- << "Missing required field '" << kVersionField << "''. Contents of "
- << kParameterName << " document in "
+ // No "targetVersion" or "previousVersion" field.
+ return version;
+ } catch (const DBException& e) {
+ auto status = e.toStatus();
+ status.addContext(str::stream()
+ << "Invalid " << kParameterName << " document in "
<< NamespaceString::kServerConfigurationNamespace.toString() << ": "
<< featureCompatibilityVersionDoc << ". See "
<< feature_compatibility_version_documentation::kCompatibilityLink
<< ".");
+ return status;
}
-
- return version;
+ MONGO_UNREACHABLE
}
} // namespace mongo
diff --git a/src/mongo/db/commands/feature_compatibility_version_parser.h b/src/mongo/db/commands/feature_compatibility_version_parser.h
index 01f25fe6f5d..2fdaf962c49 100644
--- a/src/mongo/db/commands/feature_compatibility_version_parser.h
+++ b/src/mongo/db/commands/feature_compatibility_version_parser.h
@@ -47,8 +47,17 @@ public:
static constexpr StringData kVersionUnset = "Unset"_sd;
static constexpr StringData kParameterName = "featureCompatibilityVersion"_sd;
- static constexpr StringData kVersionField = "version"_sd;
- static constexpr StringData kTargetVersionField = "targetVersion"_sd;
+
+ static constexpr StringData kLastLTS = kVersion44;
+ static constexpr StringData kLastContinuous = kVersion44;
+ static constexpr StringData kLatest = kVersion451;
+
+ static ServerGlobalParams::FeatureCompatibility::Version parseVersion(StringData versionString);
+
+ static StringData serializeVersion(ServerGlobalParams::FeatureCompatibility::Version version);
+
+ static Status validatePreviousVersionField(
+ ServerGlobalParams::FeatureCompatibility::Version version);
/**
* Parses the featureCompatibilityVersion document from the server configuration collection
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 f34a64d157c..87b0540ebea 100644
--- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
+++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
@@ -200,7 +200,8 @@ public:
return true;
}
- FeatureCompatibilityVersion::setTargetUpgrade(opCtx);
+ FeatureCompatibilityVersion::setTargetUpgradeFrom(
+ opCtx, ServerGlobalParams::FeatureCompatibility::kLastLTS);
{
// Take the global lock in S mode to create a barrier for operations taking the
@@ -237,7 +238,8 @@ public:
}
hangWhileUpgrading.pauseWhileSet(opCtx);
- FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(opCtx, requestedVersion);
+ FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(
+ opCtx, FeatureCompatibilityVersionParser::parseVersion(requestedVersion));
} else if (requestedVersion == FeatureCompatibilityVersionParser::kVersion44) {
uassert(ErrorCodes::IllegalOperation,
"cannot initiate setting featureCompatibilityVersion to 4.4 while a previous "
@@ -279,7 +281,8 @@ public:
"nodes");
LOGV2(4637905, "The current replica set config has been propagated to all nodes.");
- FeatureCompatibilityVersion::setTargetDowngrade(opCtx);
+ FeatureCompatibilityVersion::setTargetDowngrade(
+ opCtx, FeatureCompatibilityVersionParser::parseVersion(requestedVersion));
{
// Take the global lock in S mode to create a barrier for operations taking the
@@ -317,7 +320,8 @@ public:
}
hangWhileDowngrading.pauseWhileSet(opCtx);
- FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(opCtx, requestedVersion);
+ FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(
+ opCtx, FeatureCompatibilityVersionParser::parseVersion(requestedVersion));
}
return true;
diff --git a/src/mongo/db/repair_database_and_check_version.cpp b/src/mongo/db/repair_database_and_check_version.cpp
index b7a56e42764..3624ce8b6f3 100644
--- a/src/mongo/db/repair_database_and_check_version.cpp
+++ b/src/mongo/db/repair_database_and_check_version.cpp
@@ -41,6 +41,7 @@
#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/catalog/multi_index_block.h"
#include "mongo/db/commands/feature_compatibility_version.h"
+#include "mongo/db/commands/feature_compatibility_version_document_gen.h"
#include "mongo/db/commands/feature_compatibility_version_documentation.h"
#include "mongo/db/commands/feature_compatibility_version_parser.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
@@ -115,22 +116,19 @@ Status restoreMissingFeatureCompatibilityVersionDocument(OperationContext* opCtx
fcvColl,
BSON("_id" << FeatureCompatibilityVersionParser::kParameterName),
featureCompatibilityVersion)) {
- LOGV2(21000,
+ LOGV2(4926905,
"Re-creating featureCompatibilityVersion document that was deleted. Creating new "
- "document with version "
- "{FeatureCompatibilityVersionParser_kVersion44}.",
- "Re-creating featureCompatibilityVersion document that was deleted",
- "version"_attr = FeatureCompatibilityVersionParser::kVersion44);
+ "document with last LTS version.",
+ "version"_attr = FeatureCompatibilityVersionParser::kLastLTS);
- BSONObj fcvObj = BSON("_id" << FeatureCompatibilityVersionParser::kParameterName
- << FeatureCompatibilityVersionParser::kVersionField
- << FeatureCompatibilityVersionParser::kVersion44);
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
writeConflictRetry(opCtx, "insertFCVDocument", fcvNss.ns(), [&] {
WriteUnitOfWork wunit(opCtx);
OpDebug* const nullOpDebug = nullptr;
- uassertStatusOK(
- fcvColl->insertDocument(opCtx, InsertStatement(fcvObj), nullOpDebug, false));
+ uassertStatusOK(fcvColl->insertDocument(
+ opCtx, InsertStatement(fcvDoc.toBSON()), nullOpDebug, false));
wunit.commit();
});
}
diff --git a/src/mongo/db/repl/initial_syncer_test.cpp b/src/mongo/db/repl/initial_syncer_test.cpp
index 8adea565621..b4ddd113e96 100644
--- a/src/mongo/db/repl/initial_syncer_test.cpp
+++ b/src/mongo/db/repl/initial_syncer_test.cpp
@@ -36,6 +36,7 @@
#include <ostream>
#include "mongo/db/client.h"
+#include "mongo/db/commands/feature_compatibility_version_document_gen.h"
#include "mongo/db/commands/feature_compatibility_version_parser.h"
#include "mongo/db/index_builds_coordinator_mongod.h"
#include "mongo/db/json.h"
@@ -676,9 +677,9 @@ void assertFCVRequest(RemoteCommandRequest request) {
}
void InitialSyncerTest::processSuccessfulFCVFetcherResponseLastStable() {
- auto docs = {BSON("_id" << FeatureCompatibilityVersionParser::kParameterName << "version"
- << FeatureCompatibilityVersionParser::kVersion44)};
- processSuccessfulFCVFetcherResponse(docs);
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
+ processSuccessfulFCVFetcherResponse({fcvDoc.toBSON()});
}
void InitialSyncerTest::processSuccessfulFCVFetcherResponse(std::vector<BSONObj> docs) {
@@ -1895,8 +1896,9 @@ TEST_F(InitialSyncerTest,
TEST_F(InitialSyncerTest,
InitialSyncerReturnsTooManyMatchingDocumentsWhenFCVFetcherReturnsMultipleDocuments) {
- auto docs = {BSON("_id" << FeatureCompatibilityVersionParser::kParameterName << "version"
- << FeatureCompatibilityVersionParser::kVersion44),
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
+ auto docs = {fcvDoc.toBSON(),
BSON("_id"
<< "other")};
runInitialSyncWithBadFCVResponse(docs, ErrorCodes::TooManyMatchingDocuments);
@@ -1904,24 +1906,25 @@ TEST_F(InitialSyncerTest,
TEST_F(InitialSyncerTest,
InitialSyncerReturnsIncompatibleServerVersionWhenFCVFetcherReturnsUpgradeTargetVersion) {
- auto docs = {BSON("_id" << FeatureCompatibilityVersionParser::kParameterName << "version"
- << FeatureCompatibilityVersionParser::kVersion44 << "targetVersion"
- << FeatureCompatibilityVersionParser::kVersion451)};
- runInitialSyncWithBadFCVResponse(docs, ErrorCodes::IncompatibleServerVersion);
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
+ fcvDoc.setTargetVersion(ServerGlobalParams::FeatureCompatibility::kLatest);
+ runInitialSyncWithBadFCVResponse({fcvDoc.toBSON()}, ErrorCodes::IncompatibleServerVersion);
}
TEST_F(InitialSyncerTest,
InitialSyncerReturnsIncompatibleServerVersionWhenFCVFetcherReturnsDowngradeTargetVersion) {
- auto docs = {BSON("_id" << FeatureCompatibilityVersionParser::kParameterName << "version"
- << FeatureCompatibilityVersionParser::kVersion44 << "targetVersion"
- << FeatureCompatibilityVersionParser::kVersion44)};
- runInitialSyncWithBadFCVResponse(docs, ErrorCodes::IncompatibleServerVersion);
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
+ fcvDoc.setTargetVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
+ fcvDoc.setPreviousVersion(ServerGlobalParams::FeatureCompatibility::kLatest);
+ runInitialSyncWithBadFCVResponse({fcvDoc.toBSON()}, ErrorCodes::IncompatibleServerVersion);
}
-TEST_F(InitialSyncerTest, InitialSyncerReturnsBadValueWhenFCVFetcherReturnsNoVersion) {
+TEST_F(InitialSyncerTest, InitialSyncerReturnsParseErrorWhenFCVFetcherReturnsNoVersion) {
auto docs = {BSON("_id" << FeatureCompatibilityVersionParser::kParameterName << "targetVersion"
<< FeatureCompatibilityVersionParser::kVersion44)};
- runInitialSyncWithBadFCVResponse(docs, ErrorCodes::BadValue);
+ runInitialSyncWithBadFCVResponse(docs, ((ErrorCodes::Error)40414));
}
TEST_F(InitialSyncerTest, InitialSyncerSucceedsWhenFCVFetcherReturnsOldVersion) {
@@ -1951,9 +1954,9 @@ TEST_F(InitialSyncerTest, InitialSyncerSucceedsWhenFCVFetcherReturnsOldVersion)
// Oplog entry associated with the beginApplyingTimestamp.
processSuccessfulLastOplogEntryFetcherResponse({makeOplogEntryObj(1)});
- auto docs = {BSON("_id" << FeatureCompatibilityVersionParser::kParameterName << "version"
- << FeatureCompatibilityVersionParser::kVersion44)};
- processSuccessfulFCVFetcherResponse(docs);
+ FeatureCompatibilityVersionDocument fcvDoc;
+ fcvDoc.setVersion(ServerGlobalParams::FeatureCompatibility::kLastLTS);
+ processSuccessfulFCVFetcherResponse({fcvDoc.toBSON()});
}
// We shut it down so we do not have to finish initial sync. If the fCV fetcher got an error,
diff --git a/src/mongo/db/server_options.h b/src/mongo/db/server_options.h
index 6577d946c32..05130d67482 100644
--- a/src/mongo/db/server_options.h
+++ b/src/mongo/db/server_options.h
@@ -204,6 +204,16 @@ struct ServerGlobalParams {
static constexpr Version kLastContinuous = Version::kFullyDowngradedTo44;
static constexpr Version kLastLTS = Version::kFullyDowngradedTo44;
+ // These constants should only be used for generic FCV references. Generic references are
+ // FCV references that are expected to exist across LTS binary versions.
+ // NOTE: DO NOT USE THEM FOR REGULAR FCV CHECKS.
+ static constexpr Version kUpgradingFromLastLTSToLatest = Version::kUpgradingFrom44To451;
+ static constexpr Version kUpgradingFromLastContinuousToLatest =
+ Version::kUpgradingFrom44To451;
+ static constexpr Version kDowngradingFromLatestToLastLTS = Version::kDowngradingFrom451To44;
+ static constexpr Version kDowngradingFromLatestToLastContinuous =
+ Version::kDowngradingFrom451To44;
+
/**
* On startup, the featureCompatibilityVersion may not have been explicitly set yet. This
* exposes the actual state of the featureCompatibilityVersion if it is uninitialized.
diff --git a/src/mongo/shell/feature_compatibility_version.js b/src/mongo/shell/feature_compatibility_version.js
index b014a3b90d5..a126ae9d165 100644
--- a/src/mongo/shell/feature_compatibility_version.js
+++ b/src/mongo/shell/feature_compatibility_version.js
@@ -16,14 +16,24 @@ var lastStableFCV = "4.4";
/**
* Checks the featureCompatibilityVersion document and server parameter. The
* featureCompatibilityVersion document is of the form {_id: "featureCompatibilityVersion", version:
- * <required>, targetVersion: <optional>}. The getParameter result is of the form
- * {featureCompatibilityVersion: {version: <required>, targetVersion: <optional>}, ok: 1}.
+ * <required>, targetVersion: <optional>, previousVersion: <optional>}. The getParameter result is
+ * of the form {featureCompatibilityVersion: {version: <required>, targetVersion: <optional>,
+ * previousVersion: <optional>}, ok: 1}.
*/
function checkFCV(adminDB, version, targetVersion) {
let res = adminDB.runCommand({getParameter: 1, featureCompatibilityVersion: 1});
assert.commandWorked(res);
assert.eq(res.featureCompatibilityVersion.version, version, tojson(res));
assert.eq(res.featureCompatibilityVersion.targetVersion, targetVersion, tojson(res));
+ // When both version and targetVersion are equal to lastStableFCV, downgrade is in progress.
+ // This tests that previousVersion is always equal to latestFCV in downgrading states or
+ // undefined otherwise.
+ const isDowngrading = (version === lastStableFCV && targetVersion === lastStableFCV);
+ if (isDowngrading) {
+ assert.eq(res.featureCompatibilityVersion.previousVersion, latestFCV, tojson(res));
+ } else {
+ assert.eq(res.featureCompatibilityVersion.previousVersion, undefined, tojson(res));
+ }
// This query specifies an explicit readConcern because some FCV tests pass a connection that
// has manually run isMaster with internalClient, and mongod expects internalClients (ie. other
@@ -34,6 +44,11 @@ function checkFCV(adminDB, version, targetVersion) {
.next();
assert.eq(doc.version, version, tojson(doc));
assert.eq(doc.targetVersion, targetVersion, tojson(doc));
+ if (isDowngrading) {
+ assert.eq(doc.previousVersion, latestFCV, tojson(doc));
+ } else {
+ assert.eq(doc.previousVersion, undefined, tojson(doc));
+ }
}
/**