This also changes the error behavior of explain so that the explain command succeeds as long as it has provided valid output, even if the underlying operation failed.
155 lines
4.1 KiB
JavaScript
155 lines
4.1 KiB
JavaScript
// Test that even when the execution of a query fails, explain reports query
|
|
// planner information.
|
|
|
|
var t = db.explain_execution_error;
|
|
t.drop();
|
|
|
|
var result;
|
|
|
|
/**
|
|
* Asserts that explain reports an error in its execution stats section.
|
|
*/
|
|
function assertExecError(explain) {
|
|
var errorObj;
|
|
|
|
var execStats = explain.executionStats;
|
|
if (execStats.executionStages.stage == "SINGLE_SHARD") {
|
|
errorObj = execStats.executionStages.shards[0];
|
|
}
|
|
else {
|
|
errorObj = execStats;
|
|
}
|
|
|
|
assert.eq(false, errorObj.executionSuccess);
|
|
assert("errorMessage" in errorObj);
|
|
assert("errorCode" in errorObj);
|
|
}
|
|
|
|
/**
|
|
* Asserts that explain reports success in its execution stats section.
|
|
*/
|
|
function assertExecSuccess(explain) {
|
|
var errorObj;
|
|
|
|
var execStats = explain.executionStats;
|
|
if (execStats.executionStages.stage == "SINGLE_SHARD") {
|
|
errorObj = execStats.executionStages.shards[0];
|
|
}
|
|
else {
|
|
errorObj = execStats;
|
|
}
|
|
|
|
assert.eq(true, errorObj.executionSuccess);
|
|
assert(!("errorMessage" in errorObj));
|
|
assert(!("errorCode" in errorObj));
|
|
}
|
|
|
|
// Make a string that exceeds 1 MB.
|
|
var bigStr = "x";
|
|
while (bigStr.length < (1024 * 1024)) {
|
|
bigStr += bigStr;
|
|
}
|
|
|
|
// Make a collection that is about 40 MB.
|
|
for (var i = 0; i < 40; i++) {
|
|
assert.writeOK(t.insert({a: bigStr, b: 1, c: i}));
|
|
}
|
|
|
|
// A query which sorts the whole collection by "b" should throw an error due to hitting the
|
|
// memory limit for sort.
|
|
assert.throws(function() {
|
|
t.find({a: {$exists: true}}).sort({b: 1}).itcount();
|
|
});
|
|
|
|
// Explain of this query should succeed at query planner verbosity.
|
|
result = db.runCommand({
|
|
explain: {
|
|
find: t.getName(),
|
|
filter: {a: {$exists: true}},
|
|
sort: {b: 1}
|
|
},
|
|
verbosity: "queryPlanner"
|
|
});
|
|
assert.commandWorked(result);
|
|
assert("queryPlanner" in result);
|
|
|
|
// Explaining the same query at execution stats verbosity should succeed, but indicate that the
|
|
// underlying operation failed.
|
|
result = db.runCommand({
|
|
explain: {
|
|
find: t.getName(),
|
|
filter: {a: {$exists: true}},
|
|
sort: {b: 1}
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandWorked(result);
|
|
assert("queryPlanner" in result);
|
|
assert("executionStats" in result);
|
|
assertExecError(result);
|
|
|
|
// The underlying operation should also report a failure at allPlansExecution verbosity.
|
|
result = db.runCommand({
|
|
explain: {
|
|
find: t.getName(),
|
|
filter: {a: {$exists: true}},
|
|
sort: {b: 1}
|
|
},
|
|
verbosity: "allPlansExecution"
|
|
});
|
|
assert.commandWorked(result);
|
|
assert("queryPlanner" in result);
|
|
assert("executionStats" in result);
|
|
assert("allPlansExecution" in result.executionStats);
|
|
assertExecError(result);
|
|
|
|
// Now we introduce two indices. One provides the requested sort order, and
|
|
// the other does not.
|
|
t.ensureIndex({b: 1});
|
|
t.ensureIndex({c: 1});
|
|
|
|
// The query should no longer fail with a memory limit error because the planner can obtain
|
|
// the sort by scanning an index.
|
|
assert.eq(40, t.find({c: {$lt: 40}}).sort({b: 1}).itcount());
|
|
|
|
// The explain should succeed at all verbosity levels because the query itself succeeds.
|
|
// First test "queryPlanner" verbosity.
|
|
result = db.runCommand({
|
|
explain: {
|
|
find: t.getName(),
|
|
filter: {c: {$lt: 40}},
|
|
sort: {b: 1}
|
|
},
|
|
verbosity: "queryPlanner"
|
|
});
|
|
assert.commandWorked(result);
|
|
assert("queryPlanner" in result);
|
|
|
|
result = db.runCommand({
|
|
explain: {
|
|
find: t.getName(),
|
|
filter: {c: {$lt: 40}},
|
|
sort: {b: 1}
|
|
},
|
|
verbosity: "executionStats"
|
|
});
|
|
assert.commandWorked(result);
|
|
assert("queryPlanner" in result);
|
|
assert("executionStats" in result);
|
|
assertExecSuccess(result);
|
|
|
|
// We expect allPlansExecution verbosity to show execution stats for both candidate plans.
|
|
result = db.runCommand({
|
|
explain: {
|
|
find: t.getName(),
|
|
filter: {c: {$lt: 40}},
|
|
sort: {b: 1}
|
|
},
|
|
verbosity: "allPlansExecution"
|
|
});
|
|
assert.commandWorked(result);
|
|
assert("queryPlanner" in result);
|
|
assert("executionStats" in result);
|
|
assert("allPlansExecution" in result.executionStats);
|
|
assertExecSuccess(result);
|