diff options
Diffstat (limited to 'src/mongo/s/commands/cluster_split_collection_cmd.cpp')
-rw-r--r-- | src/mongo/s/commands/cluster_split_collection_cmd.cpp | 365 |
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 |