summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Schwerin <schwerin@mongodb.com>2015-02-04 18:27:17 -0500
committerAndy Schwerin <schwerin@mongodb.com>2015-02-05 10:54:22 -0500
commit6f37514e83c808470704f85a0375b7f240091af3 (patch)
tree54484cc4935620353e17ee7f232ab5b19ff1f644
parent02c6f0b6c12a0ecbb6d37fd2224f14deefe93ba2 (diff)
downloadmongo-6f37514e83c808470704f85a0375b7f240091af3.tar.gz
SERVER-17173 Emulate listCollections/listIndexes in 3.0 mongos when communicating with 2.6 mongod.
-rw-r--r--src/mongo/db/commands/list_collections.cpp7
-rw-r--r--src/mongo/s/commands_public.cpp170
2 files changed, 135 insertions, 42 deletions
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp
index e9da451096e..3863a05f18b 100644
--- a/src/mongo/db/commands/list_collections.cpp
+++ b/src/mongo/db/commands/list_collections.cpp
@@ -83,8 +83,11 @@ namespace mongo {
BSONElement filterElt = jsobj["filter"];
if (!filterElt.eoo()) {
if (filterElt.type() != mongo::Object) {
- return appendCommandStatus(result, Status(ErrorCodes::BadValue,
- "\"filter\" must be an object"));
+ return appendCommandStatus(
+ result,
+ Status(ErrorCodes::TypeMismatch,
+ str::stream() << "\"filter\" must be of type Object, not " <<
+ typeName(filterElt.type())));
}
StatusWithMatchExpression statusWithMatcher =
MatchExpressionParser::parse(filterElt.Obj());
diff --git a/src/mongo/s/commands_public.cpp b/src/mongo/s/commands_public.cpp
index 29cbb1b5413..0f3ced2ec5b 100644
--- a/src/mongo/s/commands_public.cpp
+++ b/src/mongo/s/commands_public.cpp
@@ -2731,15 +2731,16 @@ namespace mongo {
return ok;
}
- class CmdListCollections : public PublicGridCommand {
- public:
- CmdListCollections() : PublicGridCommand( "listCollections" ) {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::listCollections);
- out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
+ /**
+ * Template algorithm for commands that operate by passing through commands to
+ * the shard primary, and then fall back to an aggregation on the shard primary
+ * if the command isn't available. This is primarily an abstraction of the implementation
+ * of listCollections and listIndexes.
+ */
+ class ListPassthroughWithAggFallbackCommand : public PublicGridCommand {
+ protected:
+ explicit ListPassthroughWithAggFallbackCommand(const char* name) :
+ PublicGridCommand(name) {
}
bool run(OperationContext* txn, const string& dbName,
@@ -2748,57 +2749,146 @@ namespace mongo {
string& errmsg,
BSONObjBuilder& result,
bool) {
- DBConfigPtr conf = grid.getDBConfig( dbName , false );
+ DBConfigPtr conf = grid.getDBConfig(dbName , false);
if (!conf) {
errmsg = str::stream() << "Failed to load db sharding metadata for " << dbName;
return false;
}
- bool retval = passthrough( conf, cmdObj, result );
+ BSONObj cmdResultObj;
+ BSONObjBuilder listCmdResultBuilder;
+ if (passthrough(conf, cmdObj, listCmdResultBuilder)) {
+ cmdResultObj = listCmdResultBuilder.done();
+ }
+ else {
+ BSONObj listCmdResult = listCmdResultBuilder.done();
+ Status status = getStatusFromCommandResult(listCmdResult);
+ invariant(!status.isOK());
+ if (ErrorCodes::CommandNotFound != status) {
+ result.appendElements(listCmdResult);
+ return false;
+ }
- Status storeCursorStatus = storePossibleCursor(conf->getPrimary().getConnString(),
- result.asTempObj());
- if (!storeCursorStatus.isOK()) {
- return appendCommandStatus(result, storeCursorStatus);
+ const BSONObj aggCmdObj = buildAggregationEquivalent(dbName, cmdObj);
+ LOG(1) << "Falling back due to old shard server version from " << cmdObj <<
+ " to " << aggCmdObj;
+ BSONObjBuilder aggCmdResultBuilder;
+ if (passthrough(conf, aggCmdObj, aggCmdResultBuilder)) {
+ cmdResultObj = aggCmdResultBuilder.obj();
+ }
+ else {
+ result.appendElements(aggCmdResultBuilder.done());
+ return false;
+ }
+ }
+ invariant(cmdResultObj["ok"].trueValue()); // passthrough() would have returned
+ // false, otherwise.
+ Status status = storePossibleCursor(conf->getPrimary().getConnString(),
+ cmdResultObj);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
}
+ result.appendElements(cmdResultObj);
+ return true;
+ }
+
+ private:
+ /**
+ * Returns a BSON object describing the aggregation command to run on a 2.6 mongod
+ * if that mongod doesn't support the command described by cmdObj directly.
+ */
+ virtual BSONObj buildAggregationEquivalent(
+ const std::string& dbName, const BSONObj& cmdObj) = 0;
+ };
+
+ class CmdListCollections : public ListPassthroughWithAggFallbackCommand {
+ public:
+ CmdListCollections() : ListPassthroughWithAggFallbackCommand( "listCollections" ) {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::listCollections);
+ out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
+ }
- return retval;
+ private:
+ virtual BSONObj buildAggregationEquivalent(
+ const std::string& dbName, const BSONObj& cmdObj) {
+ static const long long defaultBatchSize = std::numeric_limits<long long>::max();
+ long long batchSize;
+ uassertStatusOK(parseCommandCursorOptions(cmdObj, defaultBatchSize, &batchSize));
+ const BSONElement filterElt = cmdObj["filter"];
+ if (!filterElt.eoo() && filterElt.type() != Object) {
+ uasserted(ErrorCodes::TypeMismatch,
+ str::stream() << "\"filter\" must be of type Object, not " <<
+ typeName(filterElt.type()));
+ }
+
+ BSONObjBuilder aggCmdObjBuilder;
+ aggCmdObjBuilder <<
+ "aggregate" << "system.namespaces" <<
+ "cursor" << BSON("batchSize" << batchSize);
+ {
+ BSONArrayBuilder pipelineBuilder(aggCmdObjBuilder.subarrayStart("pipeline"));
+ pipelineBuilder <<
+ // Filter out namespaces with "$" in their name. These are always indexes,
+ // except for the master/slave oplog local.oplog.$main; however mongos
+ // cannot be used to observe the contents of nodes' local databases, so we
+ // make no exception for it here.
+ BSON("$match" <<
+ BSON("name" << BSONRegEx("^[^$]*$"))) <<
+ // Sort by name, for consistency with listCollections behavior on mongod.
+ BSON("$sort" <<
+ BSON("name" << 1)) <<
+ // Remove the database name qualification of the namespace name.
+ BSON("$project" <<
+ BSON("name" <<
+ BSON("$substr" <<
+ BSON_ARRAY("$name" <<
+ static_cast<long long>(1 + dbName.size()) <<
+ -1)) <<
+ "options" << 1));
+ if (!filterElt.eoo()) {
+ pipelineBuilder << BSON("$match" << filterElt.Obj());
+ }
+ }
+ return aggCmdObjBuilder.obj();
}
} cmdListCollections;
- class CmdListIndexes : public PublicGridCommand {
+ class CmdListIndexes : public ListPassthroughWithAggFallbackCommand {
public:
- CmdListIndexes() : PublicGridCommand( "listIndexes" ) {}
+ CmdListIndexes() : ListPassthroughWithAggFallbackCommand( "listIndexes" ) {}
virtual void addRequiredPrivileges(const std::string& dbname,
const BSONObj& cmdObj,
std::vector<Privilege>* out) {
- string ns = parseNs( dbname, cmdObj );
ActionSet actions;
actions.addAction(ActionType::listIndexes);
out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
}
- bool run(OperationContext* txn, const string& dbName,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result,
- bool) {
- DBConfigPtr conf = grid.getDBConfig( dbName , false );
- if (!conf) {
- errmsg = str::stream() << "Failed to load db sharding metadata for " << dbName;
- return false;
- }
-
- bool retval = passthrough( conf, cmdObj, result );
-
- Status storeCursorStatus = storePossibleCursor(conf->getPrimary().getConnString(),
- result.asTempObj());
- if (!storeCursorStatus.isOK()) {
- return appendCommandStatus(result, storeCursorStatus);
- }
-
- return retval;
+ private:
+ virtual BSONObj buildAggregationEquivalent(
+ const std::string& dbName, const BSONObj& cmdObj) {
+ const BSONElement firstElement = cmdObj.firstElement();
+ uassert(28606,
+ str::stream() <<
+ "Argument to listIndexes must be a non-empty String, but found " <<
+ firstElement.toString(false) << " with type " <<
+ typeName(firstElement.type()),
+ (firstElement.type() == String &&
+ NamespaceString::validCollectionName(firstElement.valueStringData())));
+
+ static const long long defaultBatchSize = std::numeric_limits<long long>::max();
+ long long batchSize;
+ uassertStatusOK(parseCommandCursorOptions(cmdObj, defaultBatchSize, &batchSize));
+
+ return BSON(
+ "aggregate" << "system.indexes" <<
+ "cursor" << BSON("batchSize" << batchSize) <<
+ "pipeline" << BSON_ARRAY(
+ BSON("$match" << BSON("ns" << parseNs(dbName, cmdObj)))));
}
} cmdListIndexes;