[SERVER-19415] PlanCache.listQueryShapes() reports the wrong query predicates for $or queries that use the SubplanStage Created: 15/Jul/15  Updated: 29/Jan/18  Resolved: 07/Dec/17

Status: Closed
Project: Core Server
Component/s: Querying
Affects Version/s: 3.0.0
Fix Version/s: 3.7.1

Type: Bug Priority: Minor - P4
Reporter: David Storch Assignee: Ian Boros
Resolution: Done Votes: 1
Labels: neweng
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
depends on SERVER-22833 MatchExpression::toBSON does not nece... Closed
Backwards Compatibility: Fully Compatible
Operating System: ALL
Steps To Reproduce:

db.c.drop();
db.c.ensureIndex({a: 1});
db.c.ensureIndex({b: 1});
db.c.ensureIndex({c: 1});
db.c.ensureIndex({d: 1});
 
db.c.insert({a:1, b:0, c:0, d:0});
db.c.insert({a:0, b:0, c:1, d:0});
db.c.insert({a:1, b:1, c:1, d:1});
 
// Run the query that will create the plan cache entries.
db.c.find({$or: [{a: 1, b: 1}, {c: 1, d: 1}]});
 
db.c.getPlanCache().listQueryShapes();
[
	{
               // The value of this field is wrong! It should be {a: 1, b: 1}.
		"query" : {
			"$or" : [
				{
					"a" : 1,
					"b" : 1
				},
				{
					"c" : 1,
					"d" : 1
				}
			]
		},
		"sort" : {
 
		},
		"projection" : {
 
		}
	},
	{
               // The value of this field is wrong! It should be {c: 1, d: 1}.
		"query" : {
			"$or" : [
				{
					"a" : 1,
					"b" : 1
				},
				{
					"c" : 1,
					"d" : 1
				}
			]
		},
		"sort" : {
 
		},
		"projection" : {
 
		}
	}
]

Sprint: Query 2017-12-18
Participants:
Case:

 Description   

Rooted $or queries (i.e. queries that consist of just an or at the top-level, like {$or: [{...}, {...}, ...]}) and contained $or queries (i.e. queries that have an $or which is AND-related with another predicate or predicates, like {p: 1, $or: [{...}, {...}, ...]}) are planned using the special SubplanStage path.

The SubplanStage reads from and writes to the plan cache on a per-$or clause basis. That is, if you run the query {$or: [{a: 1, b: 1}, {c: 1, d: 1}]} against a server with an empty plan cache, and both clauses are indexed, then the SubplanStage will create two plan cache entries: one for the query {a: 1, b: 1} and another for the query {c: 1, d: 1}. On subsequent executions of the rooted $or query, the plan for each of the two clauses will individually get taken from the plan cache.

When the SubplanStage creates the plan cache entries for each branch, it erroneously associates the entire rooted $or predicate with each plan cache entry. Instead, it should pass the query predicate {a: 1, b: 1} to the plan cache for the first clause of the $or, and should pass {c: 1, d: 1} for the second clause. This is benign in that the bug is only in the plan cache's diagnostics/reporting, however it could be very confusing for users. See "Steps to Reproduce" for details.

The code responsible for this bug is already marked with a TODO:

https://github.com/mongodb/mongo/blob/3f301ac62ea983d864f9a5ad017876171e2f1104/src/mongo/db/query/canonical_query.cpp#L238-L239

There is a also descriptive test for this behavior which should be modified alongside the fix:

https://github.com/mongodb/mongo/blob/3f301ac62ea983d864f9a5ad017876171e2f1104/src/mongo/db/query/canonical_query_test.cpp#L522-L524



 Comments   
Comment by Githook User [ 07/Dec/17 ]

Author:

{'name': 'Ian Boros', 'email': 'ian.boros@10gen.com'}

Message: SERVER-19415 Fix planCacheListQueryShapes command to report correct query predicate
Branch: master
https://github.com/mongodb/mongo/commit/332ae576ef6e24080c0f8e9755f1574bbb90fdab

Comment by Ian Boros [ 05/Dec/17 ]

Just a note: I am only able to reproduce this when the collection has some data. It seems like the reason for this is that running the multi-planner on an empty collection will result in a tie, meaning no plan will be added to the cache. Even if they didn't tie, we don't cache query plans if they produce zero results.

I've updated the "reproduce" section of the ticket.

Comment by J Rassi [ 23/Jul/15 ]

To address this, the PlanCache could use canonicalQuery.root()->toBSON() as the stored query predicate instead of canonicalQuery.getParsed()->getFilter().

Generated at Thu Feb 08 03:50:53 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.