summaryrefslogtreecommitdiff
path: root/src/mongo/s/commands/cluster_split_collection_cmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/commands/cluster_split_collection_cmd.cpp')
-rw-r--r--src/mongo/s/commands/cluster_split_collection_cmd.cpp365
1 files changed, 176 insertions, 189 deletions
diff --git a/src/mongo/s/commands/cluster_split_collection_cmd.cpp b/src/mongo/s/commands/cluster_split_collection_cmd.cpp
index acd99d32c7b..fd5f462518c 100644
--- a/src/mongo/s/commands/cluster_split_collection_cmd.cpp
+++ b/src/mongo/s/commands/cluster_split_collection_cmd.cpp
@@ -49,244 +49,231 @@
namespace mongo {
- using std::shared_ptr;
- using std::string;
- using std::vector;
+using std::shared_ptr;
+using std::string;
+using std::vector;
namespace {
- class SplitCollectionCmd : public Command {
- public:
- SplitCollectionCmd() : Command("split", false, "split") { }
+class SplitCollectionCmd : public Command {
+public:
+ SplitCollectionCmd() : Command("split", false, "split") {}
+
+ virtual bool slaveOk() const {
+ return true;
+ }
+
+ virtual bool adminOnly() const {
+ return true;
+ }
+
+ virtual bool isWriteCommandForConfigServer() const {
+ return false;
+ }
+
+ virtual void help(std::stringstream& help) const {
+ help << " example: - split the shard that contains give key\n"
+ << " { split : 'alleyinsider.blog.posts' , find : { ts : 1 } }\n"
+ << " example: - split the shard that contains the key with this as the middle\n"
+ << " { split : 'alleyinsider.blog.posts' , middle : { ts : 1 } }\n"
+ << " NOTE: this does not move the chunks, it just creates a logical separation.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(NamespaceString(parseNs(dbname, cmdObj))),
+ ActionType::splitChunk)) {
+ return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+ return Status::OK();
+ }
+
+ virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
+ return parseNsFullyQualified(dbname, cmdObj);
+ }
+
+ virtual bool run(OperationContext* txn,
+ const std::string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ std::string& errmsg,
+ BSONObjBuilder& result) {
+ ShardConnection::sync();
+
+ const NamespaceString nss(parseNs(dbname, cmdObj));
+ if (nss.size() == 0) {
+ return appendCommandStatus(
+ result, Status(ErrorCodes::InvalidNamespace, "no namespace specified"));
+ }
- virtual bool slaveOk() const {
- return true;
+ auto status = grid.catalogCache()->getDatabase(nss.db().toString());
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status.getStatus());
}
- virtual bool adminOnly() const {
- return true;
+ std::shared_ptr<DBConfig> config = status.getValue();
+ if (!config->isSharded(nss.ns())) {
+ config->reload();
+
+ if (!config->isSharded(nss.ns())) {
+ return appendCommandStatus(result,
+ Status(ErrorCodes::NamespaceNotSharded,
+ "ns [" + nss.ns() + " is not sharded."));
+ }
}
- virtual bool isWriteCommandForConfigServer() const {
+ const BSONField<BSONObj> findField("find", BSONObj());
+ const BSONField<BSONArray> boundsField("bounds", BSONArray());
+ const BSONField<BSONObj> middleField("middle", BSONObj());
+
+ BSONObj find;
+ if (FieldParser::extract(cmdObj, findField, &find, &errmsg) == FieldParser::FIELD_INVALID) {
return false;
}
- virtual void help(std::stringstream& help) const {
- help << " example: - split the shard that contains give key\n"
- << " { split : 'alleyinsider.blog.posts' , find : { ts : 1 } }\n"
- << " example: - split the shard that contains the key with this as the middle\n"
- << " { split : 'alleyinsider.blog.posts' , middle : { ts : 1 } }\n"
- << " NOTE: this does not move the chunks, it just creates a logical separation.";
+ BSONArray bounds;
+ if (FieldParser::extract(cmdObj, boundsField, &bounds, &errmsg) ==
+ FieldParser::FIELD_INVALID) {
+ return false;
}
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
+ if (!bounds.isEmpty()) {
+ if (!bounds.hasField("0")) {
+ errmsg = "lower bound not specified";
+ return false;
+ }
- if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource(
- ResourcePattern::forExactNamespace(
- NamespaceString(parseNs(dbname,
- cmdObj))),
- ActionType::splitChunk)) {
- return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ if (!bounds.hasField("1")) {
+ errmsg = "upper bound not specified";
+ return false;
}
- return Status::OK();
}
- virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
- return parseNsFullyQualified(dbname, cmdObj);
+ if (!find.isEmpty() && !bounds.isEmpty()) {
+ errmsg = "cannot specify bounds and find at the same time";
+ return false;
}
- virtual bool run(OperationContext* txn,
- const std::string& dbname,
- BSONObj& cmdObj,
- int options,
- std::string& errmsg,
- BSONObjBuilder& result) {
+ BSONObj middle;
+ if (FieldParser::extract(cmdObj, middleField, &middle, &errmsg) ==
+ FieldParser::FIELD_INVALID) {
+ return false;
+ }
- ShardConnection::sync();
+ if (find.isEmpty() && bounds.isEmpty() && middle.isEmpty()) {
+ errmsg = "need to specify find/bounds or middle";
+ return false;
+ }
- const NamespaceString nss(parseNs(dbname, cmdObj));
- if (nss.size() == 0) {
- return appendCommandStatus(result, Status(ErrorCodes::InvalidNamespace,
- "no namespace specified"));
- }
+ if (!find.isEmpty() && !middle.isEmpty()) {
+ errmsg = "cannot specify find and middle together";
+ return false;
+ }
- auto status = grid.catalogCache()->getDatabase(nss.db().toString());
- if (!status.isOK()) {
- return appendCommandStatus(result, status.getStatus());
- }
+ if (!bounds.isEmpty() && !middle.isEmpty()) {
+ errmsg = "cannot specify bounds and middle together";
+ return false;
+ }
- std::shared_ptr<DBConfig> config = status.getValue();
- if (!config->isSharded(nss.ns())) {
- config->reload();
+ // This refreshes the chunk metadata if stale.
+ ChunkManagerPtr info = config->getChunkManager(nss.ns(), true);
+ ChunkPtr chunk;
- if (!config->isSharded(nss.ns())) {
- return appendCommandStatus(result,
- Status(ErrorCodes::NamespaceNotSharded,
- "ns [" + nss.ns() + " is not sharded."));
- }
- }
+ if (!find.isEmpty()) {
+ StatusWith<BSONObj> status = info->getShardKeyPattern().extractShardKeyFromQuery(find);
- const BSONField<BSONObj> findField("find", BSONObj());
- const BSONField<BSONArray> boundsField("bounds", BSONArray());
- const BSONField<BSONObj> middleField("middle", BSONObj());
+ // Bad query
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status.getStatus());
+ }
- BSONObj find;
- if (FieldParser::extract(cmdObj, findField, &find, &errmsg) ==
- FieldParser::FIELD_INVALID) {
+ BSONObj shardKey = status.getValue();
+ if (shardKey.isEmpty()) {
+ errmsg = stream() << "no shard key found in chunk query " << find;
return false;
}
- BSONArray bounds;
- if (FieldParser::extract(cmdObj, boundsField, &bounds, &errmsg) ==
- FieldParser::FIELD_INVALID) {
+ chunk = info->findIntersectingChunk(shardKey);
+ invariant(chunk.get());
+ } else if (!bounds.isEmpty()) {
+ if (!info->getShardKeyPattern().isShardKey(bounds[0].Obj()) ||
+ !info->getShardKeyPattern().isShardKey(bounds[1].Obj())) {
+ errmsg = stream() << "shard key bounds "
+ << "[" << bounds[0].Obj() << "," << bounds[1].Obj() << ")"
+ << " are not valid for shard key pattern "
+ << info->getShardKeyPattern().toBSON();
return false;
}
- if (!bounds.isEmpty()) {
- if (!bounds.hasField("0")) {
- errmsg = "lower bound not specified";
- return false;
- }
+ BSONObj minKey = info->getShardKeyPattern().normalizeShardKey(bounds[0].Obj());
+ BSONObj maxKey = info->getShardKeyPattern().normalizeShardKey(bounds[1].Obj());
- if (!bounds.hasField("1")) {
- errmsg = "upper bound not specified";
- return false;
- }
- }
+ chunk = info->findIntersectingChunk(minKey);
+ invariant(chunk.get());
- if (!find.isEmpty() && !bounds.isEmpty()) {
- errmsg = "cannot specify bounds and find at the same time";
+ if (chunk->getMin().woCompare(minKey) != 0 || chunk->getMax().woCompare(maxKey) != 0) {
+ errmsg = stream() << "no chunk found with the shard key bounds "
+ << "[" << minKey << "," << maxKey << ")";
return false;
}
-
- BSONObj middle;
- if (FieldParser::extract(cmdObj, middleField, &middle, &errmsg) ==
- FieldParser::FIELD_INVALID) {
+ } else {
+ // Middle
+ if (!info->getShardKeyPattern().isShardKey(middle)) {
+ errmsg = stream() << "new split key " << middle
+ << " is not valid for shard key pattern "
+ << info->getShardKeyPattern().toBSON();
return false;
}
- if (find.isEmpty() && bounds.isEmpty() && middle.isEmpty()) {
- errmsg = "need to specify find/bounds or middle";
- return false;
- }
+ middle = info->getShardKeyPattern().normalizeShardKey(middle);
- if (!find.isEmpty() && !middle.isEmpty()) {
- errmsg = "cannot specify find and middle together";
- return false;
+ // Check shard key size when manually provided
+ Status status = ShardKeyPattern::checkShardKeySize(middle);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
}
- if (!bounds.isEmpty() && !middle.isEmpty()) {
- errmsg = "cannot specify bounds and middle together";
+ chunk = info->findIntersectingChunk(middle);
+ invariant(chunk.get());
+
+ if (chunk->getMin().woCompare(middle) == 0 || chunk->getMax().woCompare(middle) == 0) {
+ errmsg = stream() << "new split key " << middle
+ << " is a boundary key of existing chunk "
+ << "[" << chunk->getMin() << "," << chunk->getMax() << ")";
return false;
}
+ }
- // This refreshes the chunk metadata if stale.
- ChunkManagerPtr info = config->getChunkManager(nss.ns(), true);
- ChunkPtr chunk;
-
- if (!find.isEmpty()) {
- StatusWith<BSONObj> status =
- info->getShardKeyPattern().extractShardKeyFromQuery(find);
-
- // Bad query
- if (!status.isOK()) {
- return appendCommandStatus(result, status.getStatus());
- }
-
- BSONObj shardKey = status.getValue();
- if (shardKey.isEmpty()) {
- errmsg = stream() << "no shard key found in chunk query " << find;
- return false;
- }
+ invariant(chunk.get());
+ log() << "splitting chunk [" << chunk->getMin() << "," << chunk->getMax() << ")"
+ << " in collection " << nss.ns() << " on shard " << chunk->getShardId();
- chunk = info->findIntersectingChunk(shardKey);
- invariant(chunk.get());
- }
- else if (!bounds.isEmpty()) {
-
- if (!info->getShardKeyPattern().isShardKey(bounds[0].Obj())
- || !info->getShardKeyPattern().isShardKey(bounds[1].Obj())) {
- errmsg = stream() << "shard key bounds " << "[" << bounds[0].Obj() << ","
- << bounds[1].Obj() << ")"
- << " are not valid for shard key pattern "
- << info->getShardKeyPattern().toBSON();
- return false;
- }
-
- BSONObj minKey = info->getShardKeyPattern().normalizeShardKey(bounds[0].Obj());
- BSONObj maxKey = info->getShardKeyPattern().normalizeShardKey(bounds[1].Obj());
-
- chunk = info->findIntersectingChunk(minKey);
- invariant(chunk.get());
-
- if (chunk->getMin().woCompare(minKey) != 0
- || chunk->getMax().woCompare(maxKey) != 0) {
- errmsg = stream() << "no chunk found with the shard key bounds " << "["
- << minKey << "," << maxKey << ")";
- return false;
- }
- }
- else {
- // Middle
- if (!info->getShardKeyPattern().isShardKey(middle)) {
- errmsg = stream() << "new split key " << middle
- << " is not valid for shard key pattern "
- << info->getShardKeyPattern().toBSON();
- return false;
- }
-
- middle = info->getShardKeyPattern().normalizeShardKey(middle);
-
- // Check shard key size when manually provided
- Status status = ShardKeyPattern::checkShardKeySize(middle);
- if (!status.isOK()) {
- return appendCommandStatus(result, status);
- }
-
- chunk = info->findIntersectingChunk(middle);
- invariant(chunk.get());
-
- if (chunk->getMin().woCompare(middle) == 0
- || chunk->getMax().woCompare(middle) == 0) {
- errmsg = stream() << "new split key " << middle
- << " is a boundary key of existing chunk " << "["
- << chunk->getMin() << "," << chunk->getMax() << ")";
- return false;
- }
+ BSONObj res;
+ if (middle.isEmpty()) {
+ Status status = chunk->split(Chunk::atMedian, NULL, NULL);
+ if (!status.isOK()) {
+ errmsg = "split failed";
+ result.append("cause", status.toString());
+ return false;
}
+ } else {
+ vector<BSONObj> splitPoints;
+ splitPoints.push_back(middle);
- invariant(chunk.get());
- log() << "splitting chunk [" << chunk->getMin() << "," << chunk->getMax() << ")"
- << " in collection " << nss.ns()
- << " on shard " << chunk->getShardId();
-
- BSONObj res;
- if (middle.isEmpty()) {
- Status status = chunk->split(Chunk::atMedian, NULL, NULL);
- if (!status.isOK()) {
- errmsg = "split failed";
- result.append("cause", status.toString());
- return false;
- }
- }
- else {
- vector<BSONObj> splitPoints;
- splitPoints.push_back(middle);
-
- Status status = chunk->multiSplit(splitPoints, NULL);
- if (!status.isOK()) {
- errmsg = "split failed";
- result.append("cause", status.toString());
- return false;
- }
+ Status status = chunk->multiSplit(splitPoints, NULL);
+ if (!status.isOK()) {
+ errmsg = "split failed";
+ result.append("cause", status.toString());
+ return false;
}
-
- return true;
}
- } splitCollectionCmd;
+ return true;
+ }
+
+} splitCollectionCmd;
-} // namespace
-} // namespace mongo
+} // namespace
+} // namespace mongo