Skip to content

Commit

Permalink
SERVER-25000 Implemented type for configsvr's MergeChunkRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
Jess Fan committed Jul 22, 2016
1 parent b377f0a commit 752fad1
Show file tree
Hide file tree
Showing 4 changed files with 411 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/mongo/s/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ env.Library(
'request_types/commit_chunk_migration_request_type.cpp',
'request_types/remove_shard_from_zone_request_type.cpp',
'request_types/split_chunk_request_type.cpp',
'request_types/merge_chunk_request_type.cpp',
'chunk_diff.cpp',
'chunk_version.cpp',
'migration_secondary_throttle_options.cpp',
Expand Down Expand Up @@ -235,6 +236,7 @@ env.CppUnitTest('request_types_test',
'request_types/commit_chunk_migration_request_test.cpp',
'request_types/remove_shard_from_zone_request_test.cpp',
'request_types/split_chunk_request_test.cpp',
'request_types/merge_chunk_request_test.cpp',
],
LIBDEPS=[
'common',
Expand Down
167 changes: 167 additions & 0 deletions src/mongo/s/request_types/merge_chunk_request_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* 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/bsonobjbuilder.h"
#include "mongo/s/request_types/merge_chunk_request_type.h"

#include "mongo/unittest/unittest.h"

namespace mongo {

namespace {

using unittest::assertGet;

TEST(MergeChunkRequest, BasicValidConfigCommand) {
auto request = assertGet(MergeChunkRequest::parseFromConfigCommand(
BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "collEpoch"
<< OID("7fffffff0000000000000001")
<< "chunkBoundaries"
<< BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)))));
ASSERT_EQ(NamespaceString("TestDB", "TestColl"), request.getNamespace());
ASSERT_EQ(OID("7fffffff0000000000000001"), request.getEpoch());
ASSERT_EQ(BSON("a" << 1), request.getChunkBoundaries().at(0));
ASSERT_EQ(BSON("a" << 5), request.getChunkBoundaries().at(1));
ASSERT_EQ(BSON("a" << 10), request.getChunkBoundaries().at(2));
}

TEST(MergeChunkRequest, ConfigCommandtoBSON) {
BSONObj serializedRequest =
BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "collEpoch"
<< OID("7fffffff0000000000000001")
<< "chunkBoundaries"
<< BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10)));
BSONObj writeConcernObj = BSON("writeConcern" << BSON("w"
<< "majority"));

BSONObjBuilder cmdBuilder;
{
cmdBuilder.appendElements(serializedRequest);
cmdBuilder.appendElements(writeConcernObj);
}

auto request = assertGet(MergeChunkRequest::parseFromConfigCommand(serializedRequest));
auto requestToBSON = request.toConfigCommandBSON(writeConcernObj);

ASSERT_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))));
ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus());
}

TEST(MergeChunkRequest, MissingCollEpochErrors) {
auto request = MergeChunkRequest::parseFromConfigCommand(
BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "chunkBoundaries"
<< BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10))));
ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus());
}

TEST(MergeChunkRequest, MissingChunkBoundariesErrors) {
auto request =
MergeChunkRequest::parseFromConfigCommand(BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "collEpoch"
<< OID("7fffffff0000000000000001")));
ASSERT_EQ(ErrorCodes::NoSuchKey, request.getStatus());
}

TEST(MergeChunkRequest, WrongNamespaceTypeErrors) {
auto request = MergeChunkRequest::parseFromConfigCommand(BSON(
"_configsvrMergeChunk" << 1234 << "collEpoch" << OID("7fffffff0000000000000001")
<< "chunkBoundaries"
<< BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10))));
ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus());
}

TEST(MergeChunkRequest, WrongCollEpochTypeErrors) {
auto request = MergeChunkRequest::parseFromConfigCommand(
BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "collEpoch"
<< 1234
<< "chunkBoundaries"
<< BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10))));
ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus());
}

TEST(MergeChunkRequest, WrongChunkBoundariesTypeErrors) {
auto request = MergeChunkRequest::parseFromConfigCommand(BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "collEpoch"
<< OID("7fffffff0000000000000001")
<< "chunkBoundaries"
<< 1234));
ASSERT_EQ(ErrorCodes::TypeMismatch, request.getStatus());
}

