summaryrefslogtreecommitdiff
path: root/src/mongo/db/s/config
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/s/config')
-rw-r--r--src/mongo/db/s/config/configsvr_create_database_command.cpp2
-rw-r--r--src/mongo/db/s/config/configsvr_enable_sharding_command.cpp14
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager.h4
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_create_database_test.cpp88
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_database_operations.cpp44
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_enable_sharding_test.cpp32
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