mirror of https://github.com/mongodb/mongo
177 lines
7.2 KiB
JavaScript
177 lines
7.2 KiB
JavaScript
/**
|
|
* @tags: [
|
|
* requires_scripting
|
|
* ]
|
|
*/
|
|
|
|
// Confirms that JavaScript heap limits are respected in aggregation. Includes testing for mapReduce
|
|
// and $where which use aggregation for execution.
|
|
import {ShardingTest} from "jstests/libs/shardingtest.js";
|
|
|
|
const st = new ShardingTest({shards: 2});
|
|
const mongos = st.s;
|
|
|
|
let mongosDB = mongos.getDB("test");
|
|
let mongosColl = mongosDB.coll;
|
|
|
|
// Shard the collection with one chunk per shard. Insert a single document into each shard.
|
|
st.shardColl(mongosColl.getName(), {x: 1}, {x: 2}, {x: 2});
|
|
assert.commandWorked(mongosColl.insert([{x: 0}, {x: 2}]));
|
|
|
|
// The limits chosen in this test for "tooSmallHeapSizeMB", "sufficentHeapSizeMB" and "arraySize"
|
|
// reflect a setup where allocating a string of size "arraySize" with a "tooSmallHeapSizeMB"
|
|
// JavaScript heap limit will trigger an OOM event, whereas allocating the same array with a
|
|
// "sufficentHeapSizeMB" JavaScript heap limit will succeed.
|
|
const tooSmallHeapSizeMB = 10;
|
|
const sufficentHeapSizeMB = 100;
|
|
|
|
function setHeapSizeLimitMB({db, queryLimit, globalLimit}) {
|
|
assert.commandWorked(
|
|
db.adminCommand({setParameter: 1, internalQueryJavaScriptHeapSizeLimitMB: queryLimit}));
|
|
assert.commandWorked(db.adminCommand({setParameter: 1, jsHeapLimitMB: globalLimit}));
|
|
}
|
|
|
|
function allocateLargeString() {
|
|
const arraySize = 1000000;
|
|
let str = new Array(arraySize).fill(0);
|
|
return true;
|
|
}
|
|
|
|
const mapReduce = {
|
|
mapReduce: "coll",
|
|
map: allocateLargeString,
|
|
reduce: function(k, v) {
|
|
return 1;
|
|
},
|
|
out: {inline: 1}
|
|
};
|
|
const aggregateWithJSFunction = {
|
|
aggregate: "coll",
|
|
cursor: {},
|
|
pipeline: [
|
|
{$group: {_id: "$x"}},
|
|
{$project: {y: {"$function": {args: [], body: allocateLargeString, lang: "js"}}}}
|
|
],
|
|
allowDiskUse: false
|
|
};
|
|
const aggregateWithInternalJsReduce = {
|
|
aggregate: "coll",
|
|
cursor: {},
|
|
pipeline: [{
|
|
$group: {
|
|
_id: "$x",
|
|
value: {
|
|
$_internalJsReduce: {
|
|
data: {k: "$x", v: "$x"},
|
|
eval: allocateLargeString,
|
|
}
|
|
}
|
|
}
|
|
}],
|
|
allowDiskUse: false
|
|
};
|
|
const aggregateWithUserDefinedAccumulator = {
|
|
aggregate: "coll",
|
|
cursor: {},
|
|
pipeline: [{
|
|
$group: {
|
|
_id: "$x",
|
|
value: {
|
|
$accumulator: {
|
|
init: allocateLargeString,
|
|
accumulate: allocateLargeString,
|
|
accumulateArgs: [{k: "$x", v: "$x"}],
|
|
merge: allocateLargeString,
|
|
lang: 'js',
|
|
}
|
|
}
|
|
}
|
|
}],
|
|
allowDiskUse: false
|
|
};
|
|
const findWithJavaScriptFunction = {
|
|
find: "coll",
|
|
filter: {$expr: {"$function": {args: [], body: allocateLargeString, lang: "js"}}}
|
|
};
|
|
const findWithWhere = {
|
|
find: "coll",
|
|
filter: {$where: allocateLargeString}
|
|
};
|
|
|
|
/**
|
|
* The following tests will execute JavaScript on the process represented by 'db' regardless of
|
|
* whether it is a mongod or mongos. This is because the JavaScript expressions live in the merger
|
|
* part of an aggregation pipeline.
|
|
*/
|
|
function runCommonTests(db) {
|
|
// All commands are expected to work with a sufficient JS heap size.
|
|
setHeapSizeLimitMB({db: db, queryLimit: sufficentHeapSizeMB, globalLimit: sufficentHeapSizeMB});
|
|
assert.commandWorked(db.runCommand(aggregateWithJSFunction));
|
|
assert.commandWorked(db.runCommand(aggregateWithInternalJsReduce));
|
|
assert.commandWorked(db.runCommand(aggregateWithUserDefinedAccumulator));
|
|
|
|
// The aggregate command is expected to fail when the aggregation specific heap size limit is
|
|
// too low.
|
|
setHeapSizeLimitMB({db: db, queryLimit: tooSmallHeapSizeMB, globalLimit: sufficentHeapSizeMB});
|
|
assert.commandFailedWithCode(db.runCommand(aggregateWithJSFunction),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
assert.commandFailedWithCode(db.runCommand(aggregateWithInternalJsReduce),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
assert.commandFailedWithCode(db.runCommand(aggregateWithUserDefinedAccumulator),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
|
|
// All commands are expected to fail when the global heap size limit is too low, regardless
|
|
// of the aggregation limit.
|
|
setHeapSizeLimitMB({db: db, queryLimit: sufficentHeapSizeMB, globalLimit: tooSmallHeapSizeMB});
|
|
assert.commandFailedWithCode(db.runCommand(aggregateWithJSFunction),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
assert.commandFailedWithCode(db.runCommand(aggregateWithInternalJsReduce),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
assert.commandFailedWithCode(db.runCommand(aggregateWithUserDefinedAccumulator),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
}
|
|
|
|
/**
|
|
* The following tests will execute JavaScript only on mongod. This is because $where and $expr are
|
|
* only evaluated on mongod, not on mongos.
|
|
*/
|
|
function runShardTests(db) {
|
|
// All commands are expected to work with a sufficient JS heap size.
|
|
setHeapSizeLimitMB({db: db, queryLimit: sufficentHeapSizeMB, globalLimit: sufficentHeapSizeMB});
|
|
assert.commandWorked(db.runCommand(findWithJavaScriptFunction));
|
|
// TODO SERVER-45454: Uncomment when $where is executed via aggregation JavaScript expression.
|
|
// assert.commandWorked(db.runCommand(findWithWhere));
|
|
assert.commandWorked(db.runCommand(mapReduce));
|
|
|
|
// A find command with JavaScript agg expression is expected to fail when the query specific
|
|
// heap size limit is too low.
|
|
setHeapSizeLimitMB({db: db, queryLimit: tooSmallHeapSizeMB, globalLimit: sufficentHeapSizeMB});
|
|
assert.commandFailedWithCode(db.runCommand(findWithJavaScriptFunction),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
|
|
// The mapReduce command and $where are not limited by the query heap size limit and will
|
|
// succeed even if it is set too low.
|
|
setHeapSizeLimitMB({db: db, queryLimit: tooSmallHeapSizeMB, globalLimit: sufficentHeapSizeMB});
|
|
assert.commandWorked(db.runCommand(mapReduce));
|
|
// TODO SERVER-45454: Uncomment when $where is executed via aggregation JavaScript expression.
|
|
// assert.commandWorked(db.runCommand(findWithWhere));
|
|
|
|
// All commands are expected to fail when the global heap size limit is too low, regardless
|
|
// of the aggregation limit.
|
|
setHeapSizeLimitMB({db: db, queryLimit: sufficentHeapSizeMB, globalLimit: tooSmallHeapSizeMB});
|
|
assert.commandFailedWithCode(db.runCommand(findWithJavaScriptFunction),
|
|
ErrorCodes.JSInterpreterFailure);
|
|
// TODO SERVER-45454: Uncomment when $where is executed via aggregation JavaScript expression.
|
|
// assert.commandFailedWithCode(db.runCommand(findWithWhere), ErrorCodes.JSInterpreterFailure);
|
|
assert.commandFailedWithCode(db.runCommand(mapReduce), ErrorCodes.JSInterpreterFailure);
|
|
}
|
|
|
|
// Test command invocations that can execute JavaScript on either mongos or mongod.
|
|
runCommonTests(mongosDB);
|
|
|
|
// Test command invocations that will only execute JavaScript on the shards.
|
|
const shardDB = st.shard0.getDB("test");
|
|
runCommonTests(shardDB);
|
|
runShardTests(shardDB);
|
|
|
|
st.stop(); |