summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2015-09-03 13:07:08 -0400
committerDavid Storch <david.storch@10gen.com>2015-09-03 19:55:59 -0400
commit7a59ba6da674462e7f86a3a7111d58b0721a4138 (patch)
tree279879021e23ee636f2235d368bfbc9f090ef008
parent47ea04eab5c554fbbc7a9d4945c5c9850aeed2ac (diff)
downloadmongo-7a59ba6da674462e7f86a3a7111d58b0721a4138.tar.gz
SERVER-20267 use the explain command path to answer OP_QUERY with $explain on mongos
-rw-r--r--jstests/core/explain_find.js9
-rw-r--r--src/mongo/db/dbmessage.cpp2
-rw-r--r--src/mongo/db/dbmessage.h2
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp31
-rw-r--r--src/mongo/s/query/cluster_find.cpp33
-rw-r--r--src/mongo/s/query/cluster_find.h20
-rw-r--r--src/mongo/s/strategy.cpp31
7 files changed, 99 insertions, 29 deletions
diff --git a/jstests/core/explain_find.js b/jstests/core/explain_find.js
index fe6ac234986..2e2699ea05b 100644
--- a/jstests/core/explain_find.js
+++ b/jstests/core/explain_find.js
@@ -32,3 +32,12 @@ explain = db.runCommand({
printjson(explain);
assert.commandWorked(explain);
assert.eq(2, explain.executionStats.nReturned);
+
+// Compatibility test for the $explain OP_QUERY flag. This can only run if find command is disabled.
+if (!db.getMongo().useReadCommands()) {
+ var explain = t.find({$query: {a: 4}, $explain: true}).limit(-1).next();
+ assert("queryPlanner" in explain);
+ assert("executionStats" in explain);
+ assert.eq(1, explain.executionStats.nReturned);
+ assert("allPlansExecution" in explain.executionStats);
+}
diff --git a/src/mongo/db/dbmessage.cpp b/src/mongo/db/dbmessage.cpp
index b1e6a245c28..eb50ecb063a 100644
--- a/src/mongo/db/dbmessage.cpp
+++ b/src/mongo/db/dbmessage.cpp
@@ -172,7 +172,7 @@ T DbMessage::readAndAdvance() {
void replyToQuery(int queryResultFlags,
AbstractMessagingPort* p,
Message& requestMsg,
- void* data,
+ const void* data,
int size,
int nReturned,
int startingFrom,
diff --git a/src/mongo/db/dbmessage.h b/src/mongo/db/dbmessage.h
index 73c30f09bac..aa74fd59ffb 100644
--- a/src/mongo/db/dbmessage.h
+++ b/src/mongo/db/dbmessage.h
@@ -327,7 +327,7 @@ struct DbResponse {
void replyToQuery(int queryResultFlags,
AbstractMessagingPort* p,
Message& requestMsg,
- void* data,
+ const void* data,
int size,
int nReturned,
int startingFrom = 0,
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index 68cd2c00953..937515d17c7 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -35,10 +35,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/stats/counters.h"
-#include "mongo/s/cluster_explain.h"
#include "mongo/s/query/cluster_find.h"
-#include "mongo/s/strategy.h"
-#include "mongo/util/timer.h"
namespace mongo {
namespace {
@@ -116,31 +113,13 @@ public:
// Parse the command BSON to a LiteParsedQuery.
bool isExplain = true;
- auto lpqStatus = LiteParsedQuery::makeFromFindCommand(std::move(nss), cmdObj, isExplain);
- if (!lpqStatus.isOK()) {
- return lpqStatus.getStatus();
+ auto lpq = LiteParsedQuery::makeFromFindCommand(std::move(nss), cmdObj, isExplain);
+ if (!lpq.isOK()) {
+ return lpq.getStatus();
}
- auto& lpq = lpqStatus.getValue();
-
- BSONObjBuilder explainCmdBob;
- int options = 0;
- ClusterExplain::wrapAsExplain(
- cmdObj, verbosity, serverSelectionMetadata, &explainCmdBob, &options);
-
- // We will time how long it takes to run the commands on the shards.
- Timer timer;
-
- vector<Strategy::CommandResult> shardResults;
- Strategy::commandOp(
- txn, dbname, explainCmdBob.obj(), options, fullns, lpq->getFilter(), &shardResults);
-
- long long millisElapsed = timer.millis();
-
- const char* mongosStageName = ClusterExplain::getStageNameForReadOp(shardResults, cmdObj);
-
- return ClusterExplain::buildExplainResult(
- txn, shardResults, mongosStageName, millisElapsed, out);
+ return ClusterFind::runExplain(
+ txn, cmdObj, *lpq.getValue(), verbosity, serverSelectionMetadata, out);
}
bool run(OperationContext* txn,
diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp
index 0d0fb418333..4f40dd3c795 100644
--- a/src/mongo/s/query/cluster_find.cpp
+++ b/src/mongo/s/query/cluster_find.cpp
@@ -42,9 +42,11 @@
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/getmore_request.h"
+#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/catalog/catalog_cache.h"
#include "mongo/s/chunk_manager.h"
#include "mongo/s/client/shard_registry.h"
+#include "mongo/s/cluster_explain.h"
#include "mongo/s/config.h"
#include "mongo/s/grid.h"
#include "mongo/s/query/cluster_client_cursor_impl.h"
@@ -329,6 +331,37 @@ StatusWith<CursorResponse> ClusterFind::runGetMore(OperationContext* txn,
return CursorResponse(request.nss, idToReturn, std::move(batch), startingFrom);
}
+Status ClusterFind::runExplain(OperationContext* txn,
+ const BSONObj& findCommand,
+ const LiteParsedQuery& lpq,
+ ExplainCommon::Verbosity verbosity,
+ const rpc::ServerSelectionMetadata& serverSelectionMetadata,
+ BSONObjBuilder* out) {
+ BSONObjBuilder explainCmdBob;
+ int options = 0;
+ ClusterExplain::wrapAsExplain(
+ findCommand, verbosity, serverSelectionMetadata, &explainCmdBob, &options);
+
+ // We will time how long it takes to run the commands on the shards.
+ Timer timer;
+
+ std::vector<Strategy::CommandResult> shardResults;
+ Strategy::commandOp(txn,
+ lpq.nss().db().toString(),
+ explainCmdBob.obj(),
+ options,
+ lpq.nss().toString(),
+ lpq.getFilter(),
+ &shardResults);
+
+ long long millisElapsed = timer.millis();
+
+ const char* mongosStageName = ClusterExplain::getStageNameForReadOp(shardResults, findCommand);
+
+ return ClusterExplain::buildExplainResult(
+ txn, shardResults, mongosStageName, millisElapsed, out);
+}
+
StatusWith<ReadPreferenceSetting> ClusterFind::extractUnwrappedReadPref(const BSONObj& cmdObj,
const bool isSlaveOk) {
BSONElement queryOptionsElt;
diff --git a/src/mongo/s/query/cluster_find.h b/src/mongo/s/query/cluster_find.h
index ad878a11b37..7a7d3deac69 100644
--- a/src/mongo/s/query/cluster_find.h
+++ b/src/mongo/s/query/cluster_find.h
@@ -33,6 +33,7 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/db/cursor_id.h"
#include "mongo/db/query/cursor_response.h"
+#include "mongo/db/query/explain_common.h"
namespace mongo {
@@ -43,6 +44,10 @@ class OperationContext;
struct GetMoreRequest;
struct ReadPreferenceSetting;
+namespace rpc {
+class ServerSelectionMetadata;
+} // namespace rpc
+
/**
* Methods for running find and getMore operations across a sharded cluster.
*/
@@ -71,6 +76,21 @@ public:
const GetMoreRequest& request);
/**
+ * Helper to run an explain of a find operation on the shards. Fills 'out' with the result of
+ * the of the explain command on success. On failure, returns a non-OK status and does not
+ * modify 'out'.
+ *
+ * Used both if mongos receives an explain command and if it receives an OP_QUERY find with the
+ * $explain modifier.
+ */
+ static Status runExplain(OperationContext* txn,
+ const BSONObj& findCommand,
+ const LiteParsedQuery& lpq,
+ ExplainCommon::Verbosity verbosity,
+ const rpc::ServerSelectionMetadata& serverSelectionMetadata,
+ BSONObjBuilder* out);
+
+ /**
* Extracts the read preference from 'cmdObj', or determines the read pref based on 'isSlaveOk'
* if 'cmdObj' does not contain a read preference.
*
diff --git a/src/mongo/s/strategy.cpp b/src/mongo/s/strategy.cpp
index 89bc8a0e2d4..357cd8843c0 100644
--- a/src/mongo/s/strategy.cpp
+++ b/src/mongo/s/strategy.cpp
@@ -49,6 +49,7 @@
#include "mongo/db/query/lite_parsed_query.h"
#include "mongo/db/query/getmore_request.h"
#include "mongo/db/stats/counters.h"
+#include "mongo/rpc/metadata/server_selection_metadata.h"
#include "mongo/s/bson_serializable.h"
#include "mongo/s/catalog/catalog_cache.h"
#include "mongo/s/client/shard_registry.h"
@@ -194,6 +195,34 @@ void Strategy::queryOp(OperationContext* txn, Request& request) {
auto canonicalQuery = CanonicalQuery::canonicalize(q, WhereCallbackNoop());
uassertStatusOK(canonicalQuery.getStatus());
+ // If the $explain flag was set, we must run the operation on the shards as an explain
+ // command rather than a find command.
+ if (canonicalQuery.getValue()->getParsed().isExplain()) {
+ const LiteParsedQuery& lpq = canonicalQuery.getValue()->getParsed();
+ BSONObj findCommand = lpq.asFindCommand();
+
+ // We default to allPlansExecution verbosity.
+ auto verbosity = ExplainCommon::EXEC_ALL_PLANS;
+
+ const bool secondaryOk = (readPreference.pref != ReadPreference::PrimaryOnly);
+ rpc::ServerSelectionMetadata metadata(secondaryOk, readPreference);
+
+ BSONObjBuilder explainBuilder;
+ uassertStatusOK(ClusterFind::runExplain(
+ txn, findCommand, lpq, verbosity, metadata, &explainBuilder));
+
+ BSONObj explainObj = explainBuilder.done();
+ replyToQuery(0, // query result flags
+ request.p(),
+ request.m(),
+ static_cast<const void*>(explainObj.objdata()),
+ explainObj.objsize(),
+ 1, // numResults
+ 0, // startingFrom
+ CursorId(0));
+ return;
+ }
+
// Do the work to generate the first batch of results. This blocks waiting to get responses
// from the shard(s).
std::vector<BSONObj> batch;
@@ -205,11 +234,11 @@ void Strategy::queryOp(OperationContext* txn, Request& request) {
ClusterFind::runQuery(txn, *canonicalQuery.getValue(), readPreference, &batch);
uassertStatusOK(cursorId.getStatus());
- // Build the response document.
// TODO: this constant should be shared between mongos and mongod, and should
// not be inside ShardedClientCursor.
BufBuilder buffer(ShardedClientCursor::INIT_REPLY_BUFFER_SIZE);
+ // Fill out the response buffer.
int numResults = 0;
for (const auto& obj : batch) {
buffer.appendBuf((void*)obj.objdata(), obj.objsize());