-
Type: Bug
-
Resolution: Won't Do
-
Priority: Major - P3
-
None
-
Affects Version/s: None
-
Component/s: Querying
-
Query
-
ALL
The MultiPlan stage runs work() on all plans until at least one child returns IS_EOF. However, the plan ranker gives an "EOF bonus" not just to plans that return IS_EOF, but to all plans for which isEOF() returns true (a larger subset). This gives "unfinished plans" (i.e. those that haven't returned IS_EOF yet) a chance to tie with "finished plans" (i.e. those that have returned IS_EOF already), in cases when the "finished plans" are strictly preferable.
To illustrate, see the example query stats tree below. work() is called once on the the IndexScan stage, and isEOF() on that stage evaluates to true, but it returned NEED_TIME, so it should not be eligible for the EOF bonus. Indeed this plan is not given the EOF bonus, since isEOF() on the KEEP_MUTATIONS stage is false. However, if the KEEP_MUTATIONS stage was not present and the FETCH were root instead, this plan would have indeed been given the EOF bonus, since isEOF() on the FETCH stage is true.
> db.version() 2.6.4 > db.foo.drop() true > db.foo.ensureIndex({a:1}) { "createdCollectionAutomatically" : true, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.foo.ensureIndex({b:1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 } > db.foo.find({a:0,b:{$gte:0}}).itcount() 0 > db.foo.getPlanCache().getPlansByQuery({a:0,b:{$gte:0}})[1] { "details" : { "solution" : "(index-tagged expression tree: tree=Node\n---Leaf { a: 1.0 }, pos: 0\n---Leaf \n)" }, "reason" : { "score" : 1.0003, "stats" : { "type" : "KEEP_MUTATIONS", "works" : 1, // 1 work cycle "yields" : 0, "unyields" : 0, "invalidates" : 0, "advanced" : 0, "needTime" : 1, // KEEP_MUTATIONS stage returned NEED_TIME "needFetch" : 0, "isEOF" : 0, // KEEP_MUTATIONS stage isEOF() is *false* (isEOF() for this stage based on whether child returned IS_EOF) "children" : [ { "type" : "FETCH", "works" : 1, // 1 work cycle "yields" : 0, "unyields" : 0, "invalidates" : 0, "advanced" : 0, "needTime" : 1, // FETCH stage returned NEED_TIME "needFetch" : 0, "isEOF" : 1, // FETCH stage isEOF() is *true* (isEOF() for this stage based on whether isEOF() on child is true) "alreadyHasObj" : 0, "forcedFetches" : 0, "matchTested" : 0, "children" : [ { "type" : "IXSCAN", "works" : 1, // 1 work cycle "yields" : 0, "unyields" : 0, "invalidates" : 0, "advanced" : 0, "needTime" : 1, // IXSCAN stage returned NEED_TIME "needFetch" : 0, "isEOF" : 1, // IXSCAN stage isEOF() is *true* "keyPattern" : "{ a: 1.0 }", "isMultiKey" : 0, "boundsVerbose" : "field #0['a']: [0.0, 0.0]", "yieldMovedCursor" : 0, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0, "matchTested" : 0, "keysExamined" : 0, "children" : [ ] } ] } ] } }, "feedback" : { }, "filterSet" : false }
- related to
-
SERVER-17839 Remove PlanStage::isEOF
- Backlog