summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeon Zaruvinsky <leon@mongodb.com>2016-07-06 16:13:58 -0400
committerLeon Zaruvinsky <leon@mongodb.com>2016-07-19 13:52:55 -0400
commite9201e5c46124472b941a1932dc8827a36487e5e (patch)
tree2a4bac27a738170248833a6b53969f37277b6a35
parent9ce36539e7b5aef8078a0f289ce4c99091df9b51 (diff)
downloadmongo-e9201e5c46124472b941a1932dc8827a36487e5e.tar.gz
SERVER-24488 Move logic to serialize and deserialize CommitChunkMigration to a CommitChunkMigrationRequest class
-rw-r--r--src/mongo/db/s/config/configsvr_commit_chunk_migration_command.cpp115
-rw-r--r--src/mongo/db/s/migration_source_manager.cpp37
-rw-r--r--src/mongo/s/SConscript2
-rw-r--r--src/mongo/s/request_types/commit_chunk_migration_request_test.cpp108
-rw-r--r--src/mongo/s/request_types/commit_chunk_migration_request_type.cpp149
-rw-r--r--src/mongo/s/request_types/commit_chunk_migration_request_type.h102
6 files changed, 408 insertions, 105 deletions
diff --git a/src/mongo/db/s/config/configsvr_commit_chunk_migration_command.cpp b/src/mongo/db/s/config/configsvr_commit_chunk_migration_command.cpp
index 345299ff0b7..72760cdda65 100644
--- a/src/mongo/db/s/config/configsvr_commit_chunk_migration_command.cpp
+++ b/src/mongo/db/s/config/configsvr_commit_chunk_migration_command.cpp
@@ -28,7 +28,6 @@
#include "mongo/platform/basic.h"
-#include "mongo/bson/util/bson_extract.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/commands.h"
#include "mongo/db/concurrency/d_concurrency.h"
@@ -41,6 +40,7 @@
#include "mongo/s/chunk_version.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/grid.h"
+#include "mongo/s/request_types/commit_chunk_migration_request_type.h"
namespace mongo {
@@ -111,43 +111,11 @@ public:
return parseNsFullyQualified(dbname, cmdObj);
}
- /**
- * Returns a BSON subobject from "field" in "source".
- */
- StatusWith<BSONObj> getBSONField(const BSONObj& source, StringData field) {
- BSONElement fieldElement;
- Status status = bsonExtractTypedField(source, field, BSONType::Object, &fieldElement);
- if (!status.isOK()) {
- return status;
- }
-
- return fieldElement.Obj().getOwned();
- }
-
- /**
- * Parses a string value from "field" in "source" and returns it.
- */
- static std::string extractString(const BSONObj source, StringData field) {
- std::string stringResult;
- Status status = bsonExtractStringField(source, field, &stringResult);
- uassert(status.code(),
- str::stream() << "Failed to parse '" << field << "' field from BSON '"
- << source.jsonString()
- << "'"
- << causedBy(status),
- status.isOK());
- uassert(status.code(),
- str::stream() << "'" << field << "' field in BSON '" << source.jsonString()
- << "' is empty",
- stringResult.length() > 0);
- return stringResult;
- }
-
static void checkChunkIsOnShard(OperationContext* txn,
const NamespaceString& nss,
const BSONObj& min,
const BSONObj& max,
- StringData shard) {
+ const ShardId& shard) {
BSONObj chunkQuery =
BSON(ChunkType::ns() << nss.ns() << ChunkType::min() << min << ChunkType::max() << max
<< ChunkType::shard()
@@ -173,8 +141,8 @@ public:
BSONObj makeCommitChunkApplyOpsCommand(
const NamespaceString& nss,
- const StatusWith<ChunkRange>& migratedChunkRange,
- const boost::optional<StatusWith<ChunkRange>>& controlChunkRange,
+ const ChunkRange& migratedChunkRange,
+ const boost::optional<ChunkRange>& controlChunkRange,
const ChunkVersion newMigratedChunkVersion,
const boost::optional<ChunkVersion> newControlChunkVersion,
StringData toShard,
@@ -188,18 +156,16 @@ public:
op.append("ns", ChunkType::ConfigNS);
BSONObjBuilder n(op.subobjStart("o"));
- n.append(ChunkType::name(),
- ChunkType::genID(nss.ns(), migratedChunkRange.getValue().getMin()));
+ n.append(ChunkType::name(), ChunkType::genID(nss.ns(), migratedChunkRange.getMin()));
newMigratedChunkVersion.addToBSON(n, ChunkType::DEPRECATED_lastmod());
n.append(ChunkType::ns(), nss.ns());
- n.append(ChunkType::min(), migratedChunkRange.getValue().getMin());
- n.append(ChunkType::max(), migratedChunkRange.getValue().getMax());
+ n.append(ChunkType::min(), migratedChunkRange.getMin());
+ n.append(ChunkType::max(), migratedChunkRange.getMax());
n.append(ChunkType::shard(), toShard);
n.done();
BSONObjBuilder q(op.subobjStart("o2"));
- q.append(ChunkType::name(),
- ChunkType::genID(nss.ns(), migratedChunkRange.getValue().getMin()));
+ q.append(ChunkType::name(), ChunkType::genID(nss.ns(), migratedChunkRange.getMin()));
q.done();
updates.append(op.obj());
@@ -213,18 +179,16 @@ public:
op.append("ns", ChunkType::ConfigNS);
BSONObjBuilder n(op.subobjStart("o"));
- n.append(ChunkType::name(),
- ChunkType::genID(nss.ns(), controlChunkRange->getValue().getMin()));
+ n.append(ChunkType::name(), ChunkType::genID(nss.ns(), controlChunkRange->getMin()));
newControlChunkVersion->addToBSON(n, ChunkType::DEPRECATED_lastmod());
n.append(ChunkType::ns(), nss.ns());
- n.append(ChunkType::min(), controlChunkRange->getValue().getMin());
- n.append(ChunkType::max(), controlChunkRange->getValue().getMax());
+ n.append(ChunkType::min(), controlChunkRange->getMin());
+ n.append(ChunkType::max(), controlChunkRange->getMax());
n.append(ChunkType::shard(), fromShard);
n.done();
BSONObjBuilder q(op.subobjStart("o2"));
- q.append(ChunkType::name(),
- ChunkType::genID(nss.ns(), controlChunkRange->getValue().getMin()));
+ q.append(ChunkType::name(), ChunkType::genID(nss.ns(), controlChunkRange->getMin()));
q.done();
updates.append(op.obj());
@@ -243,29 +207,9 @@ public:
std::string& errmsg,
BSONObjBuilder& result) override {
const NamespaceString nss = NamespaceString(parseNs(dbName, cmdObj));
- std::string fromShard = extractString(cmdObj, "fromShard");
- std::string toShard = extractString(cmdObj, "toShard");
-
- BSONObj migratedChunkField = uassertStatusOK(getBSONField(cmdObj, "migratedChunk"));
- StatusWith<ChunkRange> migratedChunkRange = ChunkRange::fromBSON(migratedChunkField);
- uassert(migratedChunkRange.getStatus().code(),
- str::stream() << "Failed to parse 'migratedChunk' field in "
- << "ConfigSvrCommitChunkMigration command request: "
- << migratedChunkRange.getStatus().reason(),
- migratedChunkRange.isOK());
-
- // controlChunk is optional, so parse it if present.
- boost::optional<StatusWith<ChunkRange>> controlChunkRange = boost::none;
- if (cmdObj.hasField("controlChunk")) {
- BSONObj controlChunkField = uassertStatusOK(getBSONField(cmdObj, "controlChunk"));
- controlChunkRange = ChunkRange::fromBSON(controlChunkField);
- uassert(controlChunkRange->getStatus().code(),
- str::stream()
- << "Failed to parse 'controlChunk' field in ConfigSvrCommitChunkMigration "
- << "command request: "
- << controlChunkRange->getStatus().reason(),
- controlChunkRange->isOK());
- }
+
+ CommitChunkMigrationRequest commitChunkMigrationRequest =
+ uassertStatusOK(CommitChunkMigrationRequest::createFromCommand(nss, cmdObj));
// Run operations under a nested lock as a hack to prevent yielding. When query/applyOps
// commands are called, they will take a second lock, and the PlanExecutor will be unable to
@@ -280,15 +224,16 @@ public:
// Check that migratedChunk and controlChunk are where they should be, on fromShard.
checkChunkIsOnShard(txn,
nss,
- migratedChunkRange.getValue().getMin(),
- migratedChunkRange.getValue().getMax(),
- fromShard);
- if (controlChunkRange) {
+ commitChunkMigrationRequest.getMigratedChunkRange().getMin(),
+ commitChunkMigrationRequest.getMigratedChunkRange().getMax(),
+ commitChunkMigrationRequest.getFromShard());
+
+ if (commitChunkMigrationRequest.hasControlChunkRange()) {
checkChunkIsOnShard(txn,
nss,
- controlChunkRange->getValue().getMin(),
- controlChunkRange->getValue().getMax(),
- fromShard);
+ commitChunkMigrationRequest.getControlChunkRange().getMin(),
+ commitChunkMigrationRequest.getControlChunkRange().getMax(),
+ commitChunkMigrationRequest.getFromShard());
}
// Generate the new chunk version (CV). Query the current max CV of the collection. Use the
@@ -320,9 +265,11 @@ public:
ChunkVersion newMigratedChunkVersion =
ChunkVersion(currentMaxVersion.majorVersion() + 1, 0, currentMaxVersion.epoch());
boost::optional<ChunkVersion> newControlChunkVersion = boost::none;
- if (controlChunkRange) {
+ boost::optional<ChunkRange> newControlChunkRange = boost::none;
+ if (commitChunkMigrationRequest.hasControlChunkRange()) {
newControlChunkVersion =
ChunkVersion(currentMaxVersion.majorVersion() + 1, 1, currentMaxVersion.epoch());
+ newControlChunkRange = commitChunkMigrationRequest.getControlChunkRange();
}
auto applyOpsCommandResponse = grid.shardRegistry()->getConfigShard()->runCommand(
@@ -330,12 +277,12 @@ public:
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
nss.db().toString(),
makeCommitChunkApplyOpsCommand(nss,
- migratedChunkRange,
- controlChunkRange,
+ commitChunkMigrationRequest.getMigratedChunkRange(),
+ newControlChunkRange,
newMigratedChunkVersion,
newControlChunkVersion,
- toShard,
- fromShard),
+ commitChunkMigrationRequest.getToShard().toString(),
+ commitChunkMigrationRequest.getFromShard().toString()),
Shard::RetryPolicy::kIdempotent);
if (!applyOpsCommandResponse.isOK()) {
@@ -347,7 +294,7 @@ public:
}
newMigratedChunkVersion.appendWithFieldForCommands(&result, "migratedChunkVersion");
- if (controlChunkRange) {
+ if (commitChunkMigrationRequest.hasControlChunkRange()) {
newControlChunkVersion->appendWithFieldForCommands(&result, "controlChunkVersion");
}
diff --git a/src/mongo/db/s/migration_source_manager.cpp b/src/mongo/db/s/migration_source_manager.cpp
index cdc188dcd46..9b54e5d4fe3 100644
--- a/src/mongo/db/s/migration_source_manager.cpp
+++ b/src/mongo/db/s/migration_source_manager.cpp
@@ -45,6 +45,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/commit_chunk_migration_request_type.h"
#include "mongo/s/shard_key_pattern.h"
#include "mongo/s/stale_exception.h"
#include "mongo/stdx/memory.h"
@@ -284,38 +285,32 @@ Status MigrationSourceManager::commitDonateChunk(OperationContext* txn) {
str::stream() << "commit clone failed due to " << commitCloneStatus.toString()};
}
- BSONObjBuilder builder;
- builder.append("_configsvrCommitChunkMigration", _args.getNss().ns());
- builder.append("fromShard", _args.getFromShardId().toString());
- builder.append("toShard", _args.getToShardId().toString());
- {
- ChunkType migratedChunkType;
- migratedChunkType.setMin(_args.getMinKey());
- migratedChunkType.setMax(_args.getMaxKey());
- builder.append("migratedChunk", migratedChunkType.toBSON());
- }
+ ChunkType migratedChunkType;
+ migratedChunkType.setMin(_args.getMinKey());
+ migratedChunkType.setMax(_args.getMaxKey());
// If we have chunks left on the FROM shard, bump the version of one of them as well. This will
// change the local collection major version, which indicates to other processes that the chunk
// metadata has changed and they should refresh.
- bool hasControlChunk = false;
+ boost::optional<ChunkType> controlChunkType = boost::none;
if (_committedMetadata->getNumChunks() > 1) {
- hasControlChunk = true;
ChunkType differentChunk;
invariant(_committedMetadata->getDifferentChunk(_args.getMinKey(), &differentChunk));
invariant(differentChunk.getMin().woCompare(_args.getMinKey()) != 0);
-
- {
- ChunkType controlChunkType;
- controlChunkType.setMin(differentChunk.getMin());
- controlChunkType.setMax(differentChunk.getMax());
- builder.append("controlChunk", controlChunkType.toBSON());
- }
+ controlChunkType = std::move(differentChunk);
} else {
log() << "moveChunk moved last chunk out for collection '" << _args.getNss().ns() << "'";
}
+ BSONObjBuilder builder;
+ CommitChunkMigrationRequest::appendAsCommand(&builder,
+ _args.getNss(),
+ _args.getFromShardId(),
+ _args.getToShardId(),
+ migratedChunkType,
+ controlChunkType);
+
builder.append(kWriteConcernField, kMajorityWriteConcern.toBSON());
MONGO_FAIL_POINT_PAUSE_WHILE_SET(hangBeforeCommitMigration);
@@ -339,7 +334,7 @@ Status MigrationSourceManager::commitDonateChunk(OperationContext* txn) {
// response and forget the migrated chunk.
ChunkVersion uncommittedCollVersion;
- if (hasControlChunk) {
+ if (controlChunkType) {
uncommittedCollVersion = fassertStatusOK(
40084,
ChunkVersion::parseFromBSONWithFieldForCommands(
@@ -430,7 +425,7 @@ Status MigrationSourceManager::commitDonateChunk(OperationContext* txn) {
<< causedBy(commitChunkMigrationResponse.getStatus())};
}
- if (hasControlChunk) {
+ if (controlChunkType) {
ChunkVersion refreshedCollVersion = refreshedMetadata->getCollVersion();
if (refreshedCollVersion.majorVersion() <=
previousMetadataCollVersion.majorVersion() ||
diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript
index 44a314f46d3..02662435d88 100644
--- a/src/mongo/s/SConscript
+++ b/src/mongo/s/SConscript
@@ -60,6 +60,7 @@ env.Library(
'request_types/add_shard_to_zone_request_type.cpp',
'request_types/assign_key_range_to_zone_request_type.cpp',
'request_types/balance_chunk_request_type.cpp',
+ 'request_types/commit_chunk_migration_request_type.cpp',
'request_types/remove_shard_from_zone_request_type.cpp',
'chunk_diff.cpp',
'chunk_version.cpp',
@@ -230,6 +231,7 @@ env.CppUnitTest('request_types_test',
'request_types/add_shard_to_zone_request_test.cpp',
'request_types/assign_key_range_to_zone_request_test.cpp',
'request_types/balance_chunk_request_test.cpp',
+ 'request_types/commit_chunk_migration_request_test.cpp',
'request_types/remove_shard_from_zone_request_test.cpp',
],
LIBDEPS=[
diff --git a/src/mongo/s/request_types/commit_chunk_migration_request_test.cpp b/src/mongo/s/request_types/commit_chunk_migration_request_test.cpp
new file mode 100644
index 00000000000..911dc0e7360
--- /dev/null
+++ b/src/mongo/s/request_types/commit_chunk_migration_request_test.cpp
@@ -0,0 +1,108 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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/bson/bsonmisc.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/s/request_types/commit_chunk_migration_request_type.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+
+using unittest::assertGet;
+
+namespace {
+
+const auto kNamespaceString = NamespaceString("TestDB", "TestColl");
+
+const auto kShardId0 = ShardId("shard0");
+const auto kShardId1 = ShardId("shard1");
+
+const auto kKey0 = BSON("Key" << -100);
+const auto kKey1 = BSON("Key" << 100);
+const auto kKey2 = BSON("Key" << -50);
+const auto kKey3 = BSON("Key" << 50);
+
+const char kConfigSvrCommitChunkMigration[] = "_configsvrCommitChunkMigration";
+
+TEST(CommitChunkMigrationRequest, WithControlChunk) {
+ BSONObjBuilder builder;
+
+ ChunkType migratedChunkType;
+ migratedChunkType.setMin(kKey0);
+ migratedChunkType.setMax(kKey1);
+
+ ChunkType controlChunkTypeTemp;
+ controlChunkTypeTemp.setMin(kKey2);
+ controlChunkTypeTemp.setMax(kKey3);
+ boost::optional<ChunkType> controlChunkType = std::move(controlChunkTypeTemp);
+
+ CommitChunkMigrationRequest::appendAsCommand(
+ &builder, kNamespaceString, kShardId0, kShardId1, migratedChunkType, controlChunkType);
+
+ BSONObj cmdObj = builder.obj();
+
+ auto request = assertGet(CommitChunkMigrationRequest::createFromCommand(
+ NamespaceString(cmdObj[kConfigSvrCommitChunkMigration].String()), cmdObj));
+
+ ASSERT_EQ(kNamespaceString, request.getNss());
+ ASSERT_EQ(kShardId0, request.getFromShard());
+ ASSERT_EQ(kShardId1, request.getToShard());
+ ASSERT_EQ(kKey0, request.getMigratedChunkRange().getMin());
+ ASSERT_EQ(kKey1, request.getMigratedChunkRange().getMax());
+ ASSERT(request.hasControlChunkRange());
+ ASSERT_EQ(kKey2, request.getControlChunkRange().getMin());
+ ASSERT_EQ(kKey3, request.getControlChunkRange().getMax());
+}
+
+TEST(CommitChunkMigrationRequest, WithoutControlChunk) {
+ BSONObjBuilder builder;
+
+ ChunkType migratedChunkType;
+ migratedChunkType.setMin(kKey0);
+ migratedChunkType.setMax(kKey1);
+
+ CommitChunkMigrationRequest::appendAsCommand(
+ &builder, kNamespaceString, kShardId0, kShardId1, migratedChunkType, boost::none);
+
+ BSONObj cmdObj = builder.obj();
+
+ auto request = assertGet(CommitChunkMigrationRequest::createFromCommand(
+ NamespaceString(cmdObj[kConfigSvrCommitChunkMigration].String()), cmdObj));
+
+ ASSERT_EQ(kNamespaceString, request.getNss());
+ ASSERT_EQ(kShardId0, request.getFromShard());
+ ASSERT_EQ(kShardId1, request.getToShard());
+ ASSERT_EQ(kKey0, request.getMigratedChunkRange().getMin());
+ ASSERT_EQ(kKey1, request.getMigratedChunkRange().getMax());
+ ASSERT(!request.hasControlChunkRange());
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/s/request_types/commit_chunk_migration_request_type.cpp b/src/mongo/s/request_types/commit_chunk_migration_request_type.cpp
new file mode 100644
index 00000000000..f03a105c5cf
--- /dev/null
+++ b/src/mongo/s/request_types/commit_chunk_migration_request_type.cpp
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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/commit_chunk_migration_request_type.h"
+
+#include "mongo/bson/util/bson_extract.h"
+
+namespace mongo {
+namespace {
+
+const char kConfigSvrCommitChunkMigration[] = "_configsvrCommitChunkMigration";
+const char kFromShard[] = "fromShard";
+const char kToShard[] = "toShard";
+const char kMigratedChunk[] = "migratedChunk";
+const char kControlChunk[] = "controlChunk";
+
+/**
+ * Attempts to parse a ChunkRange from "field" in "source".
+ */
+StatusWith<ChunkRange> extractChunkRange(const BSONObj& source, StringData field) {
+ BSONElement fieldElement;
+ auto status = bsonExtractTypedField(source, field, BSONType::Object, &fieldElement);
+ if (!status.isOK())
+ return status;
+
+ return ChunkRange::fromBSON(fieldElement.Obj());
+}
+
+/**
+ * Attempts to parse a ShardId from "field" in "source".
+ */
+StatusWith<ShardId> extractShardId(const BSONObj& source, StringData field) {
+ std::string stringResult;
+
+ auto status = bsonExtractStringField(source, field, &stringResult);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (stringResult.empty()) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "The field '" + field.toString() + "' cannot be empty");
+ }
+
+ return ShardId(stringResult);
+}
+
+} // namespace
+
+StatusWith<CommitChunkMigrationRequest> CommitChunkMigrationRequest::createFromCommand(
+ const NamespaceString& nss, const BSONObj& obj) {
+
+ auto migratedChunkRange = extractChunkRange(obj, kMigratedChunk);
+ if (!migratedChunkRange.isOK()) {
+ return migratedChunkRange.getStatus();
+ }
+
+ CommitChunkMigrationRequest request(nss, std::move(migratedChunkRange.getValue()));
+
+ {
+ auto fromShard = extractShardId(obj, kFromShard);
+ if (!fromShard.isOK()) {
+ return fromShard.getStatus();
+ }
+
+ request._fromShard = std::move(fromShard.getValue());
+ }
+
+ {
+ auto toShard = extractShardId(obj, kToShard);
+ if (!toShard.isOK()) {
+ return toShard.getStatus();
+ }
+
+ request._toShard = std::move(toShard.getValue());
+ }
+
+ {
+ // controlChunk is optional, so parse it if present.
+ if (obj.hasField(kControlChunk)) {
+ auto controlChunkRange = extractChunkRange(obj, kControlChunk);
+ if (!controlChunkRange.isOK()) {
+ return controlChunkRange.getStatus();
+ }
+
+ request._controlChunkRange = std::move(controlChunkRange.getValue());
+ }
+ }
+
+ return request;
+}
+
+void CommitChunkMigrationRequest::appendAsCommand(
+ BSONObjBuilder* builder,
+ const NamespaceString& nss,
+ const ShardId& fromShard,
+ const ShardId& toShard,
+ const ChunkType& migratedChunkType,
+ const boost::optional<ChunkType>& controlChunkType) {
+ invariant(builder->asTempObj().isEmpty());
+ invariant(nss.isValid());
+
+ builder->append(kConfigSvrCommitChunkMigration, nss.ns());
+ builder->append(kFromShard, fromShard.toString());
+ builder->append(kToShard, toShard.toString());
+ builder->append(kMigratedChunk, migratedChunkType.toBSON());
+
+ if (controlChunkType) {
+ builder->append(kControlChunk, controlChunkType->toBSON());
+ }
+}
+
+const ChunkRange& CommitChunkMigrationRequest::getControlChunkRange() const {
+ invariant(_controlChunkRange);
+ return _controlChunkRange.get();
+}
+
+CommitChunkMigrationRequest::CommitChunkMigrationRequest(const NamespaceString& nss,
+ const ChunkRange& range)
+ : _nss(nss), _migratedChunkRange(range) {}
+
+} // namespace mongo
diff --git a/src/mongo/s/request_types/commit_chunk_migration_request_type.h b/src/mongo/s/request_types/commit_chunk_migration_request_type.h
new file mode 100644
index 00000000000..431cd20b631
--- /dev/null
+++ b/src/mongo/s/request_types/commit_chunk_migration_request_type.h
@@ -0,0 +1,102 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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 <string>
+
+#include "mongo/db/namespace_string.h"
+#include "mongo/s/catalog/type_chunk.h"
+
+namespace mongo {
+
+/**
+ * Creates and parses commit chunk migration command BSON objects.
+ */
+class CommitChunkMigrationRequest {
+public:
+ /**
+ * Parses the input command and produces a request corresponding to its arguments.
+ */
+ static StatusWith<CommitChunkMigrationRequest> createFromCommand(const NamespaceString& nss,
+ const BSONObj& obj);
+
+ /**
+ * Constructs a commitChunkMigration command with the specified parameters and writes it to
+ * the builder, without closing the builder. The builder must be empty, but callers are free
+ * to append more fields once the command has been constructed.
+ */
+ static void appendAsCommand(BSONObjBuilder* builder,
+ const NamespaceString& nss,
+ const ShardId& fromShard,
+ const ShardId& toShard,
+ const ChunkType& migratedChunkType,
+ const boost::optional<ChunkType>& controlChunkType);
+
+ const NamespaceString& getNss() const {
+ return _nss;
+ }
+
+ const ShardId& getFromShard() const {
+ return _fromShard;
+ }
+
+ const ShardId& getToShard() const {
+ return _toShard;
+ }
+
+ const ChunkRange& getMigratedChunkRange() const {
+ return _migratedChunkRange;
+ }
+
+ const ChunkRange& getControlChunkRange() const;
+
+ bool hasControlChunkRange() {
+ return bool(_controlChunkRange);
+ }
+
+private:
+ CommitChunkMigrationRequest(const NamespaceString& nss, const ChunkRange& range);
+
+ // The collection for which this request applies.
+ NamespaceString _nss;
+
+ // The source shard name.
+ ShardId _fromShard;
+
+ // The recipient shard name.
+ ShardId _toShard;
+
+ // Range of migrated chunk being moved.
+ ChunkRange _migratedChunkRange;
+
+ // Range of control chunk being moved, if it exists.
+ boost::optional<ChunkRange> _controlChunkRange;
+};
+
+} // namespace mongo