diff options
author | Simon Graetzer <simon.gratzer@mongodb.com> | 2021-06-15 10:39:32 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-06-15 11:25:45 +0000 |
commit | 335f39070b2dbb16f25aebea96d365ecebcb5a10 (patch) | |
tree | 1f0e1454f1d820d80bca524350c475d66f48c303 | |
parent | d7ea9eaf3cf9195f6f9e102fca4cb9967279d9c8 (diff) | |
download | mongo-335f39070b2dbb16f25aebea96d365ecebcb5a10.tar.gz |
SERVER-56840 IDLify _configsvrCommitChunkMerge command
-rw-r--r-- | jstests/auth/lib/commands_lib.js | 4 | ||||
-rw-r--r-- | src/mongo/db/s/config/configsvr_merge_chunk_command.cpp | 93 | ||||
-rw-r--r-- | src/mongo/db/s/config/configsvr_merge_chunks_command.cpp | 85 | ||||
-rw-r--r-- | src/mongo/db/s/merge_chunks_command.cpp | 39 | ||||
-rw-r--r-- | src/mongo/s/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/s/chunk_version.h | 14 | ||||
-rw-r--r-- | src/mongo/s/chunk_version.idl | 2 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunk_request.idl | 130 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunk_request_test.cpp | 255 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunk_request_type.cpp | 154 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunk_request_type.h | 115 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunk_request_valid.h | 44 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunks_request_test.cpp | 201 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunks_request_type.cpp | 147 | ||||
-rw-r--r-- | src/mongo/s/request_types/merge_chunks_request_type.h | 106 |
15 files changed, 593 insertions, 799 deletions
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js index 4618bb9320d..643cfafe03b 100644 --- a/jstests/auth/lib/commands_lib.js +++ b/jstests/auth/lib/commands_lib.js @@ -2934,7 +2934,7 @@ var authCommandsLib = { }, { testname: "_configsvrCommitChunkMerge", - command: {_configsvrCommitChunkMerge: "x.y"}, + command: {_configsvrCommitChunkMerge: "x.y", shard: "shard0000", collEpoch: ObjectId(), chunkBoundaries:[{a:1}, {a:5}, {a:10}]}, skipSharded: true, expectFail: true, testcases: [ @@ -2962,7 +2962,7 @@ var authCommandsLib = { }, { testname: "_configsvrCommitChunksMerge", - command: {_configsvrCommitChunksMerge: "x.y"}, + command: {_configsvrCommitChunksMerge: "x.y", shard: "shard0000", collUUID: {uuid: UUID()}, chunkRange: {min:{a:1}, max:{a:10}}}, skipSharded: true, expectFail: true, testcases: [ diff --git a/src/mongo/db/s/config/configsvr_merge_chunk_command.cpp b/src/mongo/db/s/config/configsvr_merge_chunk_command.cpp index 1d1b8b5f835..17e7fb7436c 100644 --- a/src/mongo/db/s/config/configsvr_merge_chunk_command.cpp +++ b/src/mongo/db/s/config/configsvr_merge_chunk_command.cpp @@ -40,7 +40,7 @@ #include "mongo/db/repl/read_concern_args.h" #include "mongo/db/s/config/sharding_catalog_manager.h" #include "mongo/s/grid.h" -#include "mongo/s/request_types/merge_chunk_request_type.h" +#include "mongo/s/request_types/merge_chunk_request_gen.h" #include "mongo/util/str.h" namespace mongo { @@ -62,13 +62,13 @@ namespace { * writeConcern: <BSONObj> * } */ -class ConfigSvrMergeChunkCommand : public BasicCommand { +class ConfigSvrMergeChunkCommand : public TypedCommand<ConfigSvrMergeChunkCommand> { public: - ConfigSvrMergeChunkCommand() : BasicCommand("_configsvrCommitChunkMerge") {} + using Request = ConfigSvrMergeChunk; std::string help() const override { return "Internal command, which is sent by a shard to the sharding config server. Do " - "not call directly. Receives, validates, and processes a MergeChunkRequest"; + "not call directly. Receives, validates, and processes a ConfigSvrMergeChunk"; } AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { @@ -79,49 +79,56 @@ public: return true; } - bool supportsWriteConcern(const BSONObj& cmd) const override { - return true; - } - - Status checkAuthForCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) const override { - if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( - ResourcePattern::forClusterResource(), ActionType::internal)) { - return Status(ErrorCodes::Unauthorized, "Unauthorized"); + class Invocation final : public InvocationBase { + public: + using InvocationBase::InvocationBase; + + ConfigSvrMergeResponse typedRun(OperationContext* opCtx) { + uassert(ErrorCodes::IllegalOperation, + "_configsvrCommitChunkMerge can only be run on config servers", + serverGlobalParams.clusterRole == ClusterRole::ConfigServer); + + uassert(ErrorCodes::InvalidNamespace, + "invalid namespace specified for request", + ns().isValid()); + uassert(ErrorCodes::InvalidOptions, + "need to provide at least three chunk boundaries for the chunks to be merged", + request().getChunkBoundaries().size() >= 3); + + // Set the operation context read concern level to local for reads into the config + // database. + repl::ReadConcernArgs::get(opCtx) = + repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern); + + const BSONObj shardAndCollVers = + uassertStatusOK(ShardingCatalogManager::get(opCtx)->commitChunkMerge( + opCtx, + ns(), + request().getEpoch(), + request().getChunkBoundaries(), + request().getShard().toString(), + request().getValidAfter())); + return ConfigSvrMergeResponse{ + ChunkVersion::fromBSONThrowing(shardAndCollVers["shardVersion"].Obj())}; } - return Status::OK(); - } - std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const override { - return CommandHelpers::parseNsFullyQualified(cmdObj); - } + private: + NamespaceString ns() const override { + return request().getCommandParameter(); + } - bool run(OperationContext* opCtx, - const std::string& dbName, - const BSONObj& cmdObj, - BSONObjBuilder& result) override { - uassert(ErrorCodes::IllegalOperation, - "_configsvrCommitChunkMerge can only be run on config servers", - serverGlobalParams.clusterRole == ClusterRole::ConfigServer); - - // Set the operation context read concern level to local for reads into the config database. - repl::ReadConcernArgs::get(opCtx) = - repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern); - - auto parsedRequest = uassertStatusOK(MergeChunkRequest::parseFromConfigCommand(cmdObj)); - - const BSONObj shardAndCollVers = uassertStatusOK( - ShardingCatalogManager::get(opCtx)->commitChunkMerge(opCtx, - parsedRequest.getNamespace(), - parsedRequest.getEpoch(), - parsedRequest.getChunkBoundaries(), - parsedRequest.getShardName(), - parsedRequest.getValidAfter())); - result.appendElements(shardAndCollVers); + bool supportsWriteConcern() const override { + return true; + } - return true; - } + void doCheckAuthorization(OperationContext* opCtx) const override { + if (!AuthorizationSession::get(opCtx->getClient()) + ->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::internal)) { + uasserted(ErrorCodes::Unauthorized, "Unauthorized"); + } + } + }; } configsvrMergeChunkCmd; diff --git a/src/mongo/db/s/config/configsvr_merge_chunks_command.cpp b/src/mongo/db/s/config/configsvr_merge_chunks_command.cpp index 73e4c127749..c41a366fe4b 100644 --- a/src/mongo/db/s/config/configsvr_merge_chunks_command.cpp +++ b/src/mongo/db/s/config/configsvr_merge_chunks_command.cpp @@ -30,7 +30,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/commands.h" #include "mongo/db/s/config/sharding_catalog_manager.h" -#include "mongo/s/request_types/merge_chunks_request_type.h" +#include "mongo/s/request_types/merge_chunk_request_gen.h" namespace mongo { namespace { @@ -48,13 +48,13 @@ namespace { * writeConcern: <BSONObj> * } */ -class ConfigSvrMergeChunksCommand : public BasicCommand { +class ConfigSvrMergeChunksCommand : public TypedCommand<ConfigSvrMergeChunksCommand> { public: - ConfigSvrMergeChunksCommand() : BasicCommand("_configsvrCommitChunksMerge") {} + using Request = ConfigSvrMergeChunks; std::string help() const override { return "Internal command, which is sent by a shard to the sharding config server. Do " - "not call directly. Receives, validates, and processes a MergeChunksRequest"; + "not call directly. Receives, validates, and processes a ConfigSvrMergeChunks"; } AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { @@ -65,50 +65,53 @@ public: return true; } - bool supportsWriteConcern(const BSONObj& cmd) const override { - return true; - } + class Invocation final : public InvocationBase { + public: + using InvocationBase::InvocationBase; - Status checkAuthForCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) const override { - if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( - ResourcePattern::forClusterResource(), ActionType::internal)) { - return Status(ErrorCodes::Unauthorized, "Unauthorized"); - } - return Status::OK(); - } + ConfigSvrMergeResponse typedRun(OperationContext* opCtx) { + uassert(ErrorCodes::IllegalOperation, + "_configsvrCommitChunksMerge can only be run on config servers", + serverGlobalParams.clusterRole == ClusterRole::ConfigServer); + uassert(ErrorCodes::InvalidNamespace, + "invalid namespace specified for request", + ns().isValid()); - std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const override { - return CommandHelpers::parseNsFullyQualified(cmdObj); - } - - bool run(OperationContext* opCtx, - const std::string& dbName, - const BSONObj& cmdObj, - BSONObjBuilder& result) override { - uassert(ErrorCodes::IllegalOperation, - "_configsvrCommitChunksMerge can only be run on config servers", - serverGlobalParams.clusterRole == ClusterRole::ConfigServer); + // Set the operation context read concern level to local for reads into the config + // database. + repl::ReadConcernArgs::get(opCtx) = + repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern); - // Set the operation context read concern level to local for reads into the config database. - repl::ReadConcernArgs::get(opCtx) = - repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern); + const BSONObj shardAndCollVers = uassertStatusOK( + ShardingCatalogManager::get(opCtx)->commitChunksMerge(opCtx, + ns(), + request().getCollectionUUID(), + request().getChunkRange(), + request().getShard(), + request().getValidAfter())); + return ConfigSvrMergeResponse{ + ChunkVersion::fromBSONThrowing(shardAndCollVers["shardVersion"].Obj())}; + } - auto parsedRequest = uassertStatusOK(MergeChunksRequest::parseFromConfigCommand(cmdObj)); + private: + NamespaceString ns() const override { + return request().getCommandParameter(); + } - const BSONObj shardAndCollVers = uassertStatusOK( - ShardingCatalogManager::get(opCtx)->commitChunksMerge(opCtx, - parsedRequest.getNamespace(), - parsedRequest.getCollectionUUID(), - parsedRequest.getChunkRange(), - parsedRequest.getShardId(), - parsedRequest.getValidAfter())); - result.appendElements(shardAndCollVers); + bool supportsWriteConcern() const override { + return true; + } - return true; - } + void doCheckAuthorization(OperationContext* opCtx) const override { + if (!AuthorizationSession::get(opCtx->getClient()) + ->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::internal)) { + uasserted(ErrorCodes::Unauthorized, "Unauthorized"); + } + } + }; } configsvrMergeChunksCmd; + } // namespace } // namespace mongo diff --git a/src/mongo/db/s/merge_chunks_command.cpp b/src/mongo/db/s/merge_chunks_command.cpp index f68afa19cac..2b3235113a2 100644 --- a/src/mongo/db/s/merge_chunks_command.cpp +++ b/src/mongo/db/s/merge_chunks_command.cpp @@ -48,8 +48,7 @@ #include "mongo/s/catalog/type_chunk.h" #include "mongo/s/client/shard_registry.h" #include "mongo/s/grid.h" -#include "mongo/s/request_types/merge_chunk_request_type.h" -#include "mongo/s/request_types/merge_chunks_request_type.h" +#include "mongo/s/request_types/merge_chunk_request_gen.h" #include "mongo/util/str.h" namespace mongo { @@ -83,20 +82,17 @@ Shard::CommandResponse commitUsingChunkRange(OperationContext* opCtx, const auto currentTime = VectorClock::get(opCtx)->getTime(); auto collUUID = metadata.getUUID(); invariant(collUUID); - MergeChunksRequest request{nss, - shardingState->shardId(), - *collUUID, - chunkRange, - currentTime.clusterTime().asTimestamp()}; - - auto configCmdObj = - request.toConfigCommandBSON(ShardingCatalogClient::kMajorityWriteConcern.toBSON()); + + ConfigSvrMergeChunks request{nss, shardingState->shardId(), *collUUID, chunkRange}; + request.setValidAfter(currentTime.clusterTime().asTimestamp()); + request.setWriteConcern(ShardingCatalogClient::kMajorityWriteConcern.toBSON()); + auto cmdResponse = uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getConfigShard()->runCommand( opCtx, ReadPreferenceSetting{ReadPreference::PrimaryOnly}, - "admin", - configCmdObj, + NamespaceString::kAdminDb.toString(), + request.toBSON(BSONObj()), Shard::RetryPolicy::kIdempotent)); return cmdResponse; @@ -191,20 +187,19 @@ Shard::CommandResponse commitUsingChunksList(OperationContext* opCtx, // Run _configsvrCommitChunkMerge. // const auto currentTime = VectorClock::get(opCtx)->getTime(); - MergeChunkRequest request{nss, - shardingState->shardId().toString(), - metadata.getShardVersion().epoch(), - chunkBoundaries, - currentTime.clusterTime().asTimestamp()}; - - auto configCmdObj = - request.toConfigCommandBSON(ShardingCatalogClient::kMajorityWriteConcern.toBSON()); + ConfigSvrMergeChunk request{nss, + shardingState->shardId().toString(), + metadata.getShardVersion().epoch(), + chunkBoundaries}; + request.setValidAfter(currentTime.clusterTime().asTimestamp()); + request.setWriteConcern(ShardingCatalogClient::kMajorityWriteConcern.toBSON()); + auto cmdResponse = uassertStatusOK(Grid::get(opCtx)->shardRegistry()->getConfigShard()->runCommand( opCtx, ReadPreferenceSetting{ReadPreference::PrimaryOnly}, - "admin", - configCmdObj, + NamespaceString::kAdminDb.toString(), + request.toBSON(BSONObj()), Shard::RetryPolicy::kIdempotent)); return cmdResponse; diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index fa5dfd08e64..471839f6c4d 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -163,8 +163,7 @@ env.Library( 'request_types/flush_resharding_state_change.idl', 'request_types/flush_routing_table_cache_updates.idl', 'request_types/get_database_version.idl', - 'request_types/merge_chunk_request_type.cpp', - 'request_types/merge_chunks_request_type.cpp', + 'request_types/merge_chunk_request.idl', 'request_types/migration_secondary_throttle_options.cpp', 'request_types/move_chunk_request.cpp', 'request_types/move_primary.idl', diff --git a/src/mongo/s/chunk_version.h b/src/mongo/s/chunk_version.h index 68f430ef026..8d0b393aff8 100644 --- a/src/mongo/s/chunk_version.h +++ b/src/mongo/s/chunk_version.h @@ -88,6 +88,13 @@ public: return uassertStatusOK(fromBSON(obj)); } + static ChunkVersion fromBSONArrayThrowing(const BSONElement& element) { + uassert(ErrorCodes::TypeMismatch, + "Invalid type for chunkVersion element. Expected an array", + element.type() == Array); + return fromBSONThrowing(element.Obj()); + } + /** * NOTE: This format should not be used. Use fromBSONThrowing instead. * @@ -230,6 +237,13 @@ public: BSONObj toBSON() const; /** + * Same as ChunkVersion::appendWithField adapted for IDL + */ + void serializeToBSON(StringData fieldName, BSONObjBuilder* builder) const { + appendWithField(builder, fieldName); + } + + /** * NOTE: This format serializes chunk version as a timestamp (without the epoch) for * legacy reasons. */ diff --git a/src/mongo/s/chunk_version.idl b/src/mongo/s/chunk_version.idl index ee33893468b..82ea4f5333c 100644 --- a/src/mongo/s/chunk_version.idl +++ b/src/mongo/s/chunk_version.idl @@ -34,6 +34,8 @@ global: - "mongo/s/chunk_version.h" types: + # serializes as { 0: <major/minor>, 1: <epoch>, 2: <timestamp> } + # equivalent to using ChunkVersion::toBSON / ChunkVersion::fromBSON ChunkVersion: bson_serialization_type: object description: An object representing a chunk version for a collection. diff --git a/src/mongo/s/request_types/merge_chunk_request.idl b/src/mongo/s/request_types/merge_chunk_request.idl new file mode 100644 index 00000000000..d30db73aa42 --- /dev/null +++ b/src/mongo/s/request_types/merge_chunk_request.idl @@ -0,0 +1,130 @@ +# Copyright (C) 2021-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. +# + +# _configsvrCommitChunkMerge / _configsvrCommitChunksMerge IDL File + +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/util/uuid.h" + - "mongo/s/chunk_version.h" + - "mongo/s/request_types/merge_chunk_request_valid.h" + +imports: + - "mongo/idl/basic_types.idl" + - "mongo/s/sharding_types.idl" + - "mongo/s/chunk_range.idl" + +types: + # Non-IDL response used UUIDtoBSON instead of UUID::toCDR + my_uuid: + bson_serialization_type: object + description: "A UUID for some reason not generic uuid type." + cpp_type: "mongo::UUID" + deserializer: "mongo::UUID::parse" + serializer: "mongo::UUID::toBSON" + + # serialize [<major/minor>, <epoch>, <timestamp>] + # equivalent to using ChunkVersion::appendToCommand / ChunkVersion::parseFromCommand + ChunkVersionArray: + bson_serialization_type: any + description: "An object representing a chunk version for a collection." + cpp_type: "ChunkVersion" + serializer: "ChunkVersion::serializeToBSON" + deserializer: "ChunkVersion::fromBSONArrayThrowing" + +structs: + ConfigSvrMergeResponse: + description: "Response of the _configsvrCommitChunkMerge command." + strict: false + fields: + shardVersion: + type: ChunkVersionArray + description: "Latest version of the shard." + +commands: + _configsvrCommitChunkMerge: + command_name: _configsvrCommitChunkMerge + cpp_name: "ConfigSvrMergeChunk" + description: "The internal _configsvrCommitChunkMerge command on the config server." + namespace: type + api_version: "" + type: namespacestring + strict: false + reply_type: ConfigSvrMergeResponse + fields: + shard: + type: shard_id + description: "String identifier for a shard's name" + collEpoch: + type: objectid + cpp_name: "epoch" + description: "Uniquely identifies this instance of the collection, in case of + drop/create or shard key refine." + chunkBoundaries: + type: array<object_owned> + description: "Chunk bounds to merge." + validator: { callback: 'chunkBoundsNotEmpty' } + validAfter: + type: timestamp + description: "The time after which this chunk is at this shard." + optional: true + writeConcern: + description: "The level of write concern for the creation operation" + type: object + optional: true + + _configsvrCommitChunksMerge: + command_name: _configsvrCommitChunksMerge + cpp_name: ConfigSvrMergeChunks + description: "The internal _configsvrCommitChunksMerge command on the config server." + namespace: type + api_version: "" + type: namespacestring + strict: false + reply_type: ConfigSvrMergeResponse + fields: + shard: + description: "The id of the shard." + type: shard_id + collUUID: + type: my_uuid + cpp_name: "collectionUUID" + description: "The UUID of the collection that the chunk belongs to." + chunkRange: + type: chunk_range + cpp_name: "chunkRange" + description: "Chunk bounds to merge." + validAfter: + type: timestamp + description: "The time after which this chunk is at this shard." + optional: true + writeConcern: + description: "The level of write concern for the creation operation." + type: object + optional: true diff --git a/src/mongo/s/request_types/merge_chunk_request_test.cpp b/src/mongo/s/request_types/merge_chunk_request_test.cpp index 88d5d165148..3c11bdc639c 100644 --- a/src/mongo/s/request_types/merge_chunk_request_test.cpp +++ b/src/mongo/s/request_types/merge_chunk_request_test.cpp @@ -30,7 +30,7 @@ #include "mongo/platform/basic.h" #include "mongo/bson/bsonobjbuilder.h" -#include "mongo/s/request_types/merge_chunk_request_type.h" +#include "mongo/s/request_types/merge_chunk_request_gen.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -38,29 +38,36 @@ namespace { using unittest::assertGet; -TEST(MergeChunkRequest, BasicValidConfigCommand) { - auto request = assertGet(MergeChunkRequest::parseFromConfigCommand( +IDLParserErrorContext ctx("_configsvrCommitChunkMerge"); + +TEST(ConfigSvrMergeChunk, BasicValidConfigCommand) { + auto request = ConfigSvrMergeChunk::parse( + ctx, BSON("_configsvrCommitChunkMerge" << "TestDB.TestColl" + << "shard" + << "shard0000" << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" - << "shard0000"))); - ASSERT_EQ(NamespaceString("TestDB", "TestColl"), request.getNamespace()); + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "$db" + << "admin")); + ASSERT_EQ(NamespaceString("TestDB", "TestColl"), request.getCommandParameter()); ASSERT_EQ(OID("7fffffff0000000000000001"), request.getEpoch()); ASSERT_BSONOBJ_EQ(BSON("a" << 1), request.getChunkBoundaries().at(0)); ASSERT_BSONOBJ_EQ(BSON("a" << 5), request.getChunkBoundaries().at(1)); ASSERT_BSONOBJ_EQ(BSON("a" << 10), request.getChunkBoundaries().at(2)); - ASSERT_EQ("shard0000", request.getShardName()); + ASSERT_EQ("shard0000", request.getShard().toString()); + ASSERT_EQ("admin", request.getDbName()); } -TEST(MergeChunkRequest, ConfigCommandtoBSON) { +TEST(ConfigSvrMergeChunk, ConfigCommandtoBSON) { BSONObj serializedRequest = BSON("_configsvrCommitChunkMerge" << "TestDB.TestColl" - << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" + << "shard" << "shard0000" - << "validAfter" << Timestamp{100}); + << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "validAfter" + << Timestamp{100}); BSONObj writeConcernObj = BSON("w" << "majority"); @@ -70,113 +77,179 @@ TEST(MergeChunkRequest, ConfigCommandtoBSON) { cmdBuilder.append("writeConcern", writeConcernObj); } - auto request = assertGet(MergeChunkRequest::parseFromConfigCommand(serializedRequest)); - auto requestToBSON = request.toConfigCommandBSON(writeConcernObj); + auto appendDB = [](const BSONObj& obj) { + BSONObjBuilder builder; + builder.appendElements(obj); + builder.append("$db", "admin"); + return builder.obj(); + }; + + auto request = ConfigSvrMergeChunk::parse(ctx, appendDB(serializedRequest)); + request.setWriteConcern(writeConcernObj); + auto requestToBSON = request.toBSON(BSONObj()); ASSERT_BSONOBJ_EQ(cmdBuilder.obj(), requestToBSON); } -TEST(MergeChunkRequest, MissingNameSpaceErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand(BSON( - "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); -} +TEST(ConfigSvrMergeChunk, MissingNameSpaceErrors) { + ASSERT_THROWS_CODE( -TEST(MergeChunkRequest, MissingCollEpochErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunkMerge" - << "TestDB.TestColl" - << "chunkBoundaries" << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) - << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); + ConfigSvrMergeChunk::parse( + ctx, + BSON("collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) + << "shard" + << "shard0000" + << "$db" + << "admin")), + mongo::DBException, + 40414); } -TEST(MergeChunkRequest, MissingChunkBoundariesErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunkMerge" - << "TestDB.TestColl" - << "collEpoch" << OID("7fffffff0000000000000001") << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); +TEST(ConfigSvrMergeChunk, MissingCollEpochErrors) { + ASSERT_THROWS_CODE( + ConfigSvrMergeChunk::parse( + ctx, + BSON("_configsvrCommitChunkMerge" + << "TestDB.TestColl" + << "chunkBoundaries" + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" + << "shard0000" + << "$db" + << "admin")), + mongo::DBException, + 40414); } -TEST(MergeChunkRequest, MissingShardNameErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunkMerge" - << "TestDB.TestColl" - << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)))); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); +TEST(ConfigSvrMergeChunk, MissingChunkBoundariesErrors) { + ASSERT_THROWS_CODE( + + ConfigSvrMergeChunk::parse(ctx, + BSON("_configsvrCommitChunkMerge" + << "TestDB.TestColl" + << "collEpoch" << OID("7fffffff0000000000000001") << "shard" + << "shard0000" + << "$db" + << "admin")), + mongo::DBException, + 40414); } -TEST(MergeChunkRequest, WrongNamespaceTypeErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunkMerge" - << 1234 << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); +TEST(ConfigSvrMergeChunk, MissingShardNameErrors) { + ASSERT_THROWS_CODE( + + ConfigSvrMergeChunk::parse( + ctx, + BSON("_configsvrCommitChunkMerge" + << "TestDB.TestColl" + << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "$db" + << "admin")), + mongo::DBException, + 40414); } -TEST(MergeChunkRequest, WrongCollEpochTypeErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunkMerge" - << "TestDB.TestColl" - << "collEpoch" << 1234 << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); +TEST(ConfigSvrMergeChunk, WrongNamespaceTypeErrors) { + ASSERT_THROWS_CODE( + + ConfigSvrMergeChunk::parse( + ctx, + BSON("_configsvrCommitChunkMerge" + << 1234 << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" + << "shard0000" + << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } -TEST(MergeChunkRequest, WrongChunkBoundariesTypeErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand(BSON( - "_configsvrCommitChunkMerge" - << "TestDB.TestColl" - << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" << 1234 << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); + +TEST(ConfigSvrMergeChunk, WrongCollEpochTypeErrors) { + ASSERT_THROWS_CODE( + + ConfigSvrMergeChunk::parse( + ctx, + BSON("_configsvrCommitChunkMerge" + << "TestDB.TestColl" + << "collEpoch" << 1234 << "chunkBoundaries" + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" + << "shard0000" + << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } -TEST(MergeChunkRequest, WrongShardNameTypeErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand(BSON( - "_configsvrCommitChunkMerge" - << "TestDB.TestColl" - << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" << 1234)); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); +TEST(ConfigSvrMergeChunk, WrongChunkBoundariesTypeErrors) { + ASSERT_THROWS_CODE( + + ConfigSvrMergeChunk::parse(ctx, + BSON("_configsvrCommitChunkMerge" + << "TestDB.TestColl" + << "collEpoch" << OID("7fffffff0000000000000001") + << "chunkBoundaries" << 1234 << "shard" + << "shard0000" + << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } -TEST(MergeChunkRequest, InvalidNamespaceErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunkMerge" - << "" - << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" - << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::InvalidNamespace, request.getStatus()); +TEST(ConfigSvrMergeChunk, WrongShardNameTypeErrors) { + ASSERT_THROWS_CODE( + + ConfigSvrMergeChunk::parse( + ctx, + BSON("_configsvrCommitChunkMerge" + << "TestDB.TestColl" + << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" + << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" + << 1234 << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } -TEST(MergeChunkRequest, EmptyChunkBoundariesErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunkMerge" - << "TestDB.TestColl" - << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" << BSONArray() - << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::InvalidOptions, request.getStatus()); +//// IDL validators do not work on command value +// TEST(ConfigSvrMergeChunk, InvalidNamespaceErrors) { +// auto request = MergeChunkRequest::parseFromConfigBSONCommand( +// BSON("_configsvrCommitChunkMerge" +// << "" +// << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" +// << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)) << "shard" +// << "shard0000" << "$db" << "admin")); +// ASSERT_EQ(ErrorCodes::InvalidNamespace, request.getStatus()); +// } + +TEST(ConfigSvrMergeChunk, EmptyChunkBoundariesErrors) { + auto req = ConfigSvrMergeChunk::parse(ctx, + BSON("_configsvrCommitChunkMerge" + << "TestDB.TestColl" + << "collEpoch" << OID("7fffffff0000000000000001") + << "chunkBoundaries" << BSONArray() << "shard" + << "shard0000" + << "$db" + << "admin")); + // trigger validator (bit useless) + ASSERT_THROWS_CODE( + req.setChunkBoundaries(req.getChunkBoundaries()), DBException, ErrorCodes::InvalidOptions); } -TEST(MergeChunkRequest, TooFewChunkBoundariesErrors) { - auto request = MergeChunkRequest::parseFromConfigCommand( +TEST(ConfigSvrMergeChunk, TooFewChunkBoundariesErrors) { + auto req = ConfigSvrMergeChunk::parse( + ctx, BSON("_configsvrCommitChunkMerge" << "TestDB.TestColl" << "collEpoch" << OID("7fffffff0000000000000001") << "chunkBoundaries" << BSON_ARRAY(BSON("a" << 1) << BSON("a" << 10)) << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::InvalidOptions, request.getStatus()); + << "shard0000" + << "$db" + << "admin")); + // trigger validator (bit useless) + + ASSERT_THROWS_CODE( + req.setChunkBoundaries(req.getChunkBoundaries()), DBException, ErrorCodes::InvalidOptions); } } // namespace diff --git a/src/mongo/s/request_types/merge_chunk_request_type.cpp b/src/mongo/s/request_types/merge_chunk_request_type.cpp deleted file mode 100644 index 387bacc16b0..00000000000 --- a/src/mongo/s/request_types/merge_chunk_request_type.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include "mongo/platform/basic.h" - -#include "mongo/s/request_types/merge_chunk_request_type.h" - -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/bson/util/bson_extract.h" -#include "mongo/db/write_concern_options.h" - -namespace mongo { -namespace { - -const char kConfigsvrMergeChunk[] = "_configsvrCommitChunkMerge"; -const char kCollEpoch[] = "collEpoch"; -const char kChunkBoundaries[] = "chunkBoundaries"; -const char kShardName[] = "shard"; -const char kValidAfter[] = "validAfter"; -} // namespace - -MergeChunkRequest::MergeChunkRequest(NamespaceString nss, - std::string shardName, - OID epoch, - std::vector<BSONObj> chunkBoundaries, - boost::optional<Timestamp> validAfter) - : _nss(std::move(nss)), - _epoch(std::move(epoch)), - _chunkBoundaries(std::move(chunkBoundaries)), - _shardName(std::move(shardName)), - _validAfter(validAfter) {} - -StatusWith<MergeChunkRequest> MergeChunkRequest::parseFromConfigCommand(const BSONObj& cmdObj) { - std::string ns; - { - auto parseNamespaceStatus = bsonExtractStringField(cmdObj, kConfigsvrMergeChunk, &ns); - if (!parseNamespaceStatus.isOK()) { - return parseNamespaceStatus; - } - } - - NamespaceString nss(ns); - if (!nss.isValid()) { - return {ErrorCodes::InvalidNamespace, - str::stream() << "invalid namespace '" << nss.ns() << "' specified for request"}; - } - - OID epoch; - { - auto parseEpochStatus = bsonExtractOIDField(cmdObj, kCollEpoch, &epoch); - if (!parseEpochStatus.isOK()) { - return parseEpochStatus; - } - } - - std::vector<BSONObj> chunkBoundaries; - { - BSONElement chunkBoundariesElem; - auto chunkBoundariesElemStatus = - bsonExtractTypedField(cmdObj, kChunkBoundaries, mongo::Array, &chunkBoundariesElem); - - if (!chunkBoundariesElemStatus.isOK()) { - return chunkBoundariesElemStatus; - } - BSONObjIterator it(chunkBoundariesElem.Obj()); - while (it.more()) { - chunkBoundaries.push_back(it.next().Obj().getOwned()); - } - - if (chunkBoundaries.size() < 3) { - return {ErrorCodes::InvalidOptions, - "need to provide at least three chunk boundaries for the chunks to be merged"}; - } - } - - std::string shardName; - { - auto parseShardNameStatus = bsonExtractStringField(cmdObj, kShardName, &shardName); - if (!parseShardNameStatus.isOK()) { - return parseShardNameStatus; - } - } - - boost::optional<Timestamp> validAfter = boost::none; - { - Timestamp ts{0}; - auto status = bsonExtractTimestampField(cmdObj, kValidAfter, &ts); - if (!status.isOK() && status != ErrorCodes::NoSuchKey) { - return status; - } - - if (status.isOK()) { - validAfter = ts; - } - } - - return MergeChunkRequest(std::move(nss), - std::move(shardName), - std::move(epoch), - std::move(chunkBoundaries), - validAfter); -} - -BSONObj MergeChunkRequest::toConfigCommandBSON(const BSONObj& writeConcern) { - BSONObjBuilder cmdBuilder; - appendAsConfigCommand(&cmdBuilder); - - // Tack on passed-in writeConcern - cmdBuilder.append(WriteConcernOptions::kWriteConcernField, writeConcern); - - return cmdBuilder.obj(); -} - -void MergeChunkRequest::appendAsConfigCommand(BSONObjBuilder* cmdBuilder) { - cmdBuilder->append(kConfigsvrMergeChunk, _nss.ns()); - cmdBuilder->append(kCollEpoch, _epoch); - { - BSONArrayBuilder chunkBoundariesArray(cmdBuilder->subarrayStart(kChunkBoundaries)); - for (const auto& chunkBoundary : _chunkBoundaries) { - chunkBoundariesArray.append(chunkBoundary); - } - } - cmdBuilder->append(kShardName, _shardName); - invariant(_validAfter.is_initialized()); - cmdBuilder->append(kValidAfter, _validAfter.get()); -} - -} // namespace mongo diff --git a/src/mongo/s/request_types/merge_chunk_request_type.h b/src/mongo/s/request_types/merge_chunk_request_type.h deleted file mode 100644 index 38d2a9f90db..00000000000 --- a/src/mongo/s/request_types/merge_chunk_request_type.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <vector> - -#include "mongo/base/status_with.h" - -#include "mongo/db/jsobj.h" -#include "mongo/db/namespace_string.h" - -namespace mongo { - -/** - * Provides support for parsing and serialization of arguments to the config server mergeChunk - * command. - */ -class MergeChunkRequest { -public: - MergeChunkRequest(NamespaceString nss, - std::string shardName, - OID epoch, - std::vector<BSONObj> chunkBoundaries, - boost::optional<Timestamp> validAfter); - - /** - * Parses the provided BSON content as the internal _configsvrCommitChunkMerge command, and if - * it contains the correct types, constructs a MergeChunkRequest object from it. - * - * { - * _configsvrCommitChunkMerge: <NamespaceString nss>, - * collEpoch: <OID epoch>, - * chunkBoundaries: [ - * <BSONObj key1>, - * <BSONObj key2>, - * ... - * ], - * shard: <string shard> - * } - */ - static StatusWith<MergeChunkRequest> parseFromConfigCommand(const BSONObj& cmdObj); - - /** - * Creates a BSONObjBuilder and uses it to create and return a BSONObj from this - * MergeChunkRequest instance. Calls appendAsConfigCommand and tacks on the passed-in - * writeConcern. - */ - BSONObj toConfigCommandBSON(const BSONObj& writeConcern); - - /** - * Creates a serialized BSONObj of the internal _configsvCommitChunkMerge command from this - * MergeChunkRequest instance. - */ - void appendAsConfigCommand(BSONObjBuilder* cmdBuilder); - - const NamespaceString& getNamespace() const { - return _nss; - } - - const OID& getEpoch() const { - return _epoch; - } - - const std::vector<BSONObj>& getChunkBoundaries() const { - return _chunkBoundaries; - } - - const std::string& getShardName() const { - return _shardName; - } - - const boost::optional<Timestamp>& getValidAfter() const { - return _validAfter; - } - -private: - NamespaceString _nss; - OID _epoch; - - // The boundaries of the chunks to be merged. - std::vector<BSONObj> _chunkBoundaries; - - std::string _shardName; - - boost::optional<Timestamp> _validAfter; -}; - -} // namespace mongo diff --git a/src/mongo/s/request_types/merge_chunk_request_valid.h b/src/mongo/s/request_types/merge_chunk_request_valid.h new file mode 100644 index 00000000000..8929f9165bb --- /dev/null +++ b/src/mongo/s/request_types/merge_chunk_request_valid.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2021-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include <string> +#include <vector> + +#include "mongo/bson/bsonobj.h" + +namespace mongo { + +inline Status chunkBoundsNotEmpty(const std::vector<BSONObj>& values) { + if (values.size() < 3) { + return {ErrorCodes::InvalidOptions, + "need to provide at least three chunk boundaries for the chunks to be merged"}; + } + return Status::OK(); +} +} // namespace mongo diff --git a/src/mongo/s/request_types/merge_chunks_request_test.cpp b/src/mongo/s/request_types/merge_chunks_request_test.cpp index 2b5e48ba9f2..b97114ae94c 100644 --- a/src/mongo/s/request_types/merge_chunks_request_test.cpp +++ b/src/mongo/s/request_types/merge_chunks_request_test.cpp @@ -30,8 +30,9 @@ #include "mongo/platform/basic.h" #include "mongo/bson/bsonobjbuilder.h" -#include "mongo/s/request_types/merge_chunks_request_type.h" +#include "mongo/s/request_types/merge_chunk_request_gen.h" #include "mongo/unittest/unittest.h" +#include "mongo/util/assert_util.h" namespace mongo { namespace { @@ -39,28 +40,32 @@ namespace { using unittest::assertGet; ChunkRange chunkRange(BSON("a" << 1), BSON("a" << 10)); +IDLParserErrorContext ctx("_configsvrCommitChunksMerge"); -TEST(MergeChunksRequest, BasicValidConfigCommand) { +TEST(ConfigSvrMergeChunks, BasicValidConfigCommand) { auto collUUID = UUID::gen(); - auto request = assertGet(MergeChunksRequest::parseFromConfigCommand( + auto request = ConfigSvrMergeChunks::parse( + ctx, BSON("_configsvrCommitChunksMerge" << "TestDB.TestColl" << "collUUID" << collUUID.toBSON() << "chunkRange" << chunkRange.toBSON() << "shard" - << "shard0000"))); - ASSERT_EQ(NamespaceString("TestDB", "TestColl"), request.getNamespace()); + << "shard0000" + << "$db" + << "admin")); + ASSERT_EQ(NamespaceString("TestDB", "TestColl"), request.getCommandParameter()); ASSERT_TRUE(collUUID == request.getCollectionUUID()); ASSERT_TRUE(chunkRange == request.getChunkRange()); - ASSERT_EQ("shard0000", request.getShardId().toString()); + ASSERT_EQ("shard0000", request.getShard().toString()); } -TEST(MergeChunksRequest, ConfigCommandtoBSON) { +TEST(ConfigSvrMergeChunks, ConfigCommandtoBSON) { auto collUUID = UUID::gen(); - BSONObj serializedRequest = - BSON("_configsvrCommitChunksMerge" - << "TestDB.TestColl" - << "collUUID" << collUUID.toBSON() << "chunkRange" << chunkRange.toBSON() << "shard" - << "shard0000" - << "validAfter" << Timestamp{100}); + BSONObj serializedRequest = BSON("_configsvrCommitChunksMerge" + << "TestDB.TestColl" + << "shard" + << "shard0000" + << "collUUID" << collUUID.toBSON() << "chunkRange" + << chunkRange.toBSON() << "validAfter" << Timestamp{100}); BSONObj writeConcernObj = BSON("w" << "majority"); @@ -70,95 +75,139 @@ TEST(MergeChunksRequest, ConfigCommandtoBSON) { cmdBuilder.append("writeConcern", writeConcernObj); } - auto request = assertGet(MergeChunksRequest::parseFromConfigCommand(serializedRequest)); - auto requestToBSON = request.toConfigCommandBSON(writeConcernObj); + auto appendDB = [](const BSONObj& obj) { + BSONObjBuilder builder; + builder.appendElements(obj); + builder.append("$db", "admin"); + return builder.obj(); + }; + + auto request = ConfigSvrMergeChunks::parse(ctx, appendDB(serializedRequest)); + request.setWriteConcern(writeConcernObj); + auto requestToBSON = request.toBSON(BSONObj()); ASSERT_BSONOBJ_EQ(cmdBuilder.obj(), requestToBSON); } -TEST(MergeChunksRequest, MissingNameSpaceErrors) { +TEST(ConfigSvrMergeChunks, MissingNameSpaceErrors) { auto collUUID = UUID::gen(); - auto request = MergeChunksRequest::parseFromConfigCommand( - BSON("collUUID" << collUUID.toBSON() << "chunkRange" << chunkRange.toBSON() << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); + ASSERT_THROWS_CODE( + ConfigSvrMergeChunks::parse(ctx, + BSON("collUUID" << collUUID.toBSON() << "chunkRange" + << chunkRange.toBSON() << "shard" + << "shard0000" + << "$db" + << "admin")), + mongo::DBException, + 40414); } -TEST(MergeChunksRequest, MissingcollUUIDErrors) { - auto request = MergeChunksRequest::parseFromConfigCommand(BSON("_configsvrCommitChunksMerge" - << "TestDB.TestColl" - << "chunkRange" - << chunkRange.toBSON() << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); +TEST(ConfigSvrMergeChunks, MissingcollUUIDErrors) { + ASSERT_THROWS_CODE( + ConfigSvrMergeChunks::parse(ctx, + BSON("_configsvrCommitChunksMerge" + << "TestDB.TestColl" + << "chunkRange" << chunkRange.toBSON() << "shard" + << "shard0000" + << "$db" + << "admin")), + mongo::DBException, + 40414); + // ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); } -TEST(MergeChunksRequest, MissingChunkRangeErrors) { +TEST(ConfigSvrMergeChunks, MissingChunkRangeErrors) { auto collUUID = UUID::gen(); - auto request = MergeChunksRequest::parseFromConfigCommand(BSON("_configsvrCommitChunksMerge" - << "TestDB.TestColl" - << "collUUID" - << collUUID.toBSON() << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); + ASSERT_THROWS_CODE( + ConfigSvrMergeChunks::parse(ctx, + BSON("_configsvrCommitChunksMerge" + << "TestDB.TestColl" + << "collUUID" << collUUID.toBSON() << "shard" + << "shard0000" + << "$db" + << "admin")), + DBException, + 40414); } -TEST(MergeChunksRequest, MissingShardIdErrors) { +TEST(ConfigSvrMergeChunks, MissingShardIdErrors) { auto collUUID = UUID::gen(); - auto request = MergeChunksRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunksMerge" - << "TestDB.TestColl" - << "collUUID" << collUUID.toBSON() << "chunkRange" << chunkRange.toBSON())); - ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus()); + ASSERT_THROWS_CODE( + ConfigSvrMergeChunks::parse(ctx, + BSON("_configsvrCommitChunksMerge" + << "TestDB.TestColl" + << "collUUID" << collUUID.toBSON() << "chunkRange" + << chunkRange.toBSON() << "$db" + << "admin")), + DBException, + 40414); } -TEST(MergeChunksRequest, WrongNamespaceTypeErrors) { +TEST(ConfigSvrMergeChunks, WrongNamespaceTypeErrors) { auto collUUID = UUID::gen(); - auto request = MergeChunksRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunksMerge" << 1234 << "collUUID" << collUUID.toBSON() - << "chunkRange" << chunkRange.toBSON() << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); + ASSERT_THROWS_CODE( + ConfigSvrMergeChunks::parse(ctx, + BSON("_configsvrCommitChunksMerge" + << 1234 << "collUUID" << collUUID.toBSON() << "chunkRange" + << chunkRange.toBSON() << "shard" + << "shard0000" + << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } -TEST(MergeChunksRequest, WrongcollUUIDTypeErrors) { - auto request = MergeChunksRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunksMerge" - << "TestDB.TestColl" - << "collUUID" << 1234 << "chunkRange" << chunkRange.toBSON() << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); +TEST(ConfigSvrMergeChunks, WrongcollUUIDTypeErrors) { + ASSERT_THROWS_CODE(ConfigSvrMergeChunks::parse(ctx, + BSON("_configsvrCommitChunksMerge" + << "TestDB.TestColl" + << "collUUID" << 1234 << "chunkRange" + << chunkRange.toBSON() << "shard" + << "shard0000" + << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } -TEST(MergeChunksRequest, WrongChunkRangeTypeErrors) { - auto collUUID = UUID::gen(); - auto request = MergeChunksRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunksMerge" - << "TestDB.TestColl" - << "collUUID" << collUUID.toBSON() << "chunkRange" << 1234 << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); -} -TEST(MergeChunksRequest, WrongShardIdTypeErrors) { +TEST(ConfigSvrMergeChunks, WrongChunkRangeTypeErrors) { auto collUUID = UUID::gen(); - auto request = MergeChunksRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunksMerge" - << "TestDB.TestColl" - << "collUUID" << collUUID.toBSON() << "chunkRange" << chunkRange.toBSON() << "shard" - << 1234)); - ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus()); + ASSERT_THROWS_CODE(ConfigSvrMergeChunks::parse(ctx, + BSON("_configsvrCommitChunksMerge" + << "TestDB.TestColl" + << "collUUID" << collUUID.toBSON() + << "chunkRange" << 1234 << "shard" + << "shard0000" + << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } -TEST(MergeChunksRequest, InvalidNamespaceErrors) { +TEST(ConfigSvrMergeChunks, WrongShardIdTypeErrors) { auto collUUID = UUID::gen(); - auto request = MergeChunksRequest::parseFromConfigCommand( - BSON("_configsvrCommitChunksMerge" - << "" - << "collUUID" << collUUID.toBSON() << "chunkRange" << chunkRange.toBSON() << "shard" - << "shard0000")); - ASSERT_EQ(ErrorCodes::InvalidNamespace, request.getStatus()); + ASSERT_THROWS_CODE( + ConfigSvrMergeChunks::parse(ctx, + BSON("_configsvrCommitChunksMerge" + << "TestDB.TestColl" + << "collUUID" << collUUID.toBSON() << "chunkRange" + << chunkRange.toBSON() << "shard" << 1234 << "$db" + << "admin")), + DBException, + ErrorCodes::TypeMismatch); } +//// IDL validators do not work on command value +// TEST(ConfigSvrMergeChunks, InvalidNamespaceErrors) { +// ASSERT_THROWS_CODE({ +// auto collUUID = UUID::gen(); +// ConfigSvrMergeChunks::parse(ctx, +// BSON("_configsvrCommitChunksMerge" << "" +// << "collUUID" << collUUID.toBSON() << "chunkRange" << chunkRange.toBSON() << "shard" +// << "shard0000" << "$db" << "admin")); +// }, DBException, ErrorCodes::InvalidNamespace); +// } + } // namespace } // namespace mongo diff --git a/src/mongo/s/request_types/merge_chunks_request_type.cpp b/src/mongo/s/request_types/merge_chunks_request_type.cpp deleted file mode 100644 index 1ea06a036fb..00000000000 --- a/src/mongo/s/request_types/merge_chunks_request_type.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright (C) 2021-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include "mongo/s/request_types/merge_chunks_request_type.h" -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/bson/util/bson_extract.h" -#include "mongo/db/write_concern_options.h" -#include "mongo/platform/basic.h" - -namespace mongo { -namespace { - -const char kConfigsvrMergeChunks[] = "_configsvrCommitChunksMerge"; -const char kCollUUID[] = "collUUID"; -const char kChunkRange[] = "chunkRange"; -const char kShardId[] = "shard"; -const char kValidAfter[] = "validAfter"; -} // namespace - -MergeChunksRequest::MergeChunksRequest(NamespaceString nss, - ShardId shardId, - UUID collectionUUID, - ChunkRange chunkRange, - boost::optional<Timestamp> validAfter) - : _nss(std::move(nss)), - _collectionUUID(std::move(collectionUUID)), - _chunkRange(std::move(chunkRange)), - _shardId(std::move(shardId)), - _validAfter(validAfter) {} - -StatusWith<MergeChunksRequest> MergeChunksRequest::parseFromConfigCommand(const BSONObj& cmdObj) { - std::string ns; - { - auto parseNamespaceStatus = bsonExtractStringField(cmdObj, kConfigsvrMergeChunks, &ns); - if (!parseNamespaceStatus.isOK()) { - return parseNamespaceStatus; - } - } - NamespaceString nss(ns); - if (!nss.isValid()) { - return {ErrorCodes::InvalidNamespace, - str::stream() << "invalid namespace '" << nss.ns() << "' specified for request"}; - } - - BSONElement collUUIDElem; - { - auto parseCollUUIDStatus = - bsonExtractTypedField(cmdObj, kCollUUID, mongo::Object, &collUUIDElem); - if (!parseCollUUIDStatus.isOK()) { - return parseCollUUIDStatus; - } - } - - auto collUUID = UUID::parse(collUUIDElem.Obj().getField("uuid")); - if (!collUUID.isOK()) { - return collUUID.getStatus(); - } - - BSONElement chunkRangeElem; - { - auto chunkRangeStatus = - bsonExtractTypedField(cmdObj, kChunkRange, mongo::Object, &chunkRangeElem); - if (!chunkRangeStatus.isOK()) { - return chunkRangeStatus; - } - } - - auto chunkRange = ChunkRange::fromBSON(chunkRangeElem.Obj().getOwned()); - if (!chunkRange.isOK()) { - return chunkRange.getStatus(); - } - - std::string shardIdString; - { - auto parseShardIdStatus = bsonExtractStringField(cmdObj, kShardId, &shardIdString); - if (!parseShardIdStatus.isOK()) { - return parseShardIdStatus; - } - } - - boost::optional<Timestamp> validAfter = boost::none; - { - Timestamp ts{0}; - auto status = bsonExtractTimestampField(cmdObj, kValidAfter, &ts); - if (!status.isOK() && status != ErrorCodes::NoSuchKey) { - return status; - } - - if (status.isOK()) { - validAfter = ts; - } - } - - return MergeChunksRequest(std::move(nss), - ShardId(shardIdString), - std::move(collUUID.getValue()), - std::move(chunkRange.getValue()), - validAfter); -} - -BSONObj MergeChunksRequest::toConfigCommandBSON(const BSONObj& writeConcern) { - BSONObjBuilder cmdBuilder; - appendAsConfigCommand(&cmdBuilder); - - // Tack on passed-in writeConcern - cmdBuilder.append(WriteConcernOptions::kWriteConcernField, writeConcern); - - return cmdBuilder.obj(); -} - -void MergeChunksRequest::appendAsConfigCommand(BSONObjBuilder* cmdBuilder) { - cmdBuilder->append(kConfigsvrMergeChunks, _nss.ns()); - cmdBuilder->append(kCollUUID, _collectionUUID.toBSON()); - cmdBuilder->append(kChunkRange, _chunkRange.toBSON()); - cmdBuilder->append(kShardId, _shardId); - invariant(_validAfter.is_initialized()); - cmdBuilder->append(kValidAfter, _validAfter.get()); -} - - -} // namespace mongo diff --git a/src/mongo/s/request_types/merge_chunks_request_type.h b/src/mongo/s/request_types/merge_chunks_request_type.h deleted file mode 100644 index bed10c7f338..00000000000 --- a/src/mongo/s/request_types/merge_chunks_request_type.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (C) 2021-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * <http://www.mongodb.com/licensing/server-side-public-license>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include "mongo/db/namespace_string.h" -#include "mongo/s/catalog/type_chunk.h" - -namespace mongo { - -/** - * Provides support for parsing and serialization of arguments to the config server mergeChunks - * command. - */ -class MergeChunksRequest { -public: - MergeChunksRequest(NamespaceString nss, - ShardId shardId, - UUID collUUID, - ChunkRange chunkRange, - boost::optional<Timestamp> validAfter); - /** - * Parses the provided BSON content as the internal _configsvrCommitChunksMerge command - * and if it contains the correct types, constructs a MergeChunksRequest object from it. - * - * { - * _configsvrCommitChunksMerge: <NamespaceString nss>, - * collUUID: <UUID>, - * chunkRage: <ChunkRange [minKey, maxKey)>, - * shard: <string shard> - * } - */ - static StatusWith<MergeChunksRequest> parseFromConfigCommand(const BSONObj& cmdObj); - - /** - * Creates a BSONObjBuilder and uses it to create and return a BSONObj from this - * MergeChunksRequest instance. Calls appendAsConfigCommand and tacks on the passed-in - * writeConcern. - */ - BSONObj toConfigCommandBSON(const BSONObj& writeConcern); - - /** - * Creates a serialized BSONObj of the internal _configsvCommitChunksMerge command - * from this MergeChunksRequest instance. - */ - void appendAsConfigCommand(BSONObjBuilder* cmdBuilder); - - const NamespaceString& getNamespace() const { - return _nss; - } - - const UUID& getCollectionUUID() const { - return _collectionUUID; - } - - const ChunkRange& getChunkRange() const { - return _chunkRange; - } - - const ShardId& getShardId() const { - return _shardId; - } - - const boost::optional<Timestamp>& getValidAfter() const { - return _validAfter; - } - -private: - NamespaceString _nss; - - UUID _collectionUUID; - - ChunkRange _chunkRange; - - ShardId _shardId; - - boost::optional<Timestamp> _validAfter; -}; - -} // namespace mongo |