TEST(MergeChunkRequest, InvalidNamespaceErrors) {
auto request = MergeChunkRequest::parseFromConfigCommand(
BSON("_configsvrMergeChunk"
<< ""
<< "collEpoch"
<< OID("7fffffff0000000000000001")
<< "chunkBoundaries"
<< BSON_ARRAY(BSON("a" << 1) << BSON("a" << 5) << BSON("a" << 10))));
ASSERT_EQ(ErrorCodes::InvalidNamespace, request.getStatus());
}

TEST(MergeChunkRequest, EmptyChunkBoundariesErrors) {
auto request = MergeChunkRequest::parseFromConfigCommand(BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "collEpoch"
<< OID("7fffffff0000000000000001")
<< "chunkBoundaries"
<< BSONArray()));
ASSERT_EQ(ErrorCodes::InvalidOptions, request.getStatus());
}

TEST(MergeChunkRequest, TooFewChunkBoundariesErrors) {
auto request = MergeChunkRequest::parseFromConfigCommand(
BSON("_configsvrMergeChunk"
<< "TestDB.TestColl"
<< "collEpoch"
<< OID("7fffffff0000000000000001")
<< "chunkBoundaries"
<< BSON_ARRAY(BSON("a" << 1) << BSON("a" << 10))));
ASSERT_EQ(ErrorCodes::InvalidOptions, request.getStatus());
}
}

} // namespace mongo
144 changes: 144 additions & 0 deletions src/mongo/s/request_types/merge_chunk_request_type.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* 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/merge_chunk_request_type.h"

#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/util/bson_extract.h"

namespace mongo {

using std::vector;

namespace {

const char kConfigsvrMergeChunk[] = "_configsvrMergeChunk";
const char kCollEpoch[] = "collEpoch";
const char kChunkBoundaries[] = "chunkBoundaries";

} // unnamed namespace

MergeChunkRequest::MergeChunkRequest(NamespaceString nss,
OID epoch,
vector<BSONObj> chunkBoundaries)
: _nss(std::move(nss)),
_epoch(std::move(epoch)),
_chunkBoundaries(std::move(chunkBoundaries)) {}

StatusWith<MergeChunkRequest> MergeChunkRequest::parseFromConfigCommand(const BSONObj& cmdObj) {
std::string ns;
auto parseNamespaceStatus = bsonExtractStringField(cmdObj, kConfigsvrMergeChunk, &ns);

if (!parseNamespaceStatus.isOK()) {
return parseNamespaceStatus;
}

OID epoch;
auto parseEpochStatus = bsonExtractOIDField(cmdObj, kCollEpoch, &epoch);

if (!parseEpochStatus.isOK()) {
return parseEpochStatus;
}

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());
}
}

auto request =
MergeChunkRequest(NamespaceString(ns), std::move(epoch), std::move(chunkBoundaries));
Status validationStatus = request._validate();
if (!validationStatus.isOK()) {
return validationStatus;
}

return request;
}

BSONObj MergeChunkRequest::toConfigCommandBSON(const BSONObj& writeConcern) {
BSONObjBuilder cmdBuilder;
appendAsConfigCommand(&cmdBuilder);

// Tack on passed-in writeConcern
cmdBuilder.appendElements(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);
}
}
}

const NamespaceString& MergeChunkRequest::getNamespace() const {
return _nss;
}

const OID& MergeChunkRequest::getEpoch() const {
return _epoch;
}

const vector<BSONObj>& MergeChunkRequest::getChunkBoundaries() const {
return _chunkBoundaries;
}

Status MergeChunkRequest::_validate() {
if (!getNamespace().isValid()) {
return Status(ErrorCodes::InvalidNamespace,
str::stream() << "invalid namespace '" << _nss.ns()
<< "' specified for request");
}

if (getChunkBoundaries().size() < 3) {
return Status(
ErrorCodes::InvalidOptions,
"need to provide at least three chunk boundaries for the chunks to be merged");
}

return Status::OK();
}

} // namespace mongo
Loading

0 comments on commit 752fad1

Please sign in to comment.