From c88d4b73a63011f624cc1cf1cdd7692943d11ffa Mon Sep 17 00:00:00 2001 From: Blake Oler Date: Wed, 21 Feb 2018 13:43:24 -0500 Subject: SERVER-33196 add 'forTest' flag to movePrimary, and if it's set, make _configsvrMovePrimary call movePrimary on the primary shard --- jstests/sharding/move_primary_basic.js | 3 -- jstests/sharding/move_primary_with_test_flag.js | 51 ++++++++++++++++++++++ .../db/s/config/configsvr_move_primary_command.cpp | 38 ++++++++++++---- src/mongo/db/s/move_primary_command.cpp | 41 ++++++++++++++++- src/mongo/s/commands/cluster_move_primary_cmd.cpp | 1 + src/mongo/s/request_types/move_primary.idl | 22 +++++++++- 6 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 jstests/sharding/move_primary_with_test_flag.js diff --git a/jstests/sharding/move_primary_basic.js b/jstests/sharding/move_primary_basic.js index e2e609fc866..3a54d51be06 100644 --- a/jstests/sharding/move_primary_basic.js +++ b/jstests/sharding/move_primary_basic.js @@ -18,9 +18,6 @@ st.ensurePrimaryShard(kDbName, shard0); assert.eq(shard0, mongos.getDB('config').databases.findOne({_id: kDbName}).primary); - // Can run on shards. - assert.commandWorked(st.d0.getDB('admin').runCommand({_movePrimary: kDbName, to: shard1})); - // Can run only against the admin database. assert.commandFailedWithCode( mongos.getDB('test').runCommand({movePrimary: kDbName, to: shard0}), diff --git a/jstests/sharding/move_primary_with_test_flag.js b/jstests/sharding/move_primary_with_test_flag.js new file mode 100644 index 00000000000..e02b714de50 --- /dev/null +++ b/jstests/sharding/move_primary_with_test_flag.js @@ -0,0 +1,51 @@ +// +// Tests for movePrimary with test flag. +// + +(function() { + 'use strict'; + + var st = new ShardingTest({mongos: 1, shards: 2}); + + var mongos = st.s0; + + var kDbName = 'db'; + + var shard0 = st.shard0.shardName; + var shard1 = st.shard1.shardName; + + assert.commandWorked(mongos.adminCommand({enableSharding: kDbName})); + st.ensurePrimaryShard(kDbName, shard0); + assert.eq(shard0, mongos.getDB('config').databases.findOne({_id: kDbName}).primary); + + // Can run only against the admin database. + assert.commandFailedWithCode( + mongos.getDB('test').runCommand({movePrimary: kDbName, to: shard0, forTest: true}), + ErrorCodes.Unauthorized); + + // Can't movePrimary for 'config' database. + assert.commandFailed(mongos.adminCommand({movePrimary: 'config', to: shard0, forTest: true})); + + // Can't movePrimary for 'local' database. + assert.commandFailed(mongos.adminCommand({movePrimary: 'local', to: shard0, forTest: true})); + + // Can't movePrimary for 'admin' database. + assert.commandFailed(mongos.adminCommand({movePrimary: 'admin', to: shard0, forTest: true})); + + // Can't movePrimary for invalid db name. + assert.commandFailed(mongos.adminCommand({movePrimary: 'a.b', to: shard0, forTest: true})); + assert.commandFailed(mongos.adminCommand({movePrimary: '', to: shard0, forTest: true})); + + // Fail if 'to' shard is empty. + assert.commandFailed(mongos.adminCommand({movePrimary: kDbName, to: '', forTest: true})); + assert.commandFailed(mongos.adminCommand({movePrimary: kDbName, forTest: true})); + + // Succeed if 'to' shard is already the primary shard for the db. + assert.commandWorked(mongos.adminCommand({movePrimary: kDbName, to: shard1, forTest: true})); + // The following line will be uncommented when the underlying fcv 4.0 movePrimary logic is + // complete. + // assert.eq(shard1, mongos.getDB('config').databases.findOne({_id: kDbName}).primary); + + st.stop(); + +})(); diff --git a/src/mongo/db/s/config/configsvr_move_primary_command.cpp b/src/mongo/db/s/config/configsvr_move_primary_command.cpp index 4389584d922..82fd5898f8f 100644 --- a/src/mongo/db/s/config/configsvr_move_primary_command.cpp +++ b/src/mongo/db/s/config/configsvr_move_primary_command.cpp @@ -134,6 +134,15 @@ public: << cmdObj, opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + const std::string to = movePrimaryRequest.getTo().toString(); + + if (to.empty()) { + return CommandHelpers::appendCommandStatus( + result, + {ErrorCodes::InvalidOptions, + str::stream() << "you have to specify where you want to move it"}); + } + auto const catalogClient = Grid::get(opCtx)->catalogClient(); auto const catalogCache = Grid::get(opCtx)->catalogCache(); auto const catalogManager = ShardingCatalogManager::get(opCtx); @@ -152,16 +161,29 @@ public: opCtx, dbname, repl::ReadConcernLevel::kLocalReadConcern)) .value; - const std::string to = movePrimaryRequest.getTo().toString(); + const auto fromShard = uassertStatusOK(shardRegistry->getShard(opCtx, dbType.getPrimary())); - if (to.empty()) { - return CommandHelpers::appendCommandStatus( - result, - {ErrorCodes::InvalidOptions, - str::stream() << "you have to specify where you want to move it"}); - } + // fcv 4.0 logic for movePrimary (being tested under the 'forTest' flag while in + // development). + if (movePrimaryRequest.getForTest()) { + const NamespaceString nss(dbname); - const auto fromShard = uassertStatusOK(shardRegistry->getShard(opCtx, dbType.getPrimary())); + ShardMovePrimary shardMovePrimaryRequest; + shardMovePrimaryRequest.set_movePrimary(nss); + shardMovePrimaryRequest.setTo(movePrimaryRequest.getTo()); + + auto cmdResponse = uassertStatusOK(fromShard->runCommandWithFixedRetryAttempts( + opCtx, + ReadPreferenceSetting(ReadPreference::PrimaryOnly), + "admin", + CommandHelpers::appendMajorityWriteConcern(CommandHelpers::appendPassthroughFields( + cmdObj, shardMovePrimaryRequest.toBSON())), + Shard::RetryPolicy::kIdempotent)); + + CommandHelpers::filterCommandReplyForPassthrough(cmdResponse.response, &result); + + return true; + } const auto toShard = [&]() { auto toShardStatus = shardRegistry->getShard(opCtx, to); diff --git a/src/mongo/db/s/move_primary_command.cpp b/src/mongo/db/s/move_primary_command.cpp index 58ccf871748..6375c3e9bf4 100644 --- a/src/mongo/db/s/move_primary_command.cpp +++ b/src/mongo/db/s/move_primary_command.cpp @@ -32,6 +32,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/commands.h" +#include "mongo/s/request_types/move_primary_gen.h" #include "mongo/util/log.h" namespace mongo { @@ -67,8 +68,16 @@ public: return Status::OK(); } + virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { + const auto nsElt = cmdObj.firstElement(); + uassert(ErrorCodes::InvalidNamespace, + "'movePrimary' must be of type String", + nsElt.type() == BSONType::String); + return nsElt.str(); + } + bool run(OperationContext* opCtx, - const std::string& dbname, + const std::string& dbname_unused, const BSONObj& cmdObj, BSONObjBuilder& result) override { if (serverGlobalParams.clusterRole != ClusterRole::ShardServer) { @@ -78,6 +87,36 @@ public: "_movePrimary can only be run on shard servers")); } + auto movePrimaryRequest = MovePrimary::parse(IDLParserErrorContext("_movePrimary"), cmdObj); + const auto dbname = parseNs("", cmdObj); + + uassert( + ErrorCodes::InvalidNamespace, + str::stream() << "invalid db name specified: " << dbname, + NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow)); + + if (dbname == NamespaceString::kAdminDb || dbname == NamespaceString::kConfigDb || + dbname == NamespaceString::kLocalDb) { + return CommandHelpers::appendCommandStatus( + result, + {ErrorCodes::InvalidOptions, + str::stream() << "Can't move primary for " << dbname << " database"}); + } + + uassert(ErrorCodes::InvalidOptions, + str::stream() << "_movePrimary must be called with majority writeConcern, got " + << cmdObj, + opCtx->getWriteConcern().wMode == WriteConcernOptions::kMajority); + + const std::string to = movePrimaryRequest.getTo().toString(); + + if (to.empty()) { + return CommandHelpers::appendCommandStatus( + result, + {ErrorCodes::InvalidOptions, + str::stream() << "you have to specify where you want to move it"}); + } + return true; } diff --git a/src/mongo/s/commands/cluster_move_primary_cmd.cpp b/src/mongo/s/commands/cluster_move_primary_cmd.cpp index 7dcc1a6f101..0d21748b29a 100644 --- a/src/mongo/s/commands/cluster_move_primary_cmd.cpp +++ b/src/mongo/s/commands/cluster_move_primary_cmd.cpp @@ -105,6 +105,7 @@ public: ConfigsvrMovePrimary configMovePrimaryRequest; configMovePrimaryRequest.set_configsvrMovePrimary(nss); configMovePrimaryRequest.setTo(movePrimaryRequest.getTo()); + configMovePrimaryRequest.setForTest(movePrimaryRequest.getForTest()); // Invalidate the routing table cache entry for this database so that we reload the // collection the next time it's accessed, even if we receive a failure, e.g. NetworkError. diff --git a/src/mongo/s/request_types/move_primary.idl b/src/mongo/s/request_types/move_primary.idl index d4f79660e60..2e944cbb8ec 100644 --- a/src/mongo/s/request_types/move_primary.idl +++ b/src/mongo/s/request_types/move_primary.idl @@ -48,6 +48,10 @@ structs: to: type: string description: "The shard serving as the destination for un-sharded collections." + forTest: + type: bool + description: "The flag indicating whether the fcv 4.0 logic for movePrimary should be used." + default: false ConfigsvrMovePrimary: description: "The internal movePrimary command on the config server" @@ -58,4 +62,20 @@ structs: description: "The namespace of the database whose primary shard is to be reassigned." to: type: string - description: "The shard serving as the destination for un-sharded collections." \ No newline at end of file + description: "The shard serving as the destination for un-sharded collections." + forTest: + type: bool + description: "The flag indicating whether the fcv 4.0 logic for movePrimary should be used." + default: false + + ShardMovePrimary: + description: "The internal movePrimary command on a primary shard" + strict: false + fields: + _movePrimary: + type: namespacestring + description: "The namespace of the database whose primary shard is to be reassigned." + to: + type: string + description: "The shard serving as the destination for un-sharded collections." + \ No newline at end of file -- cgit v1.2.1