diff options
7 files changed, 120 insertions, 0 deletions
diff --git a/jstests/multiVersion/fcv_db_versioning.js b/jstests/multiVersion/fcv_db_versioning.js new file mode 100644 index 00000000000..bb47b6deafa --- /dev/null +++ b/jstests/multiVersion/fcv_db_versioning.js @@ -0,0 +1,50 @@ +/** + * Tests that database versions are properly generated and stored when upgrading from FCV 3.6 to FCV + * 4.0 in a sharded cluster. + */ + +(function() { + "use strict"; + + load("jstests/libs/feature_compatibility_version.js"); + + var st = new ShardingTest({ + shards: 1, + shardOptions: {binVersion: "last-stable"}, + }); + + let configPrimaryAdminDB = st.configRS.getPrimary().getDB("admin"); + let shardPrimaryAdminDB = st.rs0.getPrimary().getDB("admin"); + + // Make sure config and shards start with last stable FCV + checkFCV(configPrimaryAdminDB, lastStableFCV); + checkFCV(shardPrimaryAdminDB, lastStableFCV); + + assert.writeOK(st.s.getDB("test1").foo.insert({_id: "test1", x: 1})); + assert.writeOK(st.s.getDB("test2").foo.insert({_id: "test2", x: 1})); + assert.neq(null, st.s.getDB("test1").foo.findOne({_id: "test1", x: 1})); + assert.neq(null, st.s.getDB("test2").foo.findOne({_id: "test2", x: 1})); + + // Make sure the databases don't have versions when FCV <= 3.6 + let test1 = st.s.getDB("config").getCollection("databases").findOne({_id: "test1"}); + assert(!test1.hasOwnProperty("version"), "db test1 has db version before upgrade"); + + let test2 = st.s.getDB("config").getCollection("databases").findOne({_id: "test2"}); + assert(!test2.hasOwnProperty("version"), "db test2 has db version before upgrade"); + + // Set FCV to latest + assert.commandWorked( + configPrimaryAdminDB.runCommand({setFeatureCompatibilityVersion: latestFCV})); + checkFCV(configPrimaryAdminDB, latestFCV); + checkFCV(shardPrimaryAdminDB, latestFCV); + + // Make sure databases were given versions after upgrade + test1 = st.s.getDB("config").getCollection("databases").findOne({_id: "test1"}); + assert(test1.hasOwnProperty("version"), "db test1 does not have db version after upgrade"); + + test2 = st.s.getDB("config").getCollection("databases").findOne({_id: "test2"}); + assert(test2.hasOwnProperty("version"), "db test2 does not have db version after upgrade"); + + st.stop(); + +})();
\ No newline at end of file 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 5772eae91c0..3c1c84e6b34 100644 --- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp +++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp @@ -43,6 +43,8 @@ #include "mongo/db/s/config/sharding_catalog_manager.h" #include "mongo/db/server_options.h" #include "mongo/rpc/get_status_from_command_result.h" +#include "mongo/s/grid.h" +#include "mongo/s/versioning.h" #include "mongo/util/exit.h" #include "mongo/util/fail_point_service.h" #include "mongo/util/scopeguard.h" @@ -167,6 +169,21 @@ public: // Upgrade shards before config finishes its upgrade. if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) { + auto allDbs = uassertStatusOK(Grid::get(opCtx)->catalogClient()->getAllDBs( + opCtx, repl::ReadConcernLevel::kLocalReadConcern)); + + for (const auto& db : allDbs.value) { + const auto dbVersion = Versioning::newDatabaseVersion(); + + uassertStatusOK(Grid::get(opCtx)->catalogClient()->updateConfigDocument( + opCtx, + DatabaseType::ConfigNS, + BSON(DatabaseType::name(db.getName())), + BSON("$set" << BSON(DatabaseType::version(dbVersion.toBSON()))), + false, + ShardingCatalogClient::kLocalWriteConcern)); + } + uassertStatusOK( ShardingCatalogManager::get(opCtx)->setFeatureCompatibilityVersionOnShards( opCtx, diff --git a/src/mongo/s/catalog/sharding_catalog_client.h b/src/mongo/s/catalog/sharding_catalog_client.h index 63de4c670f0..427a4c53775 100644 --- a/src/mongo/s/catalog/sharding_catalog_client.h +++ b/src/mongo/s/catalog/sharding_catalog_client.h @@ -131,6 +131,14 @@ public: repl::ReadConcernLevel readConcernLevel) = 0; /** + * Retrieves all databases in a cluster. + * + * Returns a !OK status if an error occurs. + */ + virtual StatusWith<repl::OpTimeWith<std::vector<DatabaseType>>> getAllDBs( + OperationContext* opCtx, repl::ReadConcernLevel readConcern) = 0; + + /** * Retrieves the metadata for a given collection, if it exists. * * @param nss fully qualified name of the collection (case sensitive) diff --git a/src/mongo/s/catalog/sharding_catalog_client_impl.cpp b/src/mongo/s/catalog/sharding_catalog_client_impl.cpp index ed356618bfe..3bdb9b93dba 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_impl.cpp +++ b/src/mongo/s/catalog/sharding_catalog_client_impl.cpp @@ -276,6 +276,40 @@ StatusWith<repl::OpTimeWith<DatabaseType>> ShardingCatalogClientImpl::getDatabas return result; } +StatusWith<repl::OpTimeWith<std::vector<DatabaseType>>> ShardingCatalogClientImpl::getAllDBs( + OperationContext* opCtx, repl::ReadConcernLevel readConcern) { + std::vector<DatabaseType> databases; + auto findStatus = _exhaustiveFindOnConfig(opCtx, + kConfigReadSelector, + readConcern, + DatabaseType::ConfigNS, + BSONObj(), // no query filter + BSONObj(), // no sort + boost::none); // no limit + if (!findStatus.isOK()) { + return findStatus.getStatus(); + } + + for (const BSONObj& doc : findStatus.getValue().value) { + auto dbRes = DatabaseType::fromBSON(doc); + if (!dbRes.isOK()) { + return dbRes.getStatus().withContext(stream() << "Failed to parse database document " + << doc); + } + + Status validateStatus = dbRes.getValue().validate(); + if (!validateStatus.isOK()) { + return validateStatus.withContext(stream() << "Failed to validate database document " + << doc); + } + + databases.push_back(dbRes.getValue()); + } + + return repl::OpTimeWith<std::vector<DatabaseType>>{std::move(databases), + findStatus.getValue().opTime}; +} + StatusWith<repl::OpTimeWith<DatabaseType>> ShardingCatalogClientImpl::_fetchDatabaseMetadata( OperationContext* opCtx, const std::string& dbName, diff --git a/src/mongo/s/catalog/sharding_catalog_client_impl.h b/src/mongo/s/catalog/sharding_catalog_client_impl.h index 69ab623b5c2..f20ea1e4ac1 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_impl.h +++ b/src/mongo/s/catalog/sharding_catalog_client_impl.h @@ -91,6 +91,9 @@ public: const std::string& dbName, repl::ReadConcernLevel readConcernLevel) override; + StatusWith<repl::OpTimeWith<std::vector<DatabaseType>>> getAllDBs( + OperationContext* opCtx, repl::ReadConcernLevel readConcern) override; + StatusWith<repl::OpTimeWith<CollectionType>> getCollection( OperationContext* opCtx, const NamespaceString& nss, diff --git a/src/mongo/s/catalog/sharding_catalog_client_mock.cpp b/src/mongo/s/catalog/sharding_catalog_client_mock.cpp index 2dfae2be444..04fdd6b649e 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_mock.cpp +++ b/src/mongo/s/catalog/sharding_catalog_client_mock.cpp @@ -79,6 +79,11 @@ StatusWith<repl::OpTimeWith<DatabaseType>> ShardingCatalogClientMock::getDatabas return {ErrorCodes::InternalError, "Method not implemented"}; } +StatusWith<repl::OpTimeWith<std::vector<DatabaseType>>> ShardingCatalogClientMock::getAllDBs( + OperationContext* opCtx, repl::ReadConcernLevel readConcern) { + return {ErrorCodes::InternalError, "Method not implemented"}; +} + StatusWith<repl::OpTimeWith<CollectionType>> ShardingCatalogClientMock::getCollection( OperationContext* opCtx, const NamespaceString& nss, repl::ReadConcernLevel readConcernLevel) { return {ErrorCodes::InternalError, "Method not implemented"}; diff --git a/src/mongo/s/catalog/sharding_catalog_client_mock.h b/src/mongo/s/catalog/sharding_catalog_client_mock.h index 0ea4e6dd18f..643ef0e3d78 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_mock.h +++ b/src/mongo/s/catalog/sharding_catalog_client_mock.h @@ -55,6 +55,9 @@ public: const std::string& dbName, repl::ReadConcernLevel readConcernLevel) override; + StatusWith<repl::OpTimeWith<std::vector<DatabaseType>>> getAllDBs( + OperationContext* opCtx, repl::ReadConcernLevel readConcern) override; + StatusWith<repl::OpTimeWith<CollectionType>> getCollection( OperationContext* opCtx, const NamespaceString& nss, |