diff options
Diffstat (limited to 'src/mongo/s/commands/cluster_move_primary_cmd.cpp')
-rw-r--r-- | src/mongo/s/commands/cluster_move_primary_cmd.cpp | 365 |
1 files changed, 174 insertions, 191 deletions
diff --git a/src/mongo/s/commands/cluster_move_primary_cmd.cpp b/src/mongo/s/commands/cluster_move_primary_cmd.cpp index 227830f410d..f9d401a26b6 100644 --- a/src/mongo/s/commands/cluster_move_primary_cmd.cpp +++ b/src/mongo/s/commands/cluster_move_primary_cmd.cpp @@ -51,242 +51,225 @@ namespace mongo { - using std::shared_ptr; - using std::set; - using std::string; +using std::shared_ptr; +using std::set; +using std::string; namespace { - class MoveDatabasePrimaryCommand : public Command { - public: - MoveDatabasePrimaryCommand() : Command("movePrimary", false, "moveprimary") { } +class MoveDatabasePrimaryCommand : public Command { +public: + MoveDatabasePrimaryCommand() : Command("movePrimary", false, "moveprimary") {} - virtual bool slaveOk() const { - return true; - } + virtual bool slaveOk() const { + return true; + } - virtual bool adminOnly() const { - return true; - } + virtual bool adminOnly() const { + return true; + } - virtual bool isWriteCommandForConfigServer() const { - return false; - } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + + virtual void help(std::stringstream& help) const { + help << " example: { moveprimary : 'foo' , to : 'localhost:9999' }"; + } - virtual void help(std::stringstream& help) const { - help << " example: { moveprimary : 'foo' , to : 'localhost:9999' }"; + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( + ResourcePattern::forDatabaseName(parseNs(dbname, cmdObj)), ActionType::moveChunk)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); } - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { + return Status::OK(); + } - if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName( - parseNs(dbname, cmdObj)), - ActionType::moveChunk)) { - return Status(ErrorCodes::Unauthorized, "Unauthorized"); - } + virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { + return cmdObj.firstElement().str(); + } + + virtual bool run(OperationContext* txn, + const std::string& dbname_unused, + BSONObj& cmdObj, + int options, + std::string& errmsg, + BSONObjBuilder& result) { + const string dbname = parseNs("", cmdObj); - return Status::OK(); + if (dbname.empty() || !nsIsDbOnly(dbname)) { + errmsg = "invalid db name specified: " + dbname; + return false; } - virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { - return cmdObj.firstElement().str(); + if (dbname == "admin" || dbname == "config" || dbname == "local") { + errmsg = "can't move primary for " + dbname + " database"; + return false; } - virtual bool run(OperationContext* txn, - const std::string& dbname_unused, - BSONObj& cmdObj, - int options, - std::string& errmsg, - BSONObjBuilder& result) { + // Flush all cached information. This can't be perfect, but it's better than nothing. + grid.catalogCache()->invalidate(dbname); - const string dbname = parseNs("", cmdObj); + auto status = grid.catalogCache()->getDatabase(dbname); + if (!status.isOK()) { + return appendCommandStatus(result, status.getStatus()); + } - if (dbname.empty() || !nsIsDbOnly(dbname)) { - errmsg = "invalid db name specified: " + dbname; - return false; - } + shared_ptr<DBConfig> config = status.getValue(); - if (dbname == "admin" || dbname == "config" || dbname == "local") { - errmsg = "can't move primary for " + dbname + " database"; - return false; - } + const string to = cmdObj["to"].valuestrsafe(); + if (!to.size()) { + errmsg = "you have to specify where you want to move it"; + return false; + } - // Flush all cached information. This can't be perfect, but it's better than nothing. - grid.catalogCache()->invalidate(dbname); + shared_ptr<Shard> toShard = grid.shardRegistry()->getShard(to); + if (!toShard) { + string msg(str::stream() << "Could not move database '" << dbname << "' to shard '" + << to << "' because the shard does not exist"); + log() << msg; + return appendCommandStatus(result, Status(ErrorCodes::ShardNotFound, msg)); + } - auto status = grid.catalogCache()->getDatabase(dbname); - if (!status.isOK()) { - return appendCommandStatus(result, status.getStatus()); - } + shared_ptr<Shard> fromShard = grid.shardRegistry()->getShard(config->getPrimaryId()); + invariant(fromShard); - shared_ptr<DBConfig> config = status.getValue(); + if (fromShard->getConnString().sameLogicalEndpoint(toShard->getConnString())) { + errmsg = "it is already the primary"; + return false; + } - const string to = cmdObj["to"].valuestrsafe(); - if (!to.size()) { - errmsg = "you have to specify where you want to move it"; - return false; - } + if (!grid.catalogManager()->isShardHost(toShard->getConnString())) { + errmsg = "that server isn't known to me"; + return false; + } - shared_ptr<Shard> toShard = grid.shardRegistry()->getShard(to); - if (!toShard) { - string msg(str::stream() << "Could not move database '" << dbname - << "' to shard '" << to - << "' because the shard does not exist"); - log() << msg; - return appendCommandStatus(result, - Status(ErrorCodes::ShardNotFound, msg)); - } + log() << "Moving " << dbname << " primary from: " << fromShard->toString() + << " to: " << toShard->toString(); - shared_ptr<Shard> fromShard = - grid.shardRegistry()->getShard(config->getPrimaryId()); - invariant(fromShard); + string whyMessage(str::stream() << "Moving primary shard of " << dbname); + auto scopedDistLock = + grid.catalogManager()->getDistLockManager()->lock(dbname + "-movePrimary", whyMessage); - if (fromShard->getConnString().sameLogicalEndpoint(toShard->getConnString())) { - errmsg = "it is already the primary"; - return false; - } + if (!scopedDistLock.isOK()) { + return appendCommandStatus(result, scopedDistLock.getStatus()); + } - if (!grid.catalogManager()->isShardHost(toShard->getConnString())) { - errmsg = "that server isn't known to me"; - return false; - } + set<string> shardedColls; + config->getAllShardedCollections(shardedColls); - log() << "Moving " << dbname << " primary from: " - << fromShard->toString() << " to: " << toShard->toString(); + // Record start in changelog + BSONObj moveStartDetails = + _buildMoveEntry(dbname, fromShard->toString(), toShard->toString(), shardedColls); - string whyMessage(str::stream() << "Moving primary shard of " << dbname); - auto scopedDistLock = grid.catalogManager()->getDistLockManager()->lock( - dbname + "-movePrimary", whyMessage); + grid.catalogManager()->logChange(txn, "movePrimary.start", dbname, moveStartDetails); - if (!scopedDistLock.isOK()) { - return appendCommandStatus(result, scopedDistLock.getStatus()); - } + BSONArrayBuilder barr; + barr.append(shardedColls); - set<string> shardedColls; - config->getAllShardedCollections(shardedColls); - - // Record start in changelog - BSONObj moveStartDetails = _buildMoveEntry(dbname, - fromShard->toString(), - toShard->toString(), - shardedColls); - - grid.catalogManager()->logChange(txn, "movePrimary.start", dbname, moveStartDetails); - - BSONArrayBuilder barr; - barr.append(shardedColls); - - ScopedDbConnection toconn(toShard->getConnString()); - - // TODO ERH - we need a clone command which replays operations from clone start to now - // can just use local.oplog.$main - BSONObj cloneRes; - bool worked = toconn->runCommand( - dbname.c_str(), - BSON("clone" << fromShard->getConnString().toString() - << "collsToIgnore" << barr.arr() - << bypassDocumentValidationCommandOption() << true), - cloneRes); - toconn.done(); - - if (!worked) { - log() << "clone failed" << cloneRes; - errmsg = "clone failed"; - return false; - } + ScopedDbConnection toconn(toShard->getConnString()); - const string oldPrimary = fromShard->getConnString().toString(); + // TODO ERH - we need a clone command which replays operations from clone start to now + // can just use local.oplog.$main + BSONObj cloneRes; + bool worked = toconn->runCommand( + dbname.c_str(), + BSON("clone" << fromShard->getConnString().toString() << "collsToIgnore" << barr.arr() + << bypassDocumentValidationCommandOption() << true), + cloneRes); + toconn.done(); - ScopedDbConnection fromconn(fromShard->getConnString()); + if (!worked) { + log() << "clone failed" << cloneRes; + errmsg = "clone failed"; + return false; + } - config->setPrimary(toShard->getConnString().toString()); + const string oldPrimary = fromShard->getConnString().toString(); - if (shardedColls.empty()){ + ScopedDbConnection fromconn(fromShard->getConnString()); - // TODO: Collections can be created in the meantime, and we should handle in the future. - log() << "movePrimary dropping database on " << oldPrimary - << ", no sharded collections in " << dbname; + config->setPrimary(toShard->getConnString().toString()); - try { - fromconn->dropDatabase(dbname.c_str()); - } - catch (DBException& e){ - e.addContext(str::stream() << "movePrimary could not drop the database " - << dbname << " on " << oldPrimary); - throw; - } + if (shardedColls.empty()) { + // TODO: Collections can be created in the meantime, and we should handle in the future. + log() << "movePrimary dropping database on " << oldPrimary + << ", no sharded collections in " << dbname; + try { + fromconn->dropDatabase(dbname.c_str()); + } catch (DBException& e) { + e.addContext(str::stream() << "movePrimary could not drop the database " << dbname + << " on " << oldPrimary); + throw; } - else if (cloneRes["clonedColls"].type() != Array) { - // Legacy behavior from old mongod with sharded collections, *do not* delete - // database, but inform user they can drop manually (or ignore). - warning() << "movePrimary legacy mongod behavior detected. " - << "User must manually remove unsharded collections in database " - << dbname << " on " << oldPrimary; - } - else { - // We moved some unsharded collections, but not all - BSONObjIterator it(cloneRes["clonedColls"].Obj()); - - while (it.more()){ - BSONElement el = it.next(); - if (el.type() == String){ - try { - log() << "movePrimary dropping cloned collection " << el.String() - << " on " << oldPrimary; - fromconn->dropCollection(el.String()); - } - catch (DBException& e){ - e.addContext(str::stream() << "movePrimary could not drop the cloned collection " - << el.String() << " on " << oldPrimary); - throw; - } + } else if (cloneRes["clonedColls"].type() != Array) { + // Legacy behavior from old mongod with sharded collections, *do not* delete + // database, but inform user they can drop manually (or ignore). + warning() << "movePrimary legacy mongod behavior detected. " + << "User must manually remove unsharded collections in database " << dbname + << " on " << oldPrimary; + + } else { + // We moved some unsharded collections, but not all + BSONObjIterator it(cloneRes["clonedColls"].Obj()); + + while (it.more()) { + BSONElement el = it.next(); + if (el.type() == String) { + try { + log() << "movePrimary dropping cloned collection " << el.String() << " on " + << oldPrimary; + fromconn->dropCollection(el.String()); + } catch (DBException& e) { + e.addContext(str::stream() + << "movePrimary could not drop the cloned collection " + << el.String() << " on " << oldPrimary); + throw; } } } - - fromconn.done(); - - result << "primary" << toShard->toString(); - - // Record finish in changelog - BSONObj moveFinishDetails = _buildMoveEntry(dbname, - oldPrimary, - toShard->toString(), - shardedColls); - - grid.catalogManager()->logChange(txn, "movePrimary", dbname, moveFinishDetails); - return true; } - private: - static BSONObj _buildMoveEntry(const string db, - const string from, - const string to, - set<string> shardedColls) { - - BSONObjBuilder details; - details.append("database", db); - details.append("from", from); - details.append("to", to); - - BSONArrayBuilder collB(details.subarrayStart("shardedCollections")); - { - set<string>::iterator it; - for (it = shardedColls.begin(); it != shardedColls.end(); ++it) { - collB.append(*it); - } + fromconn.done(); + + result << "primary" << toShard->toString(); + + // Record finish in changelog + BSONObj moveFinishDetails = + _buildMoveEntry(dbname, oldPrimary, toShard->toString(), shardedColls); + + grid.catalogManager()->logChange(txn, "movePrimary", dbname, moveFinishDetails); + return true; + } + +private: + static BSONObj _buildMoveEntry(const string db, + const string from, + const string to, + set<string> shardedColls) { + BSONObjBuilder details; + details.append("database", db); + details.append("from", from); + details.append("to", to); + + BSONArrayBuilder collB(details.subarrayStart("shardedCollections")); + { + set<string>::iterator it; + for (it = shardedColls.begin(); it != shardedColls.end(); ++it) { + collB.append(*it); } - collB.done(); - - return details.obj(); } + collB.done(); + + return details.obj(); + } - } movePrimary; +} movePrimary; -} // namespace -} // namespace mongo +} // namespace +} // namespace mongo |