diff options
Diffstat (limited to 'src/mongo/db/s/config')
6 files changed, 138 insertions, 46 deletions
diff --git a/src/mongo/db/s/config/configsvr_create_database_command.cpp b/src/mongo/db/s/config/configsvr_create_database_command.cpp index 0f896c16388..d79557364c7 100644 --- a/src/mongo/db/s/config/configsvr_create_database_command.cpp +++ b/src/mongo/db/s/config/configsvr_create_database_command.cpp @@ -96,7 +96,7 @@ public: uassertStatusOK(Grid::get(opCtx)->catalogClient()->getDistLockManager()->lock( opCtx, dbname, "createDatabase", DistLockManager::kDefaultLockTimeout)); - ShardingCatalogManager::get(opCtx)->createDatabase(opCtx, dbname.toString()); + ShardingCatalogManager::get(opCtx)->createDatabase(opCtx, dbname, ShardId()); } private: diff --git a/src/mongo/db/s/config/configsvr_enable_sharding_command.cpp b/src/mongo/db/s/config/configsvr_enable_sharding_command.cpp index e9ca1356b62..22db41df749 100644 --- a/src/mongo/db/s/config/configsvr_enable_sharding_command.cpp +++ b/src/mongo/db/s/config/configsvr_enable_sharding_command.cpp @@ -39,6 +39,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" +#include "mongo/db/field_parser.h" #include "mongo/db/operation_context.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/s/config/sharding_catalog_manager.h" @@ -75,6 +76,8 @@ public: return true; } + static constexpr StringData kShardNameField = "primaryShard"_sd; + std::string help() const override { return "Internal command, which is exported by the sharding config server. Do not call " "directly. Enable sharding on a database."; @@ -109,6 +112,14 @@ public: const std::string dbname = parseNs("", cmdObj); + auto shardElem = cmdObj[kShardNameField]; + ShardId shardId = shardElem.ok() ? ShardId(shardElem.String()) : ShardId(); + + // If assigned, check that the shardId is valid + uassert(ErrorCodes::BadValue, + str::stream() << "invalid shard name: " << shardId, + !shardElem.ok() || shardId.isValid()); + uassert( ErrorCodes::InvalidNamespace, str::stream() << "invalid db name specified: " << dbname, @@ -131,13 +142,12 @@ public: uassertStatusOK(Grid::get(opCtx)->catalogClient()->getDistLockManager()->lock( opCtx, dbname, "enableSharding", DistLockManager::kDefaultLockTimeout)); - ShardingCatalogManager::get(opCtx)->enableSharding(opCtx, dbname); + ShardingCatalogManager::get(opCtx)->enableSharding(opCtx, dbname, shardId); audit::logEnableSharding(Client::getCurrent(), dbname); return true; } } configsvrEnableShardingCmd; - } // namespace } // namespace mongo diff --git a/src/mongo/db/s/config/sharding_catalog_manager.h b/src/mongo/db/s/config/sharding_catalog_manager.h index 0277e821c92..bd976dd497e 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager.h +++ b/src/mongo/db/s/config/sharding_catalog_manager.h @@ -232,7 +232,7 @@ public: * * Throws DatabaseDifferCase if the database already exists with a different case. */ - DatabaseType createDatabase(OperationContext* opCtx, const std::string& dbName); + DatabaseType createDatabase(OperationContext* opCtx, StringData dbName, ShardId primaryShard); /** * Creates a ScopedLock on the database name in _namespaceSerializer. This is to prevent @@ -248,7 +248,7 @@ public: * * Throws DatabaseDifferCase if the database already exists with a different case. */ - void enableSharding(OperationContext* opCtx, const std::string& dbName); + void enableSharding(OperationContext* opCtx, StringData dbName, ShardId primaryShard); /** * Retrieves all databases for a shard. diff --git a/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp index dc89e277360..bb7587c1815 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp @@ -61,11 +61,12 @@ namespace { using executor::RemoteCommandRequest; using std::vector; +using unittest::assertGet; using CreateDatabaseTest = ConfigServerTestFixture; -TEST_F(CreateDatabaseTest, createDatabaseSuccess) { - const std::string dbname = "db1"; +TEST_F(CreateDatabaseTest, createDatabaseSuccessWithoutCustomPrimary) { + auto dbname = StringData("db1"); ShardType s0; s0.setName("shard0000"); @@ -101,7 +102,7 @@ TEST_F(CreateDatabaseTest, createDatabaseSuccess) { auto future = launchAsync([this, dbname] { ThreadClient tc("Test", getGlobalServiceContext()); auto opCtx = cc().makeOperationContext(); - ShardingCatalogManager::get(opCtx.get())->createDatabase(opCtx.get(), dbname); + ShardingCatalogManager::get(opCtx.get())->createDatabase(opCtx.get(), dbname, ShardId()); }); // Return size information about first shard @@ -159,6 +160,66 @@ TEST_F(CreateDatabaseTest, createDatabaseSuccess) { future.default_timed_get(); } +TEST_F(CreateDatabaseTest, createDatabaseSuccessWithCustomPrimary) { + const std::string primaryShardName = "shard0002"; + auto dbname = StringData("dbWithCustomPrimary1"); + + ShardType s0; + s0.setName("shard0000"); + s0.setHost("ShardHost0:27017"); + setupShards(vector<ShardType>{s0}); + + ShardType s1; + s1.setName("shard0001"); + s1.setHost("ShardHost1:27017"); + setupShards(vector<ShardType>{s1}); + + ShardType s2; + s2.setName(primaryShardName); + s2.setHost("ShardHost2:27017"); + setupShards(vector<ShardType>{s2}); + + // Prime the shard registry with information about the existing shards + shardRegistry()->reload(operationContext()); + + // Set up all the target mocks return values. + RemoteCommandTargeterMock::get( + uassertStatusOK(shardRegistry()->getShard(operationContext(), s0.getName()))->getTargeter()) + ->setFindHostReturnValue(HostAndPort(s0.getHost())); + RemoteCommandTargeterMock::get( + uassertStatusOK(shardRegistry()->getShard(operationContext(), s1.getName()))->getTargeter()) + ->setFindHostReturnValue(HostAndPort(s1.getHost())); + RemoteCommandTargeterMock::get( + uassertStatusOK(shardRegistry()->getShard(operationContext(), s2.getName()))->getTargeter()) + ->setFindHostReturnValue(HostAndPort(s2.getHost())); + + // Now actually start the createDatabase work. + + auto future = launchAsync([this, dbname, primaryShardName] { + ThreadClient tc("Test", getGlobalServiceContext()); + auto opCtx = cc().makeOperationContext(); + ShardingCatalogManager::get(opCtx.get()) + ->createDatabase(opCtx.get(), dbname, ShardId(primaryShardName)); + }); + + // Return OK for _flushDatabaseCacheUpdates + onCommand([&](const RemoteCommandRequest& request) { + std::string cmdName = request.cmdObj.firstElement().fieldName(); + ASSERT_EQUALS("_flushDatabaseCacheUpdates", cmdName); + + return BSON("ok" << 1); + }); + + future.default_timed_get(); + + auto databaseDoc = assertGet(findOneOnConfigCollection( + operationContext(), DatabaseType::ConfigNS, BSON("_id" << dbname))); + + DatabaseType foundDatabase = assertGet(DatabaseType::fromBSON(databaseDoc)); + + ASSERT_EQUALS(primaryShardName, foundDatabase.getPrimary()); +} + TEST_F(CreateDatabaseTest, createDatabaseShardReturnsNamespaceNotFoundForFlushDatabaseCacheUpdates) { const std::string dbname = "db1"; @@ -197,7 +258,7 @@ TEST_F(CreateDatabaseTest, auto future = launchAsync([this, dbname] { ThreadClient tc("Test", getGlobalServiceContext()); auto opCtx = cc().makeOperationContext(); - ShardingCatalogManager::get(opCtx.get())->createDatabase(opCtx.get(), dbname); + ShardingCatalogManager::get(opCtx.get())->createDatabase(opCtx.get(), dbname, ShardId()); }); // Return size information about first shard @@ -267,7 +328,8 @@ TEST_F(CreateDatabaseTest, createDatabaseDBExists) { setupDatabase(dbname, shard.getName(), false); - ShardingCatalogManager::get(operationContext())->createDatabase(operationContext(), dbname); + ShardingCatalogManager::get(operationContext()) + ->createDatabase(operationContext(), dbname, ShardId()); } TEST_F(CreateDatabaseTest, createDatabaseDBExistsDifferentCase) { @@ -282,18 +344,18 @@ TEST_F(CreateDatabaseTest, createDatabaseDBExistsDifferentCase) { setupDatabase(dbnameDiffCase, shard.getName(), false); - ASSERT_THROWS_CODE( - ShardingCatalogManager::get(operationContext())->createDatabase(operationContext(), dbname), - AssertionException, - ErrorCodes::DatabaseDifferCase); + ASSERT_THROWS_CODE(ShardingCatalogManager::get(operationContext()) + ->createDatabase(operationContext(), dbname, ShardId()), + AssertionException, + ErrorCodes::DatabaseDifferCase); } TEST_F(CreateDatabaseTest, createDatabaseNoShards) { const std::string dbname = "db5"; - ASSERT_THROWS_CODE( - ShardingCatalogManager::get(operationContext())->createDatabase(operationContext(), dbname), - AssertionException, - ErrorCodes::ShardNotFound); + ASSERT_THROWS_CODE(ShardingCatalogManager::get(operationContext()) + ->createDatabase(operationContext(), dbname, ShardId()), + AssertionException, + ErrorCodes::ShardNotFound); } } // namespace diff --git a/src/mongo/db/s/config/sharding_catalog_manager_database_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_database_operations.cpp index 3d0fc74910a..019e6a66595 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_database_operations.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_database_operations.cpp @@ -44,6 +44,7 @@ #include "mongo/s/client/shard.h" #include "mongo/s/database_version_helpers.h" #include "mongo/s/grid.h" +#include "mongo/s/shard_id.h" #include "mongo/util/log.h" namespace mongo { @@ -58,7 +59,8 @@ const ReadPreferenceSetting kConfigReadSelector(ReadPreference::Nearest, TagSet{ } // namespace DatabaseType ShardingCatalogManager::createDatabase(OperationContext* opCtx, - const std::string& dbName) { + StringData dbName, + ShardId primaryShard) { invariant(nsIsDbOnly(dbName)); // The admin and config databases should never be explicitly created. They "just exist", @@ -67,13 +69,20 @@ DatabaseType ShardingCatalogManager::createDatabase(OperationContext* opCtx, uasserted(ErrorCodes::InvalidOptions, str::stream() << "cannot manually create database '" << dbName << "'"); } + const auto shardPtr = !primaryShard.isValid() + ? nullptr + : uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getShard(opCtx, primaryShard)); + uassert(ErrorCodes::InvalidOptions, + str::stream() << "Invalid shard name: " << primaryShard.toString(), + (primaryShard.isValid() && shardPtr != nullptr) || + (!primaryShard.isValid() && shardPtr == nullptr)); // Check if a database already exists with the same name (case sensitive), and if so, return the // existing entry. BSONObjBuilder queryBuilder; queryBuilder.appendRegex( - DatabaseType::name(), (string) "^" + pcrecpp::RE::QuoteMeta(dbName) + "$", "i"); + DatabaseType::name(), (string) "^" + pcrecpp::RE::QuoteMeta(dbName.toString()) + "$", "i"); auto docs = uassertStatusOK(Grid::get(opCtx)->catalogClient()->_exhaustiveFindOnConfig( opCtx, @@ -86,13 +95,18 @@ DatabaseType ShardingCatalogManager::createDatabase(OperationContext* opCtx, .value; if (!docs.empty()) { - BSONObj dbObj = docs.front(); - std::string actualDbName = dbObj[DatabaseType::name()].String(); + auto actualDb = uassertStatusOK(DatabaseType::fromBSON(docs.front())); uassert(ErrorCodes::DatabaseDifferCase, str::stream() << "can't have 2 databases that just differ on case " - << " have: " << actualDbName << " want to add: " << dbName, - actualDbName == dbName); + << " have: " << actualDb.getName() + << " want to add: " << dbName.toString(), + actualDb.getName() == dbName.toString()); + + uassert(ErrorCodes::NamespaceExists, + str::stream() << "database already created on a primary which is different from: " + << primaryShard, + !primaryShard.isValid() || actualDb.getPrimary() == primaryShard); // We did a local read of the database entry above and found that the database already // exists. However, the data may not be majority committed (a previous createDatabase @@ -100,17 +114,19 @@ DatabaseType ShardingCatalogManager::createDatabase(OperationContext* opCtx, // Since the current Client doesn't know the opTime of the last write to the database entry, // make it wait for the last opTime in the system when we wait for writeConcern. repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx); - return uassertStatusOK(DatabaseType::fromBSON(dbObj)); + return actualDb; } // The database does not exist. Insert an entry for the new database into the sharding catalog. // Pick a primary shard for the new database. - const auto primaryShardId = - uassertStatusOK(_selectShardForNewDatabase(opCtx, Grid::get(opCtx)->shardRegistry())); + const ShardId primaryShardId = shardPtr == nullptr + ? uassertStatusOK(_selectShardForNewDatabase(opCtx, Grid::get(opCtx)->shardRegistry())) + : shardPtr->getId(); // Insert an entry for the new database into the sharding catalog. - DatabaseType db(dbName, std::move(primaryShardId), false, databaseVersion::makeNew()); + DatabaseType db( + dbName.toString(), std::move(primaryShardId), false, databaseVersion::makeNew()); log() << "Registering new database " << db << " in sharding catalog"; @@ -153,7 +169,9 @@ DatabaseType ShardingCatalogManager::createDatabase(OperationContext* opCtx, return db; } -void ShardingCatalogManager::enableSharding(OperationContext* opCtx, const std::string& dbName) { +void ShardingCatalogManager::enableSharding(OperationContext* opCtx, + StringData dbName, + ShardId primaryShard) { invariant(nsIsDbOnly(dbName)); uassert(ErrorCodes::IllegalOperation, @@ -167,7 +185,7 @@ void ShardingCatalogManager::enableSharding(OperationContext* opCtx, const std:: // Creates the database if it doesn't exist and returns the new database entry, else returns the // existing database entry. - auto dbType = createDatabase(opCtx, dbName); + auto dbType = createDatabase(opCtx, dbName, primaryShard); dbType.setSharded(true); // We must wait for the database entry to be majority committed, because it's possible that @@ -186,7 +204,7 @@ void ShardingCatalogManager::enableSharding(OperationContext* opCtx, const std:: uassertStatusOK(Grid::get(opCtx)->catalogClient()->updateConfigDocument( opCtx, DatabaseType::ConfigNS, - BSON(DatabaseType::name(dbName)), + BSON(DatabaseType::name(dbName.toString())), BSON("$set" << BSON(DatabaseType::sharded(true))), false, ShardingCatalogClient::kLocalWriteConcern)); diff --git a/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp index 825236b9575..a14a3772b99 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp @@ -80,7 +80,7 @@ TEST_F(EnableShardingTest, noDBExists) { auto future = launchAsync([&] { ThreadClient tc("Test", getGlobalServiceContext()); auto opCtx = cc().makeOperationContext(); - ShardingCatalogManager::get(opCtx.get())->enableSharding(opCtx.get(), "db1"); + ShardingCatalogManager::get(opCtx.get())->enableSharding(opCtx.get(), "db1", ShardId()); }); // list databases for checking shard size. @@ -117,10 +117,10 @@ TEST_F(EnableShardingTest, dbExistsWithDifferentCase) { shard.setHost("shard0:12"); setupShards(vector<ShardType>{shard}); setupDatabase("Db3", shard.getName(), false); - ASSERT_THROWS_CODE( - ShardingCatalogManager::get(operationContext())->enableSharding(operationContext(), "db3"), - AssertionException, - ErrorCodes::DatabaseDifferCase); + ASSERT_THROWS_CODE(ShardingCatalogManager::get(operationContext()) + ->enableSharding(operationContext(), "db3", ShardId()), + AssertionException, + ErrorCodes::DatabaseDifferCase); } TEST_F(EnableShardingTest, dbExists) { @@ -129,7 +129,8 @@ TEST_F(EnableShardingTest, dbExists) { shard.setHost("shard0:12"); setupShards(vector<ShardType>{shard}); setupDatabase("db4", shard.getName(), false); - ShardingCatalogManager::get(operationContext())->enableSharding(operationContext(), "db4"); + ShardingCatalogManager::get(operationContext()) + ->enableSharding(operationContext(), "db4", ShardId()); } TEST_F(EnableShardingTest, succeedsWhenTheDatabaseIsAlreadySharded) { @@ -138,7 +139,8 @@ TEST_F(EnableShardingTest, succeedsWhenTheDatabaseIsAlreadySharded) { shard.setHost("shard0:12"); setupShards(vector<ShardType>{shard}); setupDatabase("db5", shard.getName(), true); - ShardingCatalogManager::get(operationContext())->enableSharding(operationContext(), "db5"); + ShardingCatalogManager::get(operationContext()) + ->enableSharding(operationContext(), "db5", ShardId()); } TEST_F(EnableShardingTest, dbExistsInvalidFormat) { @@ -157,17 +159,17 @@ TEST_F(EnableShardingTest, dbExistsInvalidFormat) { << "primary" << 12 << "partitioned" << false), ShardingCatalogClient::kMajorityWriteConcern)); - ASSERT_THROWS_CODE( - ShardingCatalogManager::get(operationContext())->enableSharding(operationContext(), "db6"), - AssertionException, - ErrorCodes::TypeMismatch); + ASSERT_THROWS_CODE(ShardingCatalogManager::get(operationContext()) + ->enableSharding(operationContext(), "db6", ShardId()), + AssertionException, + ErrorCodes::TypeMismatch); } TEST_F(EnableShardingTest, noDBExistsNoShards) { - ASSERT_THROWS_CODE( - ShardingCatalogManager::get(operationContext())->enableSharding(operationContext(), "db7"), - AssertionException, - ErrorCodes::ShardNotFound); + ASSERT_THROWS_CODE(ShardingCatalogManager::get(operationContext()) + ->enableSharding(operationContext(), "db7", ShardId()), + AssertionException, + ErrorCodes::ShardNotFound); } } // namespace |