diff options
author | Nick Zolnierz <nicholas.zolnierz@mongodb.com> | 2020-03-18 15:17:56 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-03-25 17:40:43 +0000 |
commit | fe79c4ee1dfc8d49ae06c94a927267712b32b011 (patch) | |
tree | a5ddce887802b62fb028c5d707186ebb64effe66 /jstests/noPassthrough/agg_cursor_timeout.js | |
parent | b36c69c5930d25a8f5ae348a2b2fb24f27f925e6 (diff) | |
download | mongo-fe79c4ee1dfc8d49ae06c94a927267712b32b011.tar.gz |
SERVER-46700 Update tests in aggregation suite to avoid spawning mongod/sharded clusters
Diffstat (limited to 'jstests/noPassthrough/agg_cursor_timeout.js')
-rw-r--r-- | jstests/noPassthrough/agg_cursor_timeout.js | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/jstests/noPassthrough/agg_cursor_timeout.js b/jstests/noPassthrough/agg_cursor_timeout.js new file mode 100644 index 00000000000..ff5d7001374 --- /dev/null +++ b/jstests/noPassthrough/agg_cursor_timeout.js @@ -0,0 +1,126 @@ +/** + * Tests that an aggregation cursor is killed when it is timed out by the ClientCursorMonitor. + * + * This test was designed to reproduce SERVER-25585. + */ +(function() { +'use strict'; + +// Cursor timeout on mongod is handled by a single thread/timer that will sleep for +// "clientCursorMonitorFrequencySecs" and add the sleep value to each operation's duration when +// it wakes up, timing out those whose "now() - last accessed since" time exceeds. A cursor +// timeout of 2 seconds with a monitor frequency of 1 second means an effective timeout period +// of 1 to 2 seconds. +const cursorTimeoutMs = 2000; +const cursorMonitorFrequencySecs = 1; + +const options = { + setParameter: { + internalDocumentSourceCursorBatchSizeBytes: 1, + // We use the "cursorTimeoutMillis" server parameter to decrease how long it takes for a + // non-exhausted cursor to time out. We use the "clientCursorMonitorFrequencySecs" + // server parameter to make the ClientCursorMonitor that cleans up the timed out cursors + // run more often. The combination of these server parameters reduces the amount of time + // we need to wait within this test. + cursorTimeoutMillis: cursorTimeoutMs, + clientCursorMonitorFrequencySecs: cursorMonitorFrequencySecs, + } +}; +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(options)); + +const testDB = conn.getDB('test'); + +// We use a batch size of 2 to ensure that the mongo shell does not exhaust the cursor on its +// first batch. +const batchSize = 2; +const numMatches = 5; + +function assertCursorTimesOut(collName, pipeline) { + const res = assert.commandWorked(testDB.runCommand({ + aggregate: collName, + pipeline: pipeline, + cursor: { + batchSize: batchSize, + }, + })); + + let serverStatus = assert.commandWorked(testDB.serverStatus()); + const expectedNumTimedOutCursors = serverStatus.metrics.cursor.timedOut + 1; + + const cursor = new DBCommandCursor(testDB, res, batchSize); + + // Wait until the idle cursor background job has killed the aggregation cursor. + assert.soon( + function() { + serverStatus = assert.commandWorked(testDB.serverStatus()); + return +serverStatus.metrics.cursor.timedOut === expectedNumTimedOutCursors; + }, + function() { + return "aggregation cursor failed to time out: " + tojson(serverStatus.metrics.cursor); + }); + + assert.eq(0, serverStatus.metrics.cursor.open.total, tojson(serverStatus)); + + // We attempt to exhaust the aggregation cursor to verify that sending a getMore returns an + // error due to the cursor being killed. + let err = assert.throws(function() { + cursor.itcount(); + }); + assert.eq(ErrorCodes.CursorNotFound, err.code, tojson(err)); +} + +assert.commandWorked(testDB.source.insert({local: 1})); +for (let i = 0; i < numMatches; ++i) { + assert.commandWorked(testDB.dest.insert({foreign: 1})); +} + +// Test that a regular aggregation cursor is killed when the timeout is reached. +assertCursorTimesOut('dest', []); + +// Test that an aggregation cursor with a $lookup stage is killed when the timeout is reached. +assertCursorTimesOut('source', [ + { + $lookup: { + from: 'dest', + localField: 'local', + foreignField: 'foreign', + as: 'matches', + } + }, + { + $unwind: "$matches", + }, + ]); + +// Test that an aggregation cursor with nested $lookup stages is killed when the timeout is +// reached. +assertCursorTimesOut('source', [ + { + $lookup: { + from: 'dest', + let : {local1: "$local"}, + pipeline: [ + {$match: {$expr: {$eq: ["$foreign", "$$local1"]}}}, + { + $lookup: { + from: 'source', + let : {foreign1: "$foreign"}, + pipeline: [{$match: {$expr: {$eq: ["$local", "$$foreign1"]}}}], + as: 'matches2' + } + }, + { + $unwind: "$matches2", + }, + ], + as: 'matches1', + } + }, + { + $unwind: "$matches1", + }, + ]); + +MongoRunner.stopMongod(conn); +})(); |