summaryrefslogtreecommitdiff
path: root/src/mongo/s/commands/cluster_split_collection_cmd.cpp
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2015-03-09 16:16:17 -0400
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2015-03-10 16:07:36 -0400
commitddfef79e81a33de88ace541bdc705d1712fedbc2 (patch)
tree155bcc8f14bf3a2eeb069d48a52f626301bb7f8b /src/mongo/s/commands/cluster_split_collection_cmd.cpp
parent1b64013e01d1af85f9fb49daa565e8dded2c52a3 (diff)
downloadmongo-ddfef79e81a33de88ace541bdc705d1712fedbc2.tar.gz
SERVER-17496 Move sharding admin commands to individual files
Diffstat (limited to 'src/mongo/s/commands/cluster_split_collection_cmd.cpp')
-rw-r--r--src/mongo/s/commands/cluster_split_collection_cmd.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/mongo/s/commands/cluster_split_collection_cmd.cpp b/src/mongo/s/commands/cluster_split_collection_cmd.cpp
new file mode 100644
index 00000000000..ce8795a67ef
--- /dev/null
+++ b/src/mongo/s/commands/cluster_split_collection_cmd.cpp
@@ -0,0 +1,288 @@
+/**
+ * Copyright (C) 2015 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
+
+#include "mongo/platform/basic.h"
+
+#include <string>
+#include <vector>
+
+#include "mongo/db/auth/action_set.h"
+#include "mongo/db/auth/action_type.h"
+#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/client.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/field_parser.h"
+#include "mongo/s/client/shard_connection.h"
+#include "mongo/s/chunk_manager.h"
+#include "mongo/s/grid.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+
+ using std::string;
+ using std::vector;
+
+namespace {
+
+ 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 (!client->getAuthorizationSession()->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,
+ bool fromRepl) {
+
+ if (!configServer.allUp(false, errmsg)) {
+ return false;
+ }
+
+ ShardConnection::sync();
+
+ const std::string ns = parseNs(dbname, cmdObj);
+ if (ns.size() == 0) {
+ errmsg = "no ns";
+ return false;
+ }
+
+ DBConfigPtr config = grid.getDBConfig(ns);
+ if (!config->isSharded(ns)) {
+ config->reload();
+
+ if (!config->isSharded(ns)) {
+ errmsg = "ns not sharded. have to shard before can split";
+ return false;
+ }
+ }
+
+ 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;
+ }
+
+ BSONArray bounds;
+ if (FieldParser::extract(cmdObj, boundsField, &bounds, &errmsg) ==
+ FieldParser::FIELD_INVALID) {
+ return false;
+ }
+
+ if (!bounds.isEmpty()) {
+ if (!bounds.hasField("0")) {
+ errmsg = "lower bound not specified";
+ return false;
+ }
+
+ if (!bounds.hasField("1")) {
+ errmsg = "upper bound not specified";
+ return false;
+ }
+ }
+
+ if (!find.isEmpty() && !bounds.isEmpty()) {
+ errmsg = "cannot specify bounds and find at the same time";
+ return false;
+ }
+
+ BSONObj middle;
+ if (FieldParser::extract(cmdObj, middleField, &middle, &errmsg) ==
+ FieldParser::FIELD_INVALID) {
+ return false;
+ }
+
+ if (find.isEmpty() && bounds.isEmpty() && middle.isEmpty()) {
+ errmsg = "need to specify find/bounds or middle";
+ return false;
+ }
+
+ if (!find.isEmpty() && !middle.isEmpty()) {
+ errmsg = "cannot specify find and middle together";
+ return false;
+ }
+
+ if (!bounds.isEmpty() && !middle.isEmpty()) {
+ errmsg = "cannot specify bounds and middle together";
+ return false;
+ }
+
+ // This refreshes the chunk metadata if stale.
+ ChunkManagerPtr info = config->getChunkManager(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;
+ }
+
+ 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;
+ }
+ }
+
+ invariant(chunk.get());
+ log() << "splitting chunk [" << chunk->getMin() << "," << chunk->getMax() << ")"
+ << " in collection " << ns
+ << " on shard " << chunk->getShard().getName();
+
+ 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;
+ }
+ }
+
+ return true;
+ }
+
+ } splitCollectionCmd;
+
+} // namespace
+} // namespace mongo