diff options
Diffstat (limited to 'src/mongo/s/cluster_explain.cpp')
-rw-r--r-- | src/mongo/s/cluster_explain.cpp | 507 |
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 |