diff options
Diffstat (limited to 'src/mongo/db/commands/plan_cache_commands.cpp')
-rw-r--r-- | src/mongo/db/commands/plan_cache_commands.cpp | 656 |
1 files changed, 329 insertions, 327 deletions
diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp index 3fc5d8f313e..1fc0b40493c 100644 --- a/src/mongo/db/commands/plan_cache_commands.cpp +++ b/src/mongo/db/commands/plan_cache_commands.cpp @@ -48,399 +48,401 @@ namespace { - using std::string; - using std::unique_ptr; - using namespace mongo; - - /** - * Utility function to extract error code and message from status - * and append to BSON results. - */ - void addStatus(const Status& status, BSONObjBuilder& builder) { - builder.append("ok", status.isOK() ? 1.0 : 0.0); - if (!status.isOK()) { - builder.append("code", status.code()); - } - if (!status.reason().empty()) { - builder.append("errmsg", status.reason()); - } - } +using std::string; +using std::unique_ptr; +using namespace mongo; - /** - * Retrieves a collection's plan cache from the database. - */ - static Status getPlanCache(OperationContext* txn, - Collection* collection, - const string& ns, - PlanCache** planCacheOut) { - *planCacheOut = NULL; - - if (NULL == collection) { - return Status(ErrorCodes::BadValue, "no such collection"); - } +/** + * Utility function to extract error code and message from status + * and append to BSON results. + */ +void addStatus(const Status& status, BSONObjBuilder& builder) { + builder.append("ok", status.isOK() ? 1.0 : 0.0); + if (!status.isOK()) { + builder.append("code", status.code()); + } + if (!status.reason().empty()) { + builder.append("errmsg", status.reason()); + } +} - CollectionInfoCache* infoCache = collection->infoCache(); - invariant(infoCache); +/** + * Retrieves a collection's plan cache from the database. + */ +static Status getPlanCache(OperationContext* txn, + Collection* collection, + const string& ns, + PlanCache** planCacheOut) { + *planCacheOut = NULL; + + if (NULL == collection) { + return Status(ErrorCodes::BadValue, "no such collection"); + } - PlanCache* planCache = infoCache->getPlanCache(); - invariant(planCache); + CollectionInfoCache* infoCache = collection->infoCache(); + invariant(infoCache); - *planCacheOut = planCache; - return Status::OK(); - } + PlanCache* planCache = infoCache->getPlanCache(); + invariant(planCache); - // - // Command instances. - // Registers commands with the command system and make commands - // available to the client. - // + *planCacheOut = planCache; + return Status::OK(); +} - MONGO_INITIALIZER_WITH_PREREQUISITES(SetupPlanCacheCommands, MONGO_NO_PREREQUISITES)( - InitializerContext* context) { +// +// Command instances. +// Registers commands with the command system and make commands +// available to the client. +// - // PlanCacheCommand constructors refer to static ActionType instances. - // Registering commands in a mongo static initializer ensures that - // the ActionType construction will be completed first. - new PlanCacheListQueryShapes(); - new PlanCacheClear(); - new PlanCacheListPlans(); +MONGO_INITIALIZER_WITH_PREREQUISITES(SetupPlanCacheCommands, + MONGO_NO_PREREQUISITES)(InitializerContext* context) { + // PlanCacheCommand constructors refer to static ActionType instances. + // Registering commands in a mongo static initializer ensures that + // the ActionType construction will be completed first. + new PlanCacheListQueryShapes(); + new PlanCacheClear(); + new PlanCacheListPlans(); - return Status::OK(); - } + return Status::OK(); +} -} // namespace +} // namespace namespace mongo { - using std::string; - using std::stringstream; - using std::vector; - using std::unique_ptr; +using std::string; +using std::stringstream; +using std::vector; +using std::unique_ptr; - PlanCacheCommand::PlanCacheCommand(const string& name, const string& helpText, - ActionType actionType) - : Command(name), - helpText(helpText), - actionType(actionType) { } +PlanCacheCommand::PlanCacheCommand(const string& name, + const string& helpText, + ActionType actionType) + : Command(name), helpText(helpText), actionType(actionType) {} - bool PlanCacheCommand::run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int options, - string& errmsg, - BSONObjBuilder& result) { - string ns = parseNs(dbname, cmdObj); +bool PlanCacheCommand::run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int options, + string& errmsg, + BSONObjBuilder& result) { + string ns = parseNs(dbname, cmdObj); - Status status = runPlanCacheCommand(txn, ns, cmdObj, &result); + Status status = runPlanCacheCommand(txn, ns, cmdObj, &result); - if (!status.isOK()) { - addStatus(status, result); - return false; - } - - return true; + if (!status.isOK()) { + addStatus(status, result); + return false; } - bool PlanCacheCommand::isWriteCommandForConfigServer() const { return false; } + return true; +} - bool PlanCacheCommand::slaveOk() const { - return false; - } +bool PlanCacheCommand::isWriteCommandForConfigServer() const { + return false; +} - bool PlanCacheCommand::slaveOverrideOk() const { - return true; - } +bool PlanCacheCommand::slaveOk() const { + return false; +} - void PlanCacheCommand::help(stringstream& ss) const { - ss << helpText; - } +bool PlanCacheCommand::slaveOverrideOk() const { + return true; +} - Status PlanCacheCommand::checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - AuthorizationSession* authzSession = AuthorizationSession::get(client); - ResourcePattern pattern = parseResourcePattern(dbname, cmdObj); +void PlanCacheCommand::help(stringstream& ss) const { + ss << helpText; +} - if (authzSession->isAuthorizedForActionsOnResource(pattern, actionType)) { - return Status::OK(); - } +Status PlanCacheCommand::checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + AuthorizationSession* authzSession = AuthorizationSession::get(client); + ResourcePattern pattern = parseResourcePattern(dbname, cmdObj); - return Status(ErrorCodes::Unauthorized, "unauthorized"); + if (authzSession->isAuthorizedForActionsOnResource(pattern, actionType)) { + return Status::OK(); } - // static - Status PlanCacheCommand::canonicalize(OperationContext* txn, - const string& ns, - const BSONObj& cmdObj, - CanonicalQuery** canonicalQueryOut) { - // query - required - BSONElement queryElt = cmdObj.getField("query"); - if (queryElt.eoo()) { - return Status(ErrorCodes::BadValue, "required field query missing"); - } - if (!queryElt.isABSONObj()) { - return Status(ErrorCodes::BadValue, "required field query must be an object"); - } - if (queryElt.eoo()) { - return Status(ErrorCodes::BadValue, "required field query missing"); - } - BSONObj queryObj = queryElt.Obj(); - - // sort - optional - BSONElement sortElt = cmdObj.getField("sort"); - BSONObj sortObj; - if (!sortElt.eoo()) { - if (!sortElt.isABSONObj()) { - return Status(ErrorCodes::BadValue, "optional field sort must be an object"); - } - sortObj = sortElt.Obj(); - } - - // projection - optional - BSONElement projElt = cmdObj.getField("projection"); - BSONObj projObj; - if (!projElt.eoo()) { - if (!projElt.isABSONObj()) { - return Status(ErrorCodes::BadValue, "optional field projection must be an object"); - } - projObj = projElt.Obj(); + return Status(ErrorCodes::Unauthorized, "unauthorized"); +} + +// static +Status PlanCacheCommand::canonicalize(OperationContext* txn, + const string& ns, + const BSONObj& cmdObj, + CanonicalQuery** canonicalQueryOut) { + // query - required + BSONElement queryElt = cmdObj.getField("query"); + if (queryElt.eoo()) { + return Status(ErrorCodes::BadValue, "required field query missing"); + } + if (!queryElt.isABSONObj()) { + return Status(ErrorCodes::BadValue, "required field query must be an object"); + } + if (queryElt.eoo()) { + return Status(ErrorCodes::BadValue, "required field query missing"); + } + BSONObj queryObj = queryElt.Obj(); + + // sort - optional + BSONElement sortElt = cmdObj.getField("sort"); + BSONObj sortObj; + if (!sortElt.eoo()) { + if (!sortElt.isABSONObj()) { + return Status(ErrorCodes::BadValue, "optional field sort must be an object"); } + sortObj = sortElt.Obj(); + } - // Create canonical query - CanonicalQuery* cqRaw; - - const NamespaceString nss(ns); - const WhereCallbackReal whereCallback(txn, nss.db()); - - Status result = CanonicalQuery::canonicalize( - ns, queryObj, sortObj, projObj, &cqRaw, whereCallback); - if (!result.isOK()) { - return result; + // projection - optional + BSONElement projElt = cmdObj.getField("projection"); + BSONObj projObj; + if (!projElt.eoo()) { + if (!projElt.isABSONObj()) { + return Status(ErrorCodes::BadValue, "optional field projection must be an object"); } - - *canonicalQueryOut = cqRaw; - return Status::OK(); + projObj = projElt.Obj(); } - PlanCacheListQueryShapes::PlanCacheListQueryShapes() : PlanCacheCommand("planCacheListQueryShapes", - "Displays all query shapes in a collection.", - ActionType::planCacheRead) { } + // Create canonical query + CanonicalQuery* cqRaw; - Status PlanCacheListQueryShapes::runPlanCacheCommand(OperationContext* txn, - const string& ns, - BSONObj& cmdObj, - BSONObjBuilder* bob) { - // This is a read lock. The query cache is owned by the collection. - AutoGetCollectionForRead ctx(txn, ns); + const NamespaceString nss(ns); + const WhereCallbackReal whereCallback(txn, nss.db()); - PlanCache* planCache; - Status status = getPlanCache(txn, ctx.getCollection(), ns, &planCache); - if (!status.isOK()) { - // No collection - return results with empty shapes array. - BSONArrayBuilder arrayBuilder(bob->subarrayStart("shapes")); - arrayBuilder.doneFast(); - return Status::OK(); - } - return list(*planCache, bob); + Status result = + CanonicalQuery::canonicalize(ns, queryObj, sortObj, projObj, &cqRaw, whereCallback); + if (!result.isOK()) { + return result; } - // static - Status PlanCacheListQueryShapes::list(const PlanCache& planCache, BSONObjBuilder* bob) { - invariant(bob); - - // Fetch all cached solutions from plan cache. - vector<PlanCacheEntry*> solutions = planCache.getAllEntries(); - + *canonicalQueryOut = cqRaw; + return Status::OK(); +} + +PlanCacheListQueryShapes::PlanCacheListQueryShapes() + : PlanCacheCommand("planCacheListQueryShapes", + "Displays all query shapes in a collection.", + ActionType::planCacheRead) {} + +Status PlanCacheListQueryShapes::runPlanCacheCommand(OperationContext* txn, + const string& ns, + BSONObj& cmdObj, + BSONObjBuilder* bob) { + // This is a read lock. The query cache is owned by the collection. + AutoGetCollectionForRead ctx(txn, ns); + + PlanCache* planCache; + Status status = getPlanCache(txn, ctx.getCollection(), ns, &planCache); + if (!status.isOK()) { + // No collection - return results with empty shapes array. BSONArrayBuilder arrayBuilder(bob->subarrayStart("shapes")); - for (vector<PlanCacheEntry*>::const_iterator i = solutions.begin(); i != solutions.end(); i++) { - PlanCacheEntry* entry = *i; - invariant(entry); - - BSONObjBuilder shapeBuilder(arrayBuilder.subobjStart()); - shapeBuilder.append("query", entry->query); - shapeBuilder.append("sort", entry->sort); - shapeBuilder.append("projection", entry->projection); - shapeBuilder.doneFast(); - - // Release resources for cached solution after extracting query shape. - delete entry; - } arrayBuilder.doneFast(); - return Status::OK(); } + return list(*planCache, bob); +} - PlanCacheClear::PlanCacheClear() : PlanCacheCommand("planCacheClear", - "Drops one or all cached queries in a collection.", - ActionType::planCacheWrite) { } - - Status PlanCacheClear::runPlanCacheCommand(OperationContext* txn, - const std::string& ns, - BSONObj& cmdObj, - BSONObjBuilder* bob) { - // This is a read lock. The query cache is owned by the collection. - AutoGetCollectionForRead ctx(txn, ns); +// static +Status PlanCacheListQueryShapes::list(const PlanCache& planCache, BSONObjBuilder* bob) { + invariant(bob); - PlanCache* planCache; - Status status = getPlanCache(txn, ctx.getCollection(), ns, &planCache); - if (!status.isOK()) { - // No collection - nothing to do. Return OK status. - return Status::OK(); - } - return clear(txn, planCache, ns, cmdObj); - } + // Fetch all cached solutions from plan cache. + vector<PlanCacheEntry*> solutions = planCache.getAllEntries(); - // static - Status PlanCacheClear::clear(OperationContext* txn, - PlanCache* planCache, - const string& ns, - const BSONObj& cmdObj) { - invariant(planCache); - - // According to the specification, the planCacheClear command runs in two modes: - // - clear all query shapes; or - // - clear plans for single query shape when a query shape is described in the - // command arguments. - if (cmdObj.hasField("query")) { - CanonicalQuery* cqRaw; - Status status = PlanCacheCommand::canonicalize(txn, ns, cmdObj, &cqRaw); - if (!status.isOK()) { - return status; - } + BSONArrayBuilder arrayBuilder(bob->subarrayStart("shapes")); + for (vector<PlanCacheEntry*>::const_iterator i = solutions.begin(); i != solutions.end(); i++) { + PlanCacheEntry* entry = *i; + invariant(entry); - unique_ptr<CanonicalQuery> cq(cqRaw); + BSONObjBuilder shapeBuilder(arrayBuilder.subobjStart()); + shapeBuilder.append("query", entry->query); + shapeBuilder.append("sort", entry->sort); + shapeBuilder.append("projection", entry->projection); + shapeBuilder.doneFast(); - if (!planCache->contains(*cq)) { - // Log if asked to clear non-existent query shape. - LOG(1) << ns << ": query shape doesn't exist in PlanCache - " - << cq->getQueryObj().toString() - << "(sort: " << cq->getParsed().getSort() - << "; projection: " << cq->getParsed().getProj() << ")"; - return Status::OK(); - } + // Release resources for cached solution after extracting query shape. + delete entry; + } + arrayBuilder.doneFast(); + + return Status::OK(); +} + +PlanCacheClear::PlanCacheClear() + : PlanCacheCommand("planCacheClear", + "Drops one or all cached queries in a collection.", + ActionType::planCacheWrite) {} + +Status PlanCacheClear::runPlanCacheCommand(OperationContext* txn, + const std::string& ns, + BSONObj& cmdObj, + BSONObjBuilder* bob) { + // This is a read lock. The query cache is owned by the collection. + AutoGetCollectionForRead ctx(txn, ns); + + PlanCache* planCache; + Status status = getPlanCache(txn, ctx.getCollection(), ns, &planCache); + if (!status.isOK()) { + // No collection - nothing to do. Return OK status. + return Status::OK(); + } + return clear(txn, planCache, ns, cmdObj); +} + +// static +Status PlanCacheClear::clear(OperationContext* txn, + PlanCache* planCache, + const string& ns, + const BSONObj& cmdObj) { + invariant(planCache); + + // According to the specification, the planCacheClear command runs in two modes: + // - clear all query shapes; or + // - clear plans for single query shape when a query shape is described in the + // command arguments. + if (cmdObj.hasField("query")) { + CanonicalQuery* cqRaw; + Status status = PlanCacheCommand::canonicalize(txn, ns, cmdObj, &cqRaw); + if (!status.isOK()) { + return status; + } - Status result = planCache->remove(*cq); - if (!result.isOK()) { - return result; - } + unique_ptr<CanonicalQuery> cq(cqRaw); - LOG(1) << ns << ": removed plan cache entry - " << cq->getQueryObj().toString() - << "(sort: " << cq->getParsed().getSort() + if (!planCache->contains(*cq)) { + // Log if asked to clear non-existent query shape. + LOG(1) << ns << ": query shape doesn't exist in PlanCache - " + << cq->getQueryObj().toString() << "(sort: " << cq->getParsed().getSort() << "; projection: " << cq->getParsed().getProj() << ")"; - return Status::OK(); } - // If query is not provided, make sure sort and projection are not in arguments. - // We do not want to clear the entire cache inadvertently when the user - // forgets to provide a value for "query". - if (cmdObj.hasField("sort") || cmdObj.hasField("projection")) { - return Status(ErrorCodes::BadValue, "sort or projection provided without query"); + Status result = planCache->remove(*cq); + if (!result.isOK()) { + return result; } - planCache->clear(); - - LOG(1) << ns << ": cleared plan cache"; + LOG(1) << ns << ": removed plan cache entry - " << cq->getQueryObj().toString() + << "(sort: " << cq->getParsed().getSort() + << "; projection: " << cq->getParsed().getProj() << ")"; return Status::OK(); } - PlanCacheListPlans::PlanCacheListPlans() : PlanCacheCommand("planCacheListPlans", - "Displays the cached plans for a query shape.", - ActionType::planCacheRead) { } + // If query is not provided, make sure sort and projection are not in arguments. + // We do not want to clear the entire cache inadvertently when the user + // forgets to provide a value for "query". + if (cmdObj.hasField("sort") || cmdObj.hasField("projection")) { + return Status(ErrorCodes::BadValue, "sort or projection provided without query"); + } - Status PlanCacheListPlans::runPlanCacheCommand(OperationContext* txn, - const std::string& ns, - BSONObj& cmdObj, - BSONObjBuilder* bob) { - AutoGetCollectionForRead ctx(txn, ns); + planCache->clear(); - PlanCache* planCache; - Status status = getPlanCache(txn, ctx.getCollection(), ns, &planCache); - if (!status.isOK()) { - // No collection - return empty plans array. - BSONArrayBuilder plansBuilder(bob->subarrayStart("plans")); - plansBuilder.doneFast(); - return Status::OK(); - } - return list(txn, *planCache, ns, cmdObj, bob); - } + LOG(1) << ns << ": cleared plan cache"; - // static - Status PlanCacheListPlans::list(OperationContext* txn, - const PlanCache& planCache, - const std::string& ns, - const BSONObj& cmdObj, - BSONObjBuilder* bob) { - CanonicalQuery* cqRaw; - Status status = canonicalize(txn, ns, cmdObj, &cqRaw); - if (!status.isOK()) { - return status; - } + return Status::OK(); +} - unique_ptr<CanonicalQuery> cq(cqRaw); +PlanCacheListPlans::PlanCacheListPlans() + : PlanCacheCommand("planCacheListPlans", + "Displays the cached plans for a query shape.", + ActionType::planCacheRead) {} - if (!planCache.contains(*cq)) { - // Return empty plans in results if query shape does not - // exist in plan cache. - BSONArrayBuilder plansBuilder(bob->subarrayStart("plans")); - plansBuilder.doneFast(); - return Status::OK(); - } +Status PlanCacheListPlans::runPlanCacheCommand(OperationContext* txn, + const std::string& ns, + BSONObj& cmdObj, + BSONObjBuilder* bob) { + AutoGetCollectionForRead ctx(txn, ns); - PlanCacheEntry* entryRaw; - Status result = planCache.getEntry(*cq, &entryRaw); - if (!result.isOK()) { - return result; - } - unique_ptr<PlanCacheEntry> entry(entryRaw); + PlanCache* planCache; + Status status = getPlanCache(txn, ctx.getCollection(), ns, &planCache); + if (!status.isOK()) { + // No collection - return empty plans array. + BSONArrayBuilder plansBuilder(bob->subarrayStart("plans")); + plansBuilder.doneFast(); + return Status::OK(); + } + return list(txn, *planCache, ns, cmdObj, bob); +} + +// static +Status PlanCacheListPlans::list(OperationContext* txn, + const PlanCache& planCache, + const std::string& ns, + const BSONObj& cmdObj, + BSONObjBuilder* bob) { + CanonicalQuery* cqRaw; + Status status = canonicalize(txn, ns, cmdObj, &cqRaw); + if (!status.isOK()) { + return status; + } + + unique_ptr<CanonicalQuery> cq(cqRaw); + if (!planCache.contains(*cq)) { + // Return empty plans in results if query shape does not + // exist in plan cache. BSONArrayBuilder plansBuilder(bob->subarrayStart("plans")); - size_t numPlans = entry->plannerData.size(); - invariant(numPlans == entry->decision->stats.size()); - invariant(numPlans == entry->decision->scores.size()); - for (size_t i = 0; i < numPlans; ++i) { - BSONObjBuilder planBob(plansBuilder.subobjStart()); - - // Create plan details field. - // Currently, simple string representationg of - // SolutionCacheData. Need to revisit format when we - // need to parse user-provided plan details for planCacheAddPlan. - SolutionCacheData* scd = entry->plannerData[i]; - BSONObjBuilder detailsBob(planBob.subobjStart("details")); - detailsBob.append("solution", scd->toString()); - detailsBob.doneFast(); - - // reason is comprised of score and initial stats provided by - // multi plan runner. - BSONObjBuilder reasonBob(planBob.subobjStart("reason")); - reasonBob.append("score", entry->decision->scores[i]); - BSONObjBuilder statsBob(reasonBob.subobjStart("stats")); - PlanStageStats* stats = entry->decision->stats.vector()[i]; - if (stats) { - Explain::statsToBSON(*stats, &statsBob); - } - statsBob.doneFast(); - reasonBob.doneFast(); - - // BSON object for 'feedback' field shows scores from historical executions of the plan. - BSONObjBuilder feedbackBob(planBob.subobjStart("feedback")); - if (i == 0U) { - feedbackBob.append("nfeedback", int(entry->feedback.size())); - BSONArrayBuilder scoresBob(feedbackBob.subarrayStart("scores")); - for (size_t i = 0; i < entry->feedback.size(); ++i) { - BSONObjBuilder scoreBob(scoresBob.subobjStart()); - scoreBob.append("score", entry->feedback[i]->score); - } - scoresBob.doneFast(); - } - feedbackBob.doneFast(); + plansBuilder.doneFast(); + return Status::OK(); + } - planBob.append("filterSet", scd->indexFilterApplied); + PlanCacheEntry* entryRaw; + Status result = planCache.getEntry(*cq, &entryRaw); + if (!result.isOK()) { + return result; + } + unique_ptr<PlanCacheEntry> entry(entryRaw); + + BSONArrayBuilder plansBuilder(bob->subarrayStart("plans")); + size_t numPlans = entry->plannerData.size(); + invariant(numPlans == entry->decision->stats.size()); + invariant(numPlans == entry->decision->scores.size()); + for (size_t i = 0; i < numPlans; ++i) { + BSONObjBuilder planBob(plansBuilder.subobjStart()); + + // Create plan details field. + // Currently, simple string representationg of + // SolutionCacheData. Need to revisit format when we + // need to parse user-provided plan details for planCacheAddPlan. + SolutionCacheData* scd = entry->plannerData[i]; + BSONObjBuilder detailsBob(planBob.subobjStart("details")); + detailsBob.append("solution", scd->toString()); + detailsBob.doneFast(); + + // reason is comprised of score and initial stats provided by + // multi plan runner. + BSONObjBuilder reasonBob(planBob.subobjStart("reason")); + reasonBob.append("score", entry->decision->scores[i]); + BSONObjBuilder statsBob(reasonBob.subobjStart("stats")); + PlanStageStats* stats = entry->decision->stats.vector()[i]; + if (stats) { + Explain::statsToBSON(*stats, &statsBob); } - plansBuilder.doneFast(); + statsBob.doneFast(); + reasonBob.doneFast(); + + // BSON object for 'feedback' field shows scores from historical executions of the plan. + BSONObjBuilder feedbackBob(planBob.subobjStart("feedback")); + if (i == 0U) { + feedbackBob.append("nfeedback", int(entry->feedback.size())); + BSONArrayBuilder scoresBob(feedbackBob.subarrayStart("scores")); + for (size_t i = 0; i < entry->feedback.size(); ++i) { + BSONObjBuilder scoreBob(scoresBob.subobjStart()); + scoreBob.append("score", entry->feedback[i]->score); + } + scoresBob.doneFast(); + } + feedbackBob.doneFast(); - return Status::OK(); + planBob.append("filterSet", scd->indexFilterApplied); } + plansBuilder.doneFast(); + + return Status::OK(); +} -} // namespace mongo +} // namespace mongo |