0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-21 20:49:10 +01:00
mongodb/jstests/noPassthrough/kill_pinned_cursor.js
Matt Broadstone 771dabd098 SERVER-81339 Convert ReplSetTest and ShardingTest to modules (#26332)
GitOrigin-RevId: 744aa110a53786b23c62ff53f87a1418b5991e8d
2024-08-20 22:00:49 +00:00

115 lines
4.9 KiB
JavaScript

// @tags: [
// does_not_support_stepdowns,
// requires_getmore,
// requires_replication,
// ]
//
// Uses getMore to pin an open cursor.
//
// Does not support stepdowns because if a stepdown were to occur between running find() and
// calling killCursors on the cursor ID returned by find(), the killCursors might be sent to
// different node than the one which has the cursor. This would result in the node returning
// "CursorNotFound."
//
// Test killing a pinned cursor. Since cursors are generally pinned for short periods while result
// batches are generated, this requires some special machinery to keep a cursor permanently pinned.
// This test runs manual getMores using different connections, which will not inherit the
// implicit session of the cursor establishing command.
TestData.disableImplicitSessions = true;
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
import {withPinnedCursor} from "jstests/libs/pin_getmore_cursor.js";
import {ShardingTest} from "jstests/libs/shardingtest.js";
const st = new ShardingTest({shards: 2});
// Enables the specified 'failPointName', executes 'runGetMoreFunc' function in a parallel
// shell, waits for the the failpoint to be hit, then kills the cursor and confirms that the
// kill was successful.
function runPinnedCursorKillTest({conn, failPointName, runGetMoreFunc}) {
function assertFunction(cursorId, coll) {
const db = coll.getDB();
// Kill the cursor associated with the command and assert that the kill succeeded.
let cmdRes = db.runCommand({killCursors: coll.getName(), cursors: [cursorId]});
assert.commandWorked(cmdRes);
assert.eq(cmdRes.cursorsKilled, [cursorId]);
assert.eq(cmdRes.cursorsAlive, []);
assert.eq(cmdRes.cursorsNotFound, []);
assert.eq(cmdRes.cursorsUnknown, []);
}
withPinnedCursor({
conn: conn,
sessionId: null,
db: conn.getDB("test"),
assertFunction: assertFunction,
runGetMoreFunc: runGetMoreFunc,
failPointName: failPointName,
assertEndCounts: true
});
}
// Test that killing the pinned cursor before it starts building the batch results in a
// CursorKilled exception on a replica set.
const rs0Conn = st.rs0.getPrimary();
const testParameters = {
conn: rs0Conn,
failPointName: "waitAfterPinningCursorBeforeGetMoreBatch",
runGetMoreFunc: function(collName, cursorId) {
const response = db.runCommand({getMore: cursorId, collection: collName});
// We expect that the operation will get interrupted and fail.
assert.commandFailedWithCode(response, ErrorCodes.CursorKilled);
}
};
runPinnedCursorKillTest(testParameters);
// Check the case where a killCursor is run as we're building a getMore batch on mongod.
(function() {
testParameters.conn = rs0Conn;
testParameters.failPointName = "waitWithPinnedCursorDuringGetMoreBatch";
// Force yield to occur on every PlanExecutor iteration, so that the getMore is guaranteed
// to check for interrupts.
assert.commandWorked(testParameters.conn.getDB("admin").runCommand(
{setParameter: 1, internalQueryExecYieldIterations: 1}));
runPinnedCursorKillTest(testParameters);
})();
(function() {
// Run the equivalent test on the mongos. This time, we will force the shards to hang as
// well. This is so that we can guarantee that the mongos is checking for interruption at
// the appropriate time, and not just propagating an error it receives from the mongods.
testParameters.failPointName = "waitAfterPinningCursorBeforeGetMoreBatch";
FixtureHelpers.runCommandOnEachPrimary({
db: st.s.getDB("admin"),
cmdObj: {configureFailPoint: "waitAfterPinningCursorBeforeGetMoreBatch", mode: "alwaysOn"}
});
testParameters.conn = st.s;
runPinnedCursorKillTest(testParameters);
FixtureHelpers.runCommandOnEachPrimary({
db: st.s.getDB("admin"),
cmdObj: {configureFailPoint: "waitAfterPinningCursorBeforeGetMoreBatch", mode: "off"}
});
})();
// Check this case where the interrupt comes in after the batch has been built, and is about to
// be returned. This is relevant for both mongod and mongos.
const connsToRunOn = [st.s, rs0Conn];
for (let conn of connsToRunOn) {
jsTestLog("Running on conn: " + tojson(conn));
// Test that, if the pinned cursor is killed after it has finished building a batch, that
// batch is returned to the client but a subsequent getMore will fail with a
// 'CursorNotFound' error.
testParameters.failPointName = "waitBeforeUnpinningOrDeletingCursorAfterGetMoreBatch";
testParameters.runGetMoreFunc = function(collName, cursorId) {
const getMoreCmd = {getMore: cursorId, collection: collName, batchSize: 2};
// We expect that the first getMore will succeed, while the second fails because the
// cursor has been killed.
assert.commandWorked(db.runCommand(getMoreCmd));
assert.commandFailedWithCode(db.runCommand(getMoreCmd), ErrorCodes.CursorNotFound);
};
runPinnedCursorKillTest(testParameters);
}
st.stop();