diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/commands/plan_cache_commands.cpp | 65 | ||||
-rw-r--r-- | src/mongo/db/commands/plan_cache_commands.h | 35 | ||||
-rw-r--r-- | src/mongo/db/commands/plan_cache_commands_test.cpp | 32 |
3 files changed, 62 insertions, 70 deletions
diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp index da280a7c172..2728fc322e8 100644 --- a/src/mongo/db/commands/plan_cache_commands.cpp +++ b/src/mongo/db/commands/plan_cache_commands.cpp @@ -94,7 +94,6 @@ namespace { // the ActionType construction will be completed first. new PlanCacheListQueryShapes(); new PlanCacheClear(); - new PlanCacheDrop(); new PlanCacheListPlans(); return Status::OK(); @@ -244,7 +243,7 @@ namespace mongo { } PlanCacheClear::PlanCacheClear() : PlanCacheCommand("planCacheClear", - "Drops all cached queries in a collection.", + "Drops one or all cached queries in a collection.", ActionType::planCacheWrite) { } Status PlanCacheClear::runPlanCacheCommand(const string& ns, BSONObj& cmdObj, @@ -257,53 +256,47 @@ namespace mongo { if (!status.isOK()) { return status; } - return clear(ns, planCache); + return clear(planCache, ns, cmdObj); } // static - Status PlanCacheClear::clear(const std::string& ns, PlanCache* planCache) { + Status PlanCacheClear::clear(PlanCache* planCache, const string& ns, const BSONObj& cmdObj) { invariant(planCache); - planCache->clear(); - - LOG(1) << ns << ": cleared plan cache"; + // 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(ns, cmdObj, &cqRaw); + if (!status.isOK()) { + return status; + } - return Status::OK(); - } + scoped_ptr<CanonicalQuery> cq(cqRaw); + Status result = planCache->remove(*cq); + if (!result.isOK()) { + return result; + } - PlanCacheDrop::PlanCacheDrop() : PlanCacheCommand("planCacheDrop", - "Drops query shape from plan cache.", - ActionType::planCacheWrite) { } + LOG(1) << ns << ": removed plan cache entry - " << cq->getQueryObj().toString() + << "(sort: " << cq->getParsed().getSort() + << "; projection: " << cq->getParsed().getProj() << ")"; - Status PlanCacheDrop::runPlanCacheCommand(const string& ns, BSONObj& cmdObj, - BSONObjBuilder* bob) { - Client::ReadContext readCtx(ns); - Client::Context& ctx = readCtx.ctx(); - PlanCache* planCache; - Status status = getPlanCache(ctx.db(), ns, &planCache); - if (!status.isOK()) { - return status; + return Status::OK(); } - return drop(planCache, ns, cmdObj); - } - // static - Status PlanCacheDrop::drop(PlanCache* planCache, const string& ns, const BSONObj& cmdObj) { - CanonicalQuery* cqRaw; - Status status = canonicalize(ns, cmdObj, &cqRaw); - if (!status.isOK()) { - return status; + // 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"); } - scoped_ptr<CanonicalQuery> cq(cqRaw); - Status result = planCache->remove(*cq); - if (!result.isOK()) { - return result; - } + planCache->clear(); - LOG(1) << ns << ": removed plan cache entry - " << cq->getQueryObj().toString() - << "(sort: " << cq->getParsed().getSort() - << "; projection: " << cq->getParsed().getProj() << ")"; + LOG(1) << ns << ": cleared plan cache"; return Status::OK(); } diff --git a/src/mongo/db/commands/plan_cache_commands.h b/src/mongo/db/commands/plan_cache_commands.h index fd3c0832089..6cf666bce36 100644 --- a/src/mongo/db/commands/plan_cache_commands.h +++ b/src/mongo/db/commands/plan_cache_commands.h @@ -120,7 +120,12 @@ namespace mongo { /** * planCacheClear * - * { planCacheClear: <collection> } + * { + * planCacheClear: <collection>, + * query: <query>, + * sort: <sort>, + * projection: <projection> + * } * */ class PlanCacheClear : public PlanCacheCommand { @@ -130,32 +135,20 @@ namespace mongo { /** * Clears collection's plan cache. + * If query shape is provided, clears plans for that single query shape only. */ - static Status clear(const std::string& ns, PlanCache* planCache); - }; - - /** - * planCacheDrop - * - * { planCacheDrop: <collection>, key: <key> } } - * - */ - class PlanCacheDrop : public PlanCacheCommand { - public: - PlanCacheDrop(); - virtual Status runPlanCacheCommand(const std::string& ns, BSONObj& cmdObj, - BSONObjBuilder* bob); - - /** - * Drops using a cache key. - */ - static Status drop(PlanCache* planCache,const std::string& ns, const BSONObj& cmdObj); + static Status clear(PlanCache* planCache, const std::string& ns, const BSONObj& cmdObj); }; /** * planCacheListPlans * - * { planCacheListPlans: <collection>, key: <key> } } + * { + * planCacheListPlans: <collection>, + * query: <query>, + * sort: <sort>, + * projection: <projection> + * } * */ class PlanCacheListPlans : public PlanCacheCommand { diff --git a/src/mongo/db/commands/plan_cache_commands_test.cpp b/src/mongo/db/commands/plan_cache_commands_test.cpp index 66d8a3c9c29..0fdfad7da19 100644 --- a/src/mongo/db/commands/plan_cache_commands_test.cpp +++ b/src/mongo/db/commands/plan_cache_commands_test.cpp @@ -144,7 +144,7 @@ namespace { * Tests for planCacheClear */ - TEST(PlanCacheCommandsTest, planCacheClearOneKey) { + TEST(PlanCacheCommandsTest, planCacheClearAllShapes) { // Create a canonical query CanonicalQuery* cqRaw; ASSERT_OK(CanonicalQuery::canonicalize(ns, fromjson("{a: 1}"), &cqRaw)); @@ -160,7 +160,7 @@ namespace { ASSERT_EQUALS(getShapes(planCache).size(), 1U); // Clear cache and confirm number of keys afterwards. - ASSERT_OK(PlanCacheClear::clear(ns, &planCache)); + ASSERT_OK(PlanCacheClear::clear(&planCache, ns, BSONObj())); ASSERT_EQUALS(getShapes(planCache).size(), 0U); } @@ -207,24 +207,30 @@ namespace { } /** - * Tests for planCacheDrop + * Tests for planCacheClear (single query shape) */ - TEST(PlanCacheCommandsTest, planCacheDropInvalidParameter) { + TEST(PlanCacheCommandsTest, planCacheClearInvalidParameter) { PlanCache planCache; - // Missing query field is not ok. - ASSERT_NOT_OK(PlanCacheDrop::drop(&planCache, ns, BSONObj())); - // Query field type must be PlanCacheKey. - ASSERT_NOT_OK(PlanCacheDrop::drop(&planCache, ns, fromjson("{query: 12345}"))); - ASSERT_NOT_OK(PlanCacheDrop::drop(&planCache, ns, fromjson("{query: /keyisnotregex/}"))); + // Query field type must be BSON object. + ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{query: 12345}"))); + ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{query: /keyisnotregex/}"))); + // Query must pass canonicalization. + ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, + fromjson("{query: {a: {$no_such_op: 1}}}"))); + // Sort present without query is an error. + ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{sort: {a: 1}}"))); + // Projection present without query is an error. + ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, + fromjson("{projection: {_id: 0, a: 1}}"))); } - TEST(PlanCacheCommandsTest, planCacheDropUnknownKey) { + TEST(PlanCacheCommandsTest, planCacheClearUnknownKey) { PlanCache planCache; - ASSERT_NOT_OK(PlanCacheDrop::drop(&planCache, ns, fromjson("{query: {a: 1}}"))); + ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{query: {a: 1}}"))); } - TEST(PlanCacheCommandsTest, planCacheDropOneKey) { + TEST(PlanCacheCommandsTest, planCacheClearOneKey) { // Create 2 canonical queries. CanonicalQuery* cqRaw; ASSERT_OK(CanonicalQuery::canonicalize(ns, fromjson("{a: 1}"), &cqRaw)); @@ -253,7 +259,7 @@ namespace { // Drop {b: 1} from cache. Make sure {a: 1} is still in cache afterwards. BSONObjBuilder bob; - ASSERT_OK(PlanCacheDrop::drop(&planCache, ns, BSON("query" << cqB->getQueryObj()))); + ASSERT_OK(PlanCacheClear::clear(&planCache, ns, BSON("query" << cqB->getQueryObj()))); vector<BSONObj> shapesAfter = getShapes(planCache); ASSERT_EQUALS(shapesAfter.size(), 1U); ASSERT_EQUALS(shapesAfter[0], shapeA); |