summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLouis Williams <louis.williams@mongodb.com>2017-09-22 13:55:59 -0400
committerLouis Williams <louis.williams@mongodb.com>2017-09-28 11:10:05 -0400
commitb0b99866781302ba8b16de033ff2681f20483c14 (patch)
treee5e988301e89390aebcf5ec12055465e353dbb77 /src
parentf24fbb0011c6ded9101f08574e7cd07e63690a9b (diff)
downloadmongo-b0b99866781302ba8b16de033ff2681f20483c14.tar.gz
SERVER-31209 Persist targetVersion in FCV document to indicate an upgrade/downgrade in progress.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/catalog/database_impl.cpp2
-rw-r--r--src/mongo/db/commands/feature_compatibility_version.cpp225
-rw-r--r--src/mongo/db/commands/feature_compatibility_version.h60
-rw-r--r--src/mongo/db/commands/feature_compatibility_version_command_parser.cpp1
-rw-r--r--src/mongo/db/commands/feature_compatibility_version_command_parser.h1
-rw-r--r--src/mongo/db/commands/mr.cpp5
-rw-r--r--src/mongo/db/commands/set_feature_compatibility_version_command.cpp41
-rw-r--r--src/mongo/db/db.cpp34
-rw-r--r--src/mongo/db/server_options.h14
-rw-r--r--src/mongo/dbtests/dbtests.cpp1
10 files changed, 260 insertions, 124 deletions
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 5f6f7d61748..2891b7977bc 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -763,7 +763,7 @@ Collection* DatabaseImpl::createCollection(OperationContext* opCtx,
CollectionOptions optionsWithUUID = options;
if (enableCollectionUUIDs && !optionsWithUUID.uuid &&
- serverGlobalParams.featureCompatibility.isSchemaVersion36.load() == true) {
+ serverGlobalParams.featureCompatibility.isSchemaVersion36()) {
optionsWithUUID.uuid.emplace(CollectionUUID::gen());
}
diff --git a/src/mongo/db/commands/feature_compatibility_version.cpp b/src/mongo/db/commands/feature_compatibility_version.cpp
index f6515bc0cd1..c35d1eb993f 100644
--- a/src/mongo/db/commands/feature_compatibility_version.cpp
+++ b/src/mongo/db/commands/feature_compatibility_version.cpp
@@ -55,51 +55,21 @@ constexpr StringData FeatureCompatibilityVersion::kCommandName;
constexpr StringData FeatureCompatibilityVersion::kDatabase;
constexpr StringData FeatureCompatibilityVersion::kParameterName;
constexpr StringData FeatureCompatibilityVersion::kVersionField;
+constexpr StringData FeatureCompatibilityVersion::kTargetVersionField;
-namespace {
-BSONObj makeUpdateCommand(StringData newVersion, BSONObj writeConcern) {
- BSONObjBuilder updateCmd;
+StatusWith<FeatureCompatibilityVersionInfo> FeatureCompatibilityVersion::parse(
+ const BSONObj& featureCompatibilityVersionInfo) {
+ FeatureCompatibilityVersionInfo versionInfo;
- NamespaceString nss(FeatureCompatibilityVersion::kCollection);
- updateCmd.append("update", nss.coll());
- {
- BSONArrayBuilder updates(updateCmd.subarrayStart("updates"));
- {
- BSONObjBuilder updateSpec(updates.subobjStart());
- {
- BSONObjBuilder queryFilter(updateSpec.subobjStart("q"));
- queryFilter.append("_id", FeatureCompatibilityVersion::kParameterName);
- }
- {
- BSONObjBuilder updateMods(updateSpec.subobjStart("u"));
- updateMods.append(FeatureCompatibilityVersion::kVersionField, newVersion);
- }
- updateSpec.appendBool("upsert", true);
- }
- }
-
- if (!writeConcern.isEmpty()) {
- updateCmd.append(WriteConcernOptions::kWriteConcernField, writeConcern);
- }
-
- return updateCmd.obj();
-}
-} // namespace
-
-StatusWith<ServerGlobalParams::FeatureCompatibility::Version> FeatureCompatibilityVersion::parse(
- const BSONObj& featureCompatibilityVersionDoc) {
- bool foundVersionField = false;
- ServerGlobalParams::FeatureCompatibility::Version version;
-
- for (auto&& elem : featureCompatibilityVersionDoc) {
+ for (auto&& elem : featureCompatibilityVersionInfo) {
auto fieldName = elem.fieldNameStringData();
if (fieldName == "_id") {
continue;
- } else if (fieldName == FeatureCompatibilityVersion::kVersionField) {
- foundVersionField = true;
+ } else if (fieldName == FeatureCompatibilityVersion::kVersionField ||
+ fieldName == FeatureCompatibilityVersion::kTargetVersionField) {
if (elem.type() != BSONType::String) {
return Status(ErrorCodes::TypeMismatch,
- str::stream() << FeatureCompatibilityVersion::kVersionField
+ str::stream() << fieldName
<< " must be of type String, but was of type "
<< typeName(elem.type())
<< ". Contents of "
@@ -107,20 +77,20 @@ StatusWith<ServerGlobalParams::FeatureCompatibility::Version> FeatureCompatibili
<< " document in "
<< FeatureCompatibilityVersion::kCollection
<< ": "
- << featureCompatibilityVersionDoc
+ << featureCompatibilityVersionInfo
<< ". See "
<< feature_compatibility_version::kDochubLink
<< ".");
}
+
+ ServerGlobalParams::FeatureCompatibility::Version version;
if (elem.String() == FeatureCompatibilityVersionCommandParser::kVersion36) {
version = ServerGlobalParams::FeatureCompatibility::Version::k36;
} else if (elem.String() == FeatureCompatibilityVersionCommandParser::kVersion34) {
version = ServerGlobalParams::FeatureCompatibility::Version::k34;
} else {
return Status(ErrorCodes::BadValue,
- str::stream() << "Invalid value for "
- << FeatureCompatibilityVersion::kVersionField
- << ", found "
+ str::stream() << "Invalid value for " << fieldName << ", found "
<< elem.String()
<< ", expected '"
<< FeatureCompatibilityVersionCommandParser::kVersion36
@@ -131,27 +101,32 @@ StatusWith<ServerGlobalParams::FeatureCompatibility::Version> FeatureCompatibili
<< " document in "
<< FeatureCompatibilityVersion::kCollection
<< ": "
- << featureCompatibilityVersionDoc
+ << featureCompatibilityVersionInfo
<< ". See "
<< feature_compatibility_version::kDochubLink
<< ".");
}
+
+ if (fieldName == FeatureCompatibilityVersion::kVersionField) {
+ versionInfo.version = version;
+ } else if (fieldName == FeatureCompatibilityVersion::kTargetVersionField) {
+ versionInfo.targetVersion = version;
+ }
} else {
return Status(ErrorCodes::BadValue,
- str::stream() << "Unrecognized field '" << elem.fieldName()
- << "''. Contents of "
+ str::stream() << "Unrecognized field '" << fieldName << "'. Contents of "
<< FeatureCompatibilityVersion::kParameterName
<< " document in "
<< FeatureCompatibilityVersion::kCollection
<< ": "
- << featureCompatibilityVersionDoc
+ << featureCompatibilityVersionInfo
<< ". See "
<< feature_compatibility_version::kDochubLink
<< ".");
}
}
- if (!foundVersionField) {
+ if (versionInfo.version == ServerGlobalParams::FeatureCompatibility::Version::kUnset) {
return Status(ErrorCodes::BadValue,
str::stream() << "Missing required field '"
<< FeatureCompatibilityVersion::kVersionField
@@ -160,45 +135,45 @@ StatusWith<ServerGlobalParams::FeatureCompatibility::Version> FeatureCompatibili
<< " document in "
<< FeatureCompatibilityVersion::kCollection
<< ": "
- << featureCompatibilityVersionDoc
+ << featureCompatibilityVersionInfo
<< ". See "
<< feature_compatibility_version::kDochubLink
<< ".");
}
- return version;
+ return versionInfo;
}
void FeatureCompatibilityVersion::set(OperationContext* opCtx, StringData version) {
- uassert(40284,
- str::stream() << "featureCompatibilityVersion must be '"
- << FeatureCompatibilityVersionCommandParser::kVersion36
- << "' or '"
- << FeatureCompatibilityVersionCommandParser::kVersion34
- << "'. See "
- << feature_compatibility_version::kDochubLink
- << ".",
- version == FeatureCompatibilityVersionCommandParser::kVersion36 ||
- version == FeatureCompatibilityVersionCommandParser::kVersion34);
+ // Upgrades to a version, which sets the 'version' field only.
+ _runUpdateCommand(opCtx, version, [version](auto updateMods) {
+ BSONObjBuilder setOp(updateMods.subobjStart("$set"));
+ setOp.append(FeatureCompatibilityVersion::kVersionField, version);
+ });
+}
- DBDirectClient client(opCtx);
- NamespaceString nss(FeatureCompatibilityVersion::kCollection);
+void FeatureCompatibilityVersion::setTargetUpgrade(OperationContext* opCtx, StringData version) {
+ // Only set 'targetVersion' field.
+ _runUpdateCommand(opCtx, version, [version](auto updateMods) {
+ BSONObjBuilder setOp(updateMods.subobjStart("$set"));
+ setOp.append(FeatureCompatibilityVersion::kTargetVersionField, version);
+ });
+}
- // Update the featureCompatibilityVersion document stored in the "admin.system.version"
- // collection.
- BSONObj updateResult;
- client.runCommand(nss.db().toString(),
- makeUpdateCommand(version, WriteConcernOptions::Majority),
- updateResult);
- uassertStatusOK(getStatusFromCommandResult(updateResult));
- uassertStatusOK(getWriteConcernStatusFromCommandResult(updateResult));
+void FeatureCompatibilityVersion::setTargetDowngrade(OperationContext* opCtx, StringData version) {
+ // Set both 'version' and 'targetVersion' fields.
+ _runUpdateCommand(opCtx, version, [version](auto updateMods) {
+ updateMods.append(FeatureCompatibilityVersion::kVersionField, version);
+ updateMods.append(FeatureCompatibilityVersion::kTargetVersionField, version);
+ });
+}
- // Close all internal connections to versions lower than 3.6.
- if (version == FeatureCompatibilityVersionCommandParser::kVersion36) {
- opCtx->getServiceContext()->getServiceEntryPoint()->endAllSessions(
- transport::Session::kLatestVersionInternalClientKeepOpen |
- transport::Session::kExternalClientKeepOpen);
- }
+void FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(OperationContext* opCtx,
+ StringData version) {
+ // Updates 'version' field, while also unsetting the 'targetVersion' field.
+ _runUpdateCommand(opCtx, version, [version](auto updateMods) {
+ updateMods.append(FeatureCompatibilityVersion::kVersionField, version);
+ });
}
void FeatureCompatibilityVersion::setIfCleanStartup(OperationContext* opCtx,
@@ -225,9 +200,10 @@ void FeatureCompatibilityVersion::setIfCleanStartup(OperationContext* opCtx,
// the "admin.system.version" collection.
invariant(autoDB.justCreated());
- // We update the value of the isSchemaVersion36 server parameter so the
- // admin.system.version collection gets a UUID.
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(true);
+ // We update the value of the version server parameter so that the admin.system.version
+ // collection gets a UUID.
+ serverGlobalParams.featureCompatibility.version.store(
+ ServerGlobalParams::FeatureCompatibility::Version::k36);
uassertStatusOK(storageInterface->createCollection(opCtx, nss, {}));
}
@@ -253,13 +229,27 @@ void FeatureCompatibilityVersion::onInsertOrUpdate(OperationContext* opCtx, cons
idElement.String() != FeatureCompatibilityVersion::kParameterName) {
return;
}
- auto newVersion = uassertStatusOK(FeatureCompatibilityVersion::parse(doc));
- log() << "setting featureCompatibilityVersion to " << toString(newVersion);
- opCtx->recoveryUnit()->onCommit([newVersion]() {
- serverGlobalParams.featureCompatibility.version.store(newVersion);
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(
- serverGlobalParams.featureCompatibility.version.load() ==
- ServerGlobalParams::FeatureCompatibility::Version::k36);
+ auto versionInfo = uassertStatusOK(FeatureCompatibilityVersion::parse(doc));
+
+ // Indicate if an upgrade or downgrade is in progress.
+ if (versionInfo.targetVersion != ServerGlobalParams::FeatureCompatibility::Version::kUnset) {
+ log() << "targeting featureCompatibilityVersion " << toString(versionInfo.targetVersion);
+ }
+
+ // To avoid extra log messages when the targetVersion is set/unset, only log when the version
+ // changes.
+ auto oldVersion = serverGlobalParams.featureCompatibility.version.load();
+ auto newVersion = versionInfo.version;
+ if (oldVersion != newVersion) {
+ log() << "setting featureCompatibilityVersion to " << toString(newVersion);
+ }
+
+ // On commit, update the server parameters, and close any incoming connections with a wire
+ // version that is below the minimum.
+ opCtx->recoveryUnit()->onCommit([opCtx, versionInfo]() {
+ serverGlobalParams.featureCompatibility.version.store(versionInfo.version);
+ serverGlobalParams.featureCompatibility.targetVersion.store(versionInfo.targetVersion);
+ _closeConnectionsBelowVersion(opCtx, versionInfo);
});
}
@@ -274,7 +264,8 @@ void FeatureCompatibilityVersion::onDelete(OperationContext* opCtx, const BSONOb
opCtx->recoveryUnit()->onCommit([]() {
serverGlobalParams.featureCompatibility.version.store(
ServerGlobalParams::FeatureCompatibility::Version::k34);
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(false);
+ serverGlobalParams.featureCompatibility.targetVersion.store(
+ ServerGlobalParams::FeatureCompatibility::Version::kUnset);
});
}
@@ -284,10 +275,70 @@ void FeatureCompatibilityVersion::onDropCollection(OperationContext* opCtx) {
opCtx->recoveryUnit()->onCommit([]() {
serverGlobalParams.featureCompatibility.version.store(
ServerGlobalParams::FeatureCompatibility::Version::k34);
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(false);
+ serverGlobalParams.featureCompatibility.targetVersion.store(
+ ServerGlobalParams::FeatureCompatibility::Version::kUnset);
});
}
+void FeatureCompatibilityVersion::_validateVersion(StringData version) {
+ uassert(40284,
+ str::stream() << "featureCompatibilityVersion must be '"
+ << FeatureCompatibilityVersionCommandParser::kVersion36
+ << "' or '"
+ << FeatureCompatibilityVersionCommandParser::kVersion34
+ << "'. See "
+ << feature_compatibility_version::kDochubLink
+ << ".",
+ version == FeatureCompatibilityVersionCommandParser::kVersion36 ||
+ version == FeatureCompatibilityVersionCommandParser::kVersion34);
+}
+
+void FeatureCompatibilityVersion::_closeConnectionsBelowVersion(
+ OperationContext* opCtx, FeatureCompatibilityVersionInfo versionInfo) {
+
+ // Close all internal connections to versions lower than 3.6.
+ if (versionInfo.version == ServerGlobalParams::FeatureCompatibility::Version::k36 ||
+ versionInfo.targetVersion == ServerGlobalParams::FeatureCompatibility::Version::k36) {
+ opCtx->getServiceContext()->getServiceEntryPoint()->endAllSessions(
+ transport::Session::kLatestVersionInternalClientKeepOpen |
+ transport::Session::kExternalClientKeepOpen);
+ }
+}
+
+void FeatureCompatibilityVersion::_runUpdateCommand(OperationContext* opCtx,
+ StringData version,
+ UpdateBuilder builder) {
+ _validateVersion(version);
+
+ DBDirectClient client(opCtx);
+ NamespaceString nss(FeatureCompatibilityVersion::kCollection);
+
+ BSONObjBuilder updateCmd;
+ updateCmd.append("update", nss.coll());
+ {
+ BSONArrayBuilder updates(updateCmd.subarrayStart("updates"));
+ {
+ BSONObjBuilder updateSpec(updates.subobjStart());
+ {
+ BSONObjBuilder queryFilter(updateSpec.subobjStart("q"));
+ queryFilter.append("_id", FeatureCompatibilityVersion::kParameterName);
+ }
+ {
+ BSONObjBuilder updateMods(updateSpec.subobjStart("u"));
+ builder(std::move(updateMods));
+ }
+ updateSpec.appendBool("upsert", true);
+ }
+ }
+ updateCmd.append(WriteConcernOptions::kWriteConcernField, WriteConcernOptions::Majority);
+
+ // Update the featureCompatibilityVersion document stored in the "admin.system.version"
+ // collection.
+ BSONObj updateResult;
+ client.runCommand(nss.db().toString(), updateCmd.obj(), updateResult);
+ uassertStatusOK(getStatusFromCommandResult(updateResult));
+ uassertStatusOK(getWriteConcernStatusFromCommandResult(updateResult));
+}
/**
* Read-only server parameter for featureCompatibilityVersion.
*/
diff --git a/src/mongo/db/commands/feature_compatibility_version.h b/src/mongo/db/commands/feature_compatibility_version.h
index d89b2e996d5..abb48b82101 100644
--- a/src/mongo/db/commands/feature_compatibility_version.h
+++ b/src/mongo/db/commands/feature_compatibility_version.h
@@ -41,6 +41,18 @@ class BSONObj;
class OperationContext;
/**
+ * Store state of featureCompatibilityVersion document.
+ **/
+struct FeatureCompatibilityVersionInfo {
+ ServerGlobalParams::FeatureCompatibility::Version version;
+ ServerGlobalParams::FeatureCompatibility::Version targetVersion;
+
+ FeatureCompatibilityVersionInfo()
+ : version(ServerGlobalParams::FeatureCompatibility::Version::kUnset),
+ targetVersion(ServerGlobalParams::FeatureCompatibility::Version::kUnset) {}
+};
+
+/**
* Startup parameter to ignore featureCompatibilityVersion checks. This parameter cannot be set if
* the node is started with --replSet, --master, or --slave. This should never be set by end users.
*/
@@ -53,12 +65,13 @@ public:
static constexpr StringData kDatabase = "admin"_sd;
static constexpr StringData kParameterName = "featureCompatibilityVersion"_sd;
static constexpr StringData kVersionField = "version"_sd;
+ static constexpr StringData kTargetVersionField = "targetVersion"_sd;
/**
* Parses the featureCompatibilityVersion document from admin.system.version, and returns the
* version.
*/
- static StatusWith<ServerGlobalParams::FeatureCompatibility::Version> parse(
+ static StatusWith<FeatureCompatibilityVersionInfo> parse(
const BSONObj& featureCompatibilityVersionDoc);
static StringData toString(ServerGlobalParams::FeatureCompatibility::Version version) {
@@ -67,6 +80,8 @@ public:
return FeatureCompatibilityVersionCommandParser::kVersion36;
case ServerGlobalParams::FeatureCompatibility::Version::k34:
return FeatureCompatibilityVersionCommandParser::kVersion34;
+ case ServerGlobalParams::FeatureCompatibility::Version::kUnset:
+ return FeatureCompatibilityVersionCommandParser::kVersionUnset;
default:
MONGO_UNREACHABLE;
}
@@ -80,6 +95,27 @@ public:
static void set(OperationContext* opCtx, StringData version);
/**
+ * Indicate intent to perform an upgrade. Should be set before schemas are modified.
+ * This sets the 'targetVersion' field only.
+ * Use unsetTargetUpgradeOrDowngrade to indicate that the schemas have completed the upgrading.
+ */
+ static void setTargetUpgrade(OperationContext* opCtx, StringData version);
+
+ /**
+ * Indicate intent to perform a downgrade. Should be set before schemas are modified.
+ * This atomically updates both the 'version' and 'targetVersion' fields.
+ * Use unsetTargetUpgradeOrDowngrade to indicate that the schemas have completed downgrading.
+ */
+ static void setTargetDowngrade(OperationContext* opCtx, StringData version);
+
+ /**
+ * Indicate the completion of an upgrade or downgrade. Should be set only when schemas are
+ * done being upgraded or modified.
+ * Unsets the 'targetVersion' field and updates the 'version' field.
+ */
+ static void unsetTargetUpgradeOrDowngrade(OperationContext* opCtx, StringData version);
+
+ /**
* If there are no non-local databases and we are not running with --shardsvr, set
* featureCompatibilityVersion to the latest value.
*/
@@ -104,6 +140,28 @@ public:
* Resets the server parameter to its default value on commit.
*/
static void onDropCollection(OperationContext* opCtx);
+
+private:
+ /**
+ * Validate version. Uasserts if invalid.
+ */
+ static void _validateVersion(StringData version);
+
+ /**
+ * Close incoming connections from interal clients who cannot speak our highest wire protocol
+ * version.
+ */
+ static void _closeConnectionsBelowVersion(OperationContext* opCtx,
+ FeatureCompatibilityVersionInfo versionInfo);
+
+ /**
+ * Build update command.
+ */
+ typedef stdx::function<void(BSONObjBuilder)> UpdateBuilder;
+ static void _runUpdateCommand(OperationContext* opCtx,
+ StringData version,
+ UpdateBuilder callback);
};
+
} // namespace mongo
diff --git a/src/mongo/db/commands/feature_compatibility_version_command_parser.cpp b/src/mongo/db/commands/feature_compatibility_version_command_parser.cpp
index ae781c8137f..1859bc13f9e 100644
--- a/src/mongo/db/commands/feature_compatibility_version_command_parser.cpp
+++ b/src/mongo/db/commands/feature_compatibility_version_command_parser.cpp
@@ -43,6 +43,7 @@ constexpr StringData kVersion32 = "3.2"_sd;
constexpr StringData FeatureCompatibilityVersionCommandParser::kVersion34;
constexpr StringData FeatureCompatibilityVersionCommandParser::kVersion36;
+constexpr StringData FeatureCompatibilityVersionCommandParser::kVersionUnset;
StatusWith<std::string> FeatureCompatibilityVersionCommandParser::extractVersionFromCommand(
StringData commandName, const BSONObj& cmdObj) {
diff --git a/src/mongo/db/commands/feature_compatibility_version_command_parser.h b/src/mongo/db/commands/feature_compatibility_version_command_parser.h
index c4544fdbff1..498c9c8261d 100644
--- a/src/mongo/db/commands/feature_compatibility_version_command_parser.h
+++ b/src/mongo/db/commands/feature_compatibility_version_command_parser.h
@@ -49,6 +49,7 @@ public:
*/
static constexpr StringData kVersion34 = "3.4"_sd;
static constexpr StringData kVersion36 = "3.6"_sd;
+ static constexpr StringData kVersionUnset = "Unset"_sd;
/**
* Interprets the specified BSON as a command and extracts the desired compatibility version
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index 84ebb935f39..b9ecf5b4006 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -421,7 +421,7 @@ void State::prepTempCollection() {
options.setNoIdIndex();
options.temp = true;
if (enableCollectionUUIDs &&
- serverGlobalParams.featureCompatibility.isSchemaVersion36.load() == true) {
+ serverGlobalParams.featureCompatibility.isSchemaVersion36()) {
options.uuid.emplace(UUID::gen());
}
incColl = incCtx.db()->createCollection(_opCtx, _config.incLong.ns(), options);
@@ -505,8 +505,7 @@ void State::prepTempCollection() {
CollectionOptions options = finalOptions;
options.temp = true;
- if (enableCollectionUUIDs &&
- serverGlobalParams.featureCompatibility.isSchemaVersion36.load() == true) {
+ if (enableCollectionUUIDs && serverGlobalParams.featureCompatibility.isSchemaVersion36()) {
// If a UUID for the final output collection was sent by mongos (i.e., the final output
// collection is sharded), use the UUID mongos sent when creating the temp collection.
// When the temp collection is renamed to the final output collection, the UUID will be
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 77bfd2bb8d9..92d8ce3e0e3 100644
--- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
+++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp
@@ -128,30 +128,33 @@ public:
opCtx, writeConcern);
}
+ // Perform an upgrade
if (version != existingVersion && isFCVUpgrade(version)) {
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(true);
+ // Set targetVersion to indicate that an upgrade is in progress, then unset when done.
+ FeatureCompatibilityVersion::setTargetUpgrade(opCtx, version);
updateUUIDSchemaVersion(opCtx, /*upgrade*/ true);
- existingVersion = version;
- }
-
- // For reproducing failure after adding UUIDs and before upgrade FCV document.
- if (MONGO_FAIL_POINT(featureCompatibilityUpgrade)) {
- exitCleanly(EXIT_CLEAN);
- }
- FeatureCompatibilityVersion::set(opCtx, version);
-
- // For reproducing failure after downgrading FCV document, and before removing UUIDs.
- if (MONGO_FAIL_POINT(featureCompatibilityDowngrade)) {
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(false);
- exitCleanly(EXIT_CLEAN);
- }
+ // For reproducing failure after adding UUIDs and before upgrade FCV document.
+ if (MONGO_FAIL_POINT(featureCompatibilityUpgrade)) {
+ exitCleanly(EXIT_CLEAN);
+ }
+ // Unsets 'targetVersion' and sets 'version' to new version.
+ FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(opCtx, version);
+
+ // Always perform a downgrade when setting the FCV to 3.4, in case of a crash after
+ // writing the document and before completing the upgrade.
+ } else if (version == FeatureCompatibilityVersionCommandParser::kVersion34) {
+ // Set targetVersion to indicate that a downgrade is in progress, then unset when
+ // complete.
+ FeatureCompatibilityVersion::setTargetDowngrade(opCtx, version);
+
+ // For reproducing failure after downgrading FCV document, and before removing UUIDs.
+ if (MONGO_FAIL_POINT(featureCompatibilityDowngrade)) {
+ exitCleanly(EXIT_CLEAN);
+ }
- // Always perform a downgrade when setting the FCV to 3.4, in case of a crash after writing
- // the document and before completing the upgrade.
- if (version == FeatureCompatibilityVersionCommandParser::kVersion34) {
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(false);
updateUUIDSchemaVersion(opCtx, /*upgrade*/ false);
+ FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(opCtx, version);
}
// Ensure we try reading the keys for signing clusterTime immediately on upgrade to 3.6.
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 8f29f0605bc..ed3ba14e852 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -405,17 +405,33 @@ void repairDatabasesAndCheckVersion(OperationContext* opCtx) {
versionColl,
BSON("_id" << FeatureCompatibilityVersion::kParameterName),
featureCompatibilityVersion)) {
- auto version = FeatureCompatibilityVersion::parse(featureCompatibilityVersion);
- if (!version.isOK()) {
- severe() << version.getStatus();
+ auto swVersionInfo =
+ FeatureCompatibilityVersion::parse(featureCompatibilityVersion);
+ if (!swVersionInfo.isOK()) {
+ severe() << swVersionInfo.getStatus();
fassertFailedNoTrace(40283);
}
- serverGlobalParams.featureCompatibility.version.store(version.getValue());
-
- // Update schemaVersion parameter.
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(
- serverGlobalParams.featureCompatibility.version.load() ==
- ServerGlobalParams::FeatureCompatibility::Version::k36);
+ auto versionInfo = swVersionInfo.getValue();
+ serverGlobalParams.featureCompatibility.version.store(versionInfo.version);
+ serverGlobalParams.featureCompatibility.targetVersion.store(
+ versionInfo.targetVersion);
+
+ // On startup, if the targetVersion field exists, then an upgrade/downgrade
+ // did not complete successfully.
+ if (versionInfo.targetVersion !=
+ ServerGlobalParams::FeatureCompatibility::Version::kUnset) {
+ log() << "** WARNING: A featureCompatibilityVersion upgrade or downgrade "
+ "did not complete. "
+ << startupWarningsLog;
+ log() << "** The current featureCompatibilityVersion is "
+ << FeatureCompatibilityVersion::toString(versionInfo.version)
+ << " and the targeted version is "
+ << FeatureCompatibilityVersion::toString(versionInfo.targetVersion)
+ << "." << startupWarningsLog;
+ log() << "** To fix this, use the setFeatureCompatibilityVersion "
+ << "command to resume upgrade to 3.6 or downgrade to 3.4."
+ << startupWarningsLog;
+ }
}
}
}
diff --git a/src/mongo/db/server_options.h b/src/mongo/db/server_options.h
index a82d20acc79..23f8bddff4f 100644
--- a/src/mongo/db/server_options.h
+++ b/src/mongo/db/server_options.h
@@ -158,14 +158,22 @@ struct ServerGlobalParams {
* 3.4 node can participate in a cluster whose feature compatibility version is 3.6.
*/
k36,
+ /**
+ * This is only used for targetVersion to indicate that no upgrade is in progress.
+ */
+ kUnset
};
// Read-only parameter featureCompatibilityVersion.
AtomicWord<Version> version{Version::k34};
- // Read-only global isSchemaVersion36. This determines whether to give Collections UUIDs
- // upon creation.
- AtomicWord<bool> isSchemaVersion36{false};
+ // If set, an upgrade or downgrade is in progress to the set version.
+ AtomicWord<Version> targetVersion{Version::kUnset};
+
+ // This determines whether to give Collections UUIDs upon creation.
+ bool isSchemaVersion36() {
+ return (version.load() == Version::k36 || targetVersion.load() == Version::k36);
+ }
// Feature validation differs depending on the role of a mongod in a replica set or
// master/slave configuration. Masters/primaries can accept user-initiated writes and
diff --git a/src/mongo/dbtests/dbtests.cpp b/src/mongo/dbtests/dbtests.cpp
index c4f26023093..8721fc4dd48 100644
--- a/src/mongo/dbtests/dbtests.cpp
+++ b/src/mongo/dbtests/dbtests.cpp
@@ -123,7 +123,6 @@ int dbtestsMain(int argc, char** argv, char** envp) {
mongo::runGlobalInitializersOrDie(argc, argv, envp);
serverGlobalParams.featureCompatibility.version.store(
ServerGlobalParams::FeatureCompatibility::Version::k36);
- serverGlobalParams.featureCompatibility.isSchemaVersion36.store(true);
repl::ReplSettings replSettings;
replSettings.setOplogSizeBytes(10 * 1024 * 1024);
ServiceContext* service = getGlobalServiceContext();