mongo/jstests/sharding/query/release_memory.js

244 lines
8.3 KiB
JavaScript

/**
* Make sure that running releaseMemory command does not affect the normal query execution.
*
* Uses getMore to pin an open cursor.
* @tags: [
* requires_getmore,
* requires_fcv_82,
* uses_parallel_shell
* ]
*/
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {funWithArgs} from "jstests/libs/parallel_shell_helpers.js";
import {ShardingTest} from "jstests/libs/shardingtest.js";
// This test manually simulates a session, which is not compatible with implicit sessions.
TestData.disableImplicitSessions = true;
const kFailPointName = "waitAfterPinningCursorBeforeGetMoreBatch";
const kFailpointOptions = {
shouldCheckForInterrupt: true,
};
const st = new ShardingTest({shards: 2});
const kDBName = "test";
const mongosDB = st.s.getDB(kDBName);
const shard0DB = st.shard0.getDB(kDBName);
const shard1DB = st.shard1.getDB(kDBName);
st.s.adminCommand({enablesharding: kDBName, primaryShard: st.shard0.name});
let coll = mongosDB.jstest_release_memory;
let docs = [];
for (let i = 0; i < 10; i++) {
docs.push({_id: i});
}
assert.commandWorked(coll.insertMany(docs));
st.shardColl(coll, {_id: 1}, {_id: 5}, {_id: 6}, kDBName, false);
function assertGetMore(targetDB, collName, cursorId, useSession, sessionId, results, docs) {
const testDB = targetDB ? targetDB : db;
let getMoreCmd = {getMore: cursorId, collection: collName, batchSize: 4};
if (useSession) {
getMoreCmd.lsid = sessionId;
}
while (cursorId != 0) {
jsTest.log(`Running getMore command ${tojson(getMoreCmd)}`);
const getMoreRes = testDB.runCommand(getMoreCmd);
assert.commandWorked(getMoreRes);
const cursor = getMoreRes.cursor;
cursorId = cursor.id;
results.push(...cursor.nextBatch);
}
if (useSession) {
assert.commandWorked(testDB.adminCommand({endSessions: [sessionId]}));
}
assert.sameMembers(results, docs);
}
// Tests that the various cursors involved in a sharded query can release memory.
function testReleaseMemory({func, useSession, cursorsNum, pinCursor, unknownCursor}) {
let cursorIdsArr = [];
let cursorIdx = 0;
let sessionIdsArr = [];
let getMoreJoiner = null;
let results = [];
for (let i = 0; i < cursorsNum; ++i) {
// Run a find against mongos. This should open cursors on both of the shards.
let findCmd = {find: coll.getName(), batchSize: 2};
if (useSession) {
// Manually start a session so it can be continued from inside a parallel shell.
const sessionId = assert.commandWorked(mongosDB.adminCommand({startSession: 1})).id;
findCmd.lsid = sessionId;
sessionIdsArr.push(sessionId);
}
jsTest.log(`Running find command ${i}`);
const findRes = mongosDB.runCommand(findCmd);
assert.commandWorked(findRes);
let cursor = findRes.cursor;
assert.neq(cursor.id, NumberLong(0));
results.push(cursor.firstBatch);
cursorIdsArr.push(cursor.id);
}
let shard0DBFailpoint;
let shard1DBFailpoint;
if (pinCursor) {
assert.gte(cursorsNum, cursorIdx + 1);
shard0DBFailpoint = configureFailPoint(shard0DB, kFailPointName, kFailpointOptions);
shard1DBFailpoint = configureFailPoint(shard1DB, kFailPointName, kFailpointOptions);
getMoreJoiner = startParallelShell(
funWithArgs(
assertGetMore,
null,
coll.getName(),
cursorIdsArr[cursorIdx],
useSession,
sessionIdsArr[cursorIdx],
results[cursorIdx],
docs,
),
st.s.port,
);
// Wait until we know the mongod cursors are pinned.
shard0DBFailpoint.wait();
shard1DBFailpoint.wait();
++cursorIdx;
}
if (unknownCursor) {
assert.gte(cursorsNum, cursorIdx + 1);
// Kill the cursor to make it go away.
jsTest.log(`killing cursor ${cursorIdsArr[cursorIdx]}`);
let cmdRes = assert.commandWorked(
mongosDB.runCommand({killCursors: coll.getName(), cursors: [cursorIdsArr[cursorIdx]]}),
);
assert.eq(cmdRes.cursorsKilled, [cursorIdsArr[cursorIdx]]);
assert.eq(cmdRes.cursorsAlive, []);
assert.eq(cmdRes.cursorsNotFound, []);
assert.eq(cmdRes.cursorsUnknown, []);
++cursorIdx;
}
// Use the function provided by the caller to call the releaseMemory command.
func(cursorIdsArr);
if (pinCursor) {
shard0DBFailpoint.off();
shard1DBFailpoint.off();
// The getMore should finish now
getMoreJoiner();
getMoreJoiner = null;
}
for (; cursorIdx < cursorIdsArr.length; ++cursorIdx) {
assertGetMore(
mongosDB,
coll.getName(),
cursorIdsArr[cursorIdx],
useSession,
sessionIdsArr[cursorIdx],
results[cursorIdx],
docs,
);
}
}
for (let useSession of [false, true]) {
// Test single cursor.
testReleaseMemory({
func: function (mongosCursorIdArr) {
jsTest.log(`Running releaseMemory command for single cursor ${tojson({releaseMemory: mongosCursorIdArr})}`);
let cmdRes = assert.commandWorked(mongosDB.runCommand({releaseMemory: mongosCursorIdArr}));
assert.eq(cmdRes.cursorsReleased, mongosCursorIdArr);
assert.eq(cmdRes.cursorsCurrentlyPinned, []);
assert.eq(cmdRes.cursorsNotFound, []);
},
useSession: useSession,
cursorsNum: 1,
pinCursor: false,
unknownCursor: false,
});
// Test multiple cursors.
testReleaseMemory({
func: function (mongosCursorIdsArr) {
jsTest.log(
`Running releaseMemory command for multiple cursors ${tojson({releaseMemory: mongosCursorIdsArr})}`,
);
let cmdRes = assert.commandWorked(mongosDB.runCommand({releaseMemory: mongosCursorIdsArr}));
assert.eq(cmdRes.cursorsReleased, mongosCursorIdsArr);
assert.eq(cmdRes.cursorsCurrentlyPinned, []);
assert.eq(cmdRes.cursorsNotFound, []);
},
useSession: useSession,
cursorsNum: 2,
pinCursor: false,
unknownCursor: false,
});
// Test not found cursor
testReleaseMemory({
func: function (mongosCursorIdArr) {
jsTest.log(
`Running releaseMemory command for single unknown cursor ${tojson({releaseMemory: mongosCursorIdArr})}`,
);
let cmdRes = assert.commandWorked(mongosDB.runCommand({releaseMemory: mongosCursorIdArr}));
assert.eq(cmdRes.cursorsReleased, []);
assert.eq(cmdRes.cursorsCurrentlyPinned, []);
assert.eq(cmdRes.cursorsNotFound, mongosCursorIdArr);
},
useSession: useSession,
cursorsNum: 1,
pinCursor: false,
unknownCursor: true,
});
// Test pinned cursor
testReleaseMemory({
func: function (mongosCursorIdArr) {
jsTest.log(
`Running releaseMemory command for single cursor pinned ${tojson({releaseMemory: mongosCursorIdArr})}`,
);
let cmdRes = assert.commandWorked(mongosDB.runCommand({releaseMemory: mongosCursorIdArr}));
assert.eq(cmdRes.cursorsReleased, []);
assert.eq(cmdRes.cursorsCurrentlyPinned, mongosCursorIdArr);
assert.eq(cmdRes.cursorsNotFound, []);
},
useSession: useSession,
cursorsNum: 1,
pinCursor: true,
unknownCursor: false,
});
// Test cursor combinations
testReleaseMemory({
func: function (mongosCursorIdArr) {
jsTest.log(
`Running releaseMemory command for all types of cursors ${tojson({releaseMemory: mongosCursorIdArr})}`,
);
let cmdRes = assert.commandWorked(mongosDB.runCommand({releaseMemory: mongosCursorIdArr}));
assert.eq(cmdRes.cursorsReleased, [mongosCursorIdArr[2]]);
assert.eq(cmdRes.cursorsCurrentlyPinned, [mongosCursorIdArr[0]]);
assert.eq(cmdRes.cursorsNotFound, [mongosCursorIdArr[1]]);
},
useSession: useSession,
cursorsNum: 3,
pinCursor: true,
unknownCursor: true,
});
}
st.stop();