summaryrefslogtreecommitdiff
path: root/src/mongo/s/cluster_explain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/cluster_explain.cpp')
-rw-r--r--src/mongo/s/cluster_explain.cpp507
1 files changed, 248 insertions, 259 deletions
diff --git a/src/mongo/s/cluster_explain.cpp b/src/mongo/s/cluster_explain.cpp
index 75b63382540..de42b51b940 100644
--- a/src/mongo/s/cluster_explain.cpp
+++ b/src/mongo/s/cluster_explain.cpp
@@ -34,320 +34,309 @@
namespace mongo {
- using std::vector;
-
- const char* ClusterExplain::kSingleShard = "SINGLE_SHARD";
- const char* ClusterExplain::kMergeFromShards = "SHARD_MERGE";
- const char* ClusterExplain::kMergeSortFromShards = "SHARD_MERGE_SORT";
- const char* ClusterExplain::kWriteOnShards = "SHARD_WRITE";
-
- namespace {
-
- //
- // BSON size limit management: these functions conditionally append to a
- // BSON object or BSON array buidlder, depending on whether or not the
- // maximum user size for a BSON object will be exceeded.
- //
-
- bool appendIfRoom(BSONObjBuilder* bob,
- const BSONObj& toAppend,
- const StringData& fieldName) {
- if ((bob->len() + toAppend.objsize()) < BSONObjMaxUserSize) {
- bob->append(fieldName, toAppend);
- return true;
- }
+using std::vector;
- // Unless 'bob' has already exceeded the max BSON user size, add a warning indicating
- // that data has been truncated.
- if (bob->len() < BSONObjMaxUserSize) {
- bob->append("warning",
- "output truncated due to nearing BSON max user size");
- }
+const char* ClusterExplain::kSingleShard = "SINGLE_SHARD";
+const char* ClusterExplain::kMergeFromShards = "SHARD_MERGE";
+const char* ClusterExplain::kMergeSortFromShards = "SHARD_MERGE_SORT";
+const char* ClusterExplain::kWriteOnShards = "SHARD_WRITE";
- return false;
- }
+namespace {
- bool appendToArrayIfRoom(BSONArrayBuilder* arrayBuilder,
- const BSONElement& toAppend) {
- if ((arrayBuilder->len() + toAppend.size()) < BSONObjMaxUserSize) {
- arrayBuilder->append(toAppend);
- return true;
- }
+//
+// BSON size limit management: these functions conditionally append to a
+// BSON object or BSON array buidlder, depending on whether or not the
+// maximum user size for a BSON object will be exceeded.
+//
- // Unless 'arrayBuilder' has already exceeded the max BSON user size, add a warning
- // indicating that data has been truncated.
- if (arrayBuilder->len() < BSONObjMaxUserSize) {
- arrayBuilder->append(BSON("warning" <<
- "output truncated due to nearing BSON max user size"));
- }
+bool appendIfRoom(BSONObjBuilder* bob, const BSONObj& toAppend, const StringData& fieldName) {
+ if ((bob->len() + toAppend.objsize()) < BSONObjMaxUserSize) {
+ bob->append(fieldName, toAppend);
+ return true;
+ }
- return false;
- }
+ // Unless 'bob' has already exceeded the max BSON user size, add a warning indicating
+ // that data has been truncated.
+ if (bob->len() < BSONObjMaxUserSize) {
+ bob->append("warning", "output truncated due to nearing BSON max user size");
+ }
- bool appendElementsIfRoom(BSONObjBuilder* bob,
- const BSONObj& toAppend) {
- if ((bob->len() + toAppend.objsize()) < BSONObjMaxUserSize) {
- bob->appendElements(toAppend);
- return true;
- }
+ return false;
+}
- // Unless 'bob' has already exceeded the max BSON user size, add a warning indicating
- // that data has been truncated.
- if (bob->len() < BSONObjMaxUserSize) {
- bob->append("warning",
- "output truncated due to nearing BSON max user size");
- }
+bool appendToArrayIfRoom(BSONArrayBuilder* arrayBuilder, const BSONElement& toAppend) {
+ if ((arrayBuilder->len() + toAppend.size()) < BSONObjMaxUserSize) {
+ arrayBuilder->append(toAppend);
+ return true;
+ }
- return false;
- }
+ // Unless 'arrayBuilder' has already exceeded the max BSON user size, add a warning
+ // indicating that data has been truncated.
+ if (arrayBuilder->len() < BSONObjMaxUserSize) {
+ arrayBuilder->append(BSON("warning"
+ << "output truncated due to nearing BSON max user size"));
+ }
- } // namespace
+ return false;
+}
- // static
- void ClusterExplain::wrapAsExplain(const BSONObj& cmdObj,
- ExplainCommon::Verbosity verbosity,
- BSONObjBuilder* out) {
- out->append("explain", cmdObj);
- out->append("verbosity", ExplainCommon::verbosityString(verbosity));
+bool appendElementsIfRoom(BSONObjBuilder* bob, const BSONObj& toAppend) {
+ if ((bob->len() + toAppend.objsize()) < BSONObjMaxUserSize) {
+ bob->appendElements(toAppend);
+ return true;
+ }
- // If the command has a readPreference, then pull it up to the top level.
- if (cmdObj.hasField("$readPreference")) {
- out->append("$queryOptions", cmdObj["$readPreference"].wrap());
- }
+ // Unless 'bob' has already exceeded the max BSON user size, add a warning indicating
+ // that data has been truncated.
+ if (bob->len() < BSONObjMaxUserSize) {
+ bob->append("warning", "output truncated due to nearing BSON max user size");
}
- // static
- Status ClusterExplain::validateShardResults(
- const vector<Strategy::CommandResult>& shardResults) {
- // Error we didn't get results from any shards.
- if (shardResults.empty()) {
- return Status(ErrorCodes::InternalError, "no shards found for explain");
- }
+ return false;
+}
- // Count up the number of shards that have execution stats and all plans
- // execution level information.
- size_t numShardsExecStats = 0;
- size_t numShardsAllPlansStats = 0;
-
- // Check that the result from each shard has a true value for "ok" and has
- // the expected "queryPlanner" field.
- for (size_t i = 0; i < shardResults.size(); i++) {
- if (!shardResults[i].result["ok"].trueValue()) {
- // Try to pass up the error code from the shard.
- ErrorCodes::Error error = ErrorCodes::OperationFailed;
- if (shardResults[i].result["code"].isNumber()) {
- error = ErrorCodes::fromInt(shardResults[i].result["code"].numberInt());
- }
-
- return Status(error, str::stream()
- << "Explain command on shard " << shardResults[i].target.toString()
- << " failed, caused by: " << shardResults[i].result);
- }
+} // namespace
- if (Object != shardResults[i].result["queryPlanner"].type()) {
- return Status(ErrorCodes::OperationFailed, str::stream()
- << "Explain command on shard " << shardResults[i].target.toString()
- << " failed, caused by: " << shardResults[i].result);
- }
+// static
+void ClusterExplain::wrapAsExplain(const BSONObj& cmdObj,
+ ExplainCommon::Verbosity verbosity,
+ BSONObjBuilder* out) {
+ out->append("explain", cmdObj);
+ out->append("verbosity", ExplainCommon::verbosityString(verbosity));
+
+ // If the command has a readPreference, then pull it up to the top level.
+ if (cmdObj.hasField("$readPreference")) {
+ out->append("$queryOptions", cmdObj["$readPreference"].wrap());
+ }
+}
+
+// static
+Status ClusterExplain::validateShardResults(const vector<Strategy::CommandResult>& shardResults) {
+ // Error we didn't get results from any shards.
+ if (shardResults.empty()) {
+ return Status(ErrorCodes::InternalError, "no shards found for explain");
+ }
- if (shardResults[i].result.hasField("executionStats")) {
- numShardsExecStats++;
- BSONObj execStats = shardResults[i].result["executionStats"].Obj();
- if (execStats.hasField("allPlansExecution")) {
- numShardsAllPlansStats++;
- }
+ // Count up the number of shards that have execution stats and all plans
+ // execution level information.
+ size_t numShardsExecStats = 0;
+ size_t numShardsAllPlansStats = 0;
+
+ // Check that the result from each shard has a true value for "ok" and has
+ // the expected "queryPlanner" field.
+ for (size_t i = 0; i < shardResults.size(); i++) {
+ if (!shardResults[i].result["ok"].trueValue()) {
+ // Try to pass up the error code from the shard.
+ ErrorCodes::Error error = ErrorCodes::OperationFailed;
+ if (shardResults[i].result["code"].isNumber()) {
+ error = ErrorCodes::fromInt(shardResults[i].result["code"].numberInt());
}
+
+ return Status(error,
+ str::stream() << "Explain command on shard "
+ << shardResults[i].target.toString()
+ << " failed, caused by: " << shardResults[i].result);
}
- // Either all shards should have execution stats info, or none should.
- if (0 != numShardsExecStats && shardResults.size() != numShardsExecStats) {
- return Status(ErrorCodes::InternalError,
- str::stream() << "Only " << numShardsExecStats
- << "/" << shardResults.size()
- << " had executionStats explain information.");
+ if (Object != shardResults[i].result["queryPlanner"].type()) {
+ return Status(ErrorCodes::OperationFailed,
+ str::stream() << "Explain command on shard "
+ << shardResults[i].target.toString()
+ << " failed, caused by: " << shardResults[i].result);
}
- // Either all shards should have all plans execution stats, or none should.
- if (0 != numShardsAllPlansStats && shardResults.size() != numShardsAllPlansStats) {
- return Status(ErrorCodes::InternalError,
- str::stream() << "Only " << numShardsAllPlansStats
- << "/" << shardResults.size()
- << " had allPlansExecution explain information.");
+ if (shardResults[i].result.hasField("executionStats")) {
+ numShardsExecStats++;
+ BSONObj execStats = shardResults[i].result["executionStats"].Obj();
+ if (execStats.hasField("allPlansExecution")) {
+ numShardsAllPlansStats++;
+ }
}
+ }
- return Status::OK();
+ // Either all shards should have execution stats info, or none should.
+ if (0 != numShardsExecStats && shardResults.size() != numShardsExecStats) {
+ return Status(ErrorCodes::InternalError,
+ str::stream() << "Only " << numShardsExecStats << "/" << shardResults.size()
+ << " had executionStats explain information.");
}
- // static
- const char* ClusterExplain::getStageNameForReadOp(
- const vector<Strategy::CommandResult>& shardResults,
- const BSONObj& explainObj) {
- if (shardResults.size() == 1) {
- return kSingleShard;
- }
- else if (explainObj.hasField("sort")) {
- return kMergeSortFromShards;
- }
- else {
- return kMergeFromShards;
- }
+ // Either all shards should have all plans execution stats, or none should.
+ if (0 != numShardsAllPlansStats && shardResults.size() != numShardsAllPlansStats) {
+ return Status(ErrorCodes::InternalError,
+ str::stream() << "Only " << numShardsAllPlansStats << "/"
+ << shardResults.size()
+ << " had allPlansExecution explain information.");
}
- // static
- void ClusterExplain::buildPlannerInfo(const vector<Strategy::CommandResult>& shardResults,
- const char* mongosStageName,
- BSONObjBuilder* out) {
- BSONObjBuilder queryPlannerBob(out->subobjStart("queryPlanner"));
+ return Status::OK();
+}
+
+// static
+const char* ClusterExplain::getStageNameForReadOp(
+ const vector<Strategy::CommandResult>& shardResults, const BSONObj& explainObj) {
+ if (shardResults.size() == 1) {
+ return kSingleShard;
+ } else if (explainObj.hasField("sort")) {
+ return kMergeSortFromShards;
+ } else {
+ return kMergeFromShards;
+ }
+}
- queryPlannerBob.appendNumber("mongosPlannerVersion", 1);
- BSONObjBuilder winningPlanBob(queryPlannerBob.subobjStart("winningPlan"));
+// static
+void ClusterExplain::buildPlannerInfo(const vector<Strategy::CommandResult>& shardResults,
+ const char* mongosStageName,
+ BSONObjBuilder* out) {
+ BSONObjBuilder queryPlannerBob(out->subobjStart("queryPlanner"));
- winningPlanBob.append("stage", mongosStageName);
- BSONArrayBuilder shardsBuilder(winningPlanBob.subarrayStart("shards"));
- for (size_t i = 0; i < shardResults.size(); i++) {
- BSONObjBuilder singleShardBob(shardsBuilder.subobjStart());
+ queryPlannerBob.appendNumber("mongosPlannerVersion", 1);
+ BSONObjBuilder winningPlanBob(queryPlannerBob.subobjStart("winningPlan"));
- BSONObj queryPlanner = shardResults[i].result["queryPlanner"].Obj();
- BSONObj serverInfo = shardResults[i].result["serverInfo"].Obj();
+ winningPlanBob.append("stage", mongosStageName);
+ BSONArrayBuilder shardsBuilder(winningPlanBob.subarrayStart("shards"));
+ for (size_t i = 0; i < shardResults.size(); i++) {
+ BSONObjBuilder singleShardBob(shardsBuilder.subobjStart());
- singleShardBob.append("shardName", shardResults[i].shardTarget.getName());
- std::string connStr = shardResults[i].shardTarget.getAddress().toString();
- singleShardBob.append("connectionString", connStr);
- appendIfRoom(&singleShardBob, serverInfo, "serverInfo");
- appendElementsIfRoom(&singleShardBob, queryPlanner);
+ BSONObj queryPlanner = shardResults[i].result["queryPlanner"].Obj();
+ BSONObj serverInfo = shardResults[i].result["serverInfo"].Obj();
- singleShardBob.doneFast();
- }
- shardsBuilder.doneFast();
- winningPlanBob.doneFast();
- queryPlannerBob.doneFast();
+ singleShardBob.append("shardName", shardResults[i].shardTarget.getName());
+ std::string connStr = shardResults[i].shardTarget.getAddress().toString();
+ singleShardBob.append("connectionString", connStr);
+ appendIfRoom(&singleShardBob, serverInfo, "serverInfo");
+ appendElementsIfRoom(&singleShardBob, queryPlanner);
+
+ singleShardBob.doneFast();
+ }
+ shardsBuilder.doneFast();
+ winningPlanBob.doneFast();
+ queryPlannerBob.doneFast();
+}
+
+// static
+void ClusterExplain::buildExecStats(const vector<Strategy::CommandResult>& shardResults,
+ const char* mongosStageName,
+ long long millisElapsed,
+ BSONObjBuilder* out) {
+ if (!shardResults[0].result.hasField("executionStats")) {
+ // The shards don't have execution stats info. Bail out without adding anything
+ // to 'out'.
+ return;
}
- // static
- void ClusterExplain::buildExecStats(const vector<Strategy::CommandResult>& shardResults,
- const char* mongosStageName,
- long long millisElapsed,
- BSONObjBuilder* out) {
- if (!shardResults[0].result.hasField("executionStats")) {
- // The shards don't have execution stats info. Bail out without adding anything
- // to 'out'.
- return;
+ BSONObjBuilder executionStatsBob(out->subobjStart("executionStats"));
+
+ // Collect summary stats from the shards.
+ long long nReturned = 0;
+ long long keysExamined = 0;
+ long long docsExamined = 0;
+ long long totalChildMillis = 0;
+ for (size_t i = 0; i < shardResults.size(); i++) {
+ BSONObj execStats = shardResults[i].result["executionStats"].Obj();
+ if (execStats.hasField("nReturned")) {
+ nReturned += execStats["nReturned"].numberLong();
}
-
- BSONObjBuilder executionStatsBob(out->subobjStart("executionStats"));
-
- // Collect summary stats from the shards.
- long long nReturned = 0;
- long long keysExamined = 0;
- long long docsExamined = 0;
- long long totalChildMillis = 0;
- for (size_t i = 0; i < shardResults.size(); i++) {
- BSONObj execStats = shardResults[i].result["executionStats"].Obj();
- if (execStats.hasField("nReturned")) {
- nReturned += execStats["nReturned"].numberLong();
- }
- if (execStats.hasField("totalKeysExamined")) {
- keysExamined += execStats["totalKeysExamined"].numberLong();
- }
- if (execStats.hasField("totalDocsExamined")) {
- docsExamined += execStats["totalDocsExamined"].numberLong();
- }
- if (execStats.hasField("executionTimeMillis")) {
- totalChildMillis += execStats["executionTimeMillis"].numberLong();
- }
+ if (execStats.hasField("totalKeysExamined")) {
+ keysExamined += execStats["totalKeysExamined"].numberLong();
}
+ if (execStats.hasField("totalDocsExamined")) {
+ docsExamined += execStats["totalDocsExamined"].numberLong();
+ }
+ if (execStats.hasField("executionTimeMillis")) {
+ totalChildMillis += execStats["executionTimeMillis"].numberLong();
+ }
+ }
- // Fill in top-level stats.
- executionStatsBob.appendNumber("nReturned", nReturned);
- executionStatsBob.appendNumber("executionTimeMillis", millisElapsed);
- executionStatsBob.appendNumber("totalKeysExamined", keysExamined);
- executionStatsBob.appendNumber("totalDocsExamined", docsExamined);
-
- // Fill in the tree of stages.
- BSONObjBuilder executionStagesBob(
- executionStatsBob.subobjStart("executionStages"));
-
- // Info for the root mongos stage.
- executionStagesBob.append("stage", mongosStageName);
- executionStatsBob.appendNumber("nReturned", nReturned);
- executionStatsBob.appendNumber("executionTimeMillis", millisElapsed);
- executionStatsBob.appendNumber("totalKeysExamined", keysExamined);
- executionStatsBob.appendNumber("totalDocsExamined", docsExamined);
- executionStagesBob.append("totalChildMillis", totalChildMillis);
+ // Fill in top-level stats.
+ executionStatsBob.appendNumber("nReturned", nReturned);
+ executionStatsBob.appendNumber("executionTimeMillis", millisElapsed);
+ executionStatsBob.appendNumber("totalKeysExamined", keysExamined);
+ executionStatsBob.appendNumber("totalDocsExamined", docsExamined);
- BSONArrayBuilder execShardsBuilder(executionStagesBob.subarrayStart("shards"));
- for (size_t i = 0; i < shardResults.size(); i++) {
- BSONObjBuilder singleShardBob(execShardsBuilder.subobjStart());
+ // Fill in the tree of stages.
+ BSONObjBuilder executionStagesBob(executionStatsBob.subobjStart("executionStages"));
- BSONObj execStats = shardResults[i].result["executionStats"].Obj();
- BSONObj execStages = execStats["executionStages"].Obj();
+ // Info for the root mongos stage.
+ executionStagesBob.append("stage", mongosStageName);
+ executionStatsBob.appendNumber("nReturned", nReturned);
+ executionStatsBob.appendNumber("executionTimeMillis", millisElapsed);
+ executionStatsBob.appendNumber("totalKeysExamined", keysExamined);
+ executionStatsBob.appendNumber("totalDocsExamined", docsExamined);
+ executionStagesBob.append("totalChildMillis", totalChildMillis);
- singleShardBob.append("shardName", shardResults[i].shardTarget.getName());
+ BSONArrayBuilder execShardsBuilder(executionStagesBob.subarrayStart("shards"));
+ for (size_t i = 0; i < shardResults.size(); i++) {
+ BSONObjBuilder singleShardBob(execShardsBuilder.subobjStart());
- // Append error-related fields, if present.
- if (!execStats["executionSuccess"].eoo()) {
- singleShardBob.append(execStats["executionSuccess"]);
- }
- if (!execStats["errorMessage"].eoo()) {
- singleShardBob.append(execStats["errorMessage"]);
- }
- if (!execStats["errorCode"].eoo()) {
- singleShardBob.append(execStats["errorCode"]);
- }
+ BSONObj execStats = shardResults[i].result["executionStats"].Obj();
+ BSONObj execStages = execStats["executionStages"].Obj();
- appendIfRoom(&singleShardBob, execStages, "executionStages");
+ singleShardBob.append("shardName", shardResults[i].shardTarget.getName());
- singleShardBob.doneFast();
+ // Append error-related fields, if present.
+ if (!execStats["executionSuccess"].eoo()) {
+ singleShardBob.append(execStats["executionSuccess"]);
+ }
+ if (!execStats["errorMessage"].eoo()) {
+ singleShardBob.append(execStats["errorMessage"]);
+ }
+ if (!execStats["errorCode"].eoo()) {
+ singleShardBob.append(execStats["errorCode"]);
}
- execShardsBuilder.doneFast();
- executionStagesBob.doneFast();
+ appendIfRoom(&singleShardBob, execStages, "executionStages");
- if (!shardResults[0].result["executionStats"].Obj().hasField("allPlansExecution")) {
- // The shards don't have execution stats for all plans, so we're done.
- executionStatsBob.doneFast();
- return;
- }
+ singleShardBob.doneFast();
+ }
+ execShardsBuilder.doneFast();
- // Add the allPlans stats from each shard.
- BSONArrayBuilder allPlansExecBob(
- executionStatsBob.subarrayStart("allPlansExecution"));
- for (size_t i = 0; i < shardResults.size(); i++) {
- BSONObjBuilder singleShardBob(execShardsBuilder.subobjStart());
+ executionStagesBob.doneFast();
- singleShardBob.append("shardName", shardResults[i].shardTarget.getName());
+ if (!shardResults[0].result["executionStats"].Obj().hasField("allPlansExecution")) {
+ // The shards don't have execution stats for all plans, so we're done.
+ executionStatsBob.doneFast();
+ return;
+ }
- BSONObj execStats = shardResults[i].result["executionStats"].Obj();
- vector<BSONElement> allPlans = execStats["allPlansExecution"].Array();
+ // Add the allPlans stats from each shard.
+ BSONArrayBuilder allPlansExecBob(executionStatsBob.subarrayStart("allPlansExecution"));
+ for (size_t i = 0; i < shardResults.size(); i++) {
+ BSONObjBuilder singleShardBob(execShardsBuilder.subobjStart());
- BSONArrayBuilder innerArrayBob(singleShardBob.subarrayStart("allPlans"));
- for (size_t j = 0; j < allPlans.size(); j++) {
- appendToArrayIfRoom(&innerArrayBob, allPlans[j]);
- }
- innerArrayBob.done();
+ singleShardBob.append("shardName", shardResults[i].shardTarget.getName());
- singleShardBob.doneFast();
+ BSONObj execStats = shardResults[i].result["executionStats"].Obj();
+ vector<BSONElement> allPlans = execStats["allPlansExecution"].Array();
+
+ BSONArrayBuilder innerArrayBob(singleShardBob.subarrayStart("allPlans"));
+ for (size_t j = 0; j < allPlans.size(); j++) {
+ appendToArrayIfRoom(&innerArrayBob, allPlans[j]);
}
- allPlansExecBob.doneFast();
+ innerArrayBob.done();
- executionStatsBob.doneFast();
+ singleShardBob.doneFast();
}
+ allPlansExecBob.doneFast();
- // static
- Status ClusterExplain::buildExplainResult(const vector<Strategy::CommandResult>& shardResults,
- const char* mongosStageName,
- long long millisElapsed,
- BSONObjBuilder* out) {
- // Explain only succeeds if all shards support the explain command.
- Status validateStatus = ClusterExplain::validateShardResults(shardResults);
- if (!validateStatus.isOK()) {
- return validateStatus;
- }
-
- buildPlannerInfo(shardResults, mongosStageName, out);
- buildExecStats(shardResults, mongosStageName, millisElapsed, out);
+ executionStatsBob.doneFast();
+}
- return Status::OK();
+// static
+Status ClusterExplain::buildExplainResult(const vector<Strategy::CommandResult>& shardResults,
+ const char* mongosStageName,
+ long long millisElapsed,
+ BSONObjBuilder* out) {
+ // Explain only succeeds if all shards support the explain command.
+ Status validateStatus = ClusterExplain::validateShardResults(shardResults);
+ if (!validateStatus.isOK()) {
+ return validateStatus;
}
-} // namespace mongo
+ buildPlannerInfo(shardResults, mongosStageName, out);
+ buildExecStats(shardResults, mongosStageName, millisElapsed, out);
+
+ return Status::OK();
+}
+
+} // namespace mongo