diff options
author | James Wahlin <james@mongodb.com> | 2021-08-25 15:25:30 -0400 |
---|---|---|
committer | James Wahlin <james@mongodb.com> | 2021-08-27 13:41:21 +0000 |
commit | a04b6e222795b0389539bd1ea95b86937fb71244 (patch) | |
tree | 9c4846bdecb24a1702d5ad65342e9bc94d180ab7 | |
parent | fa47f037feca94fd8385a15dd1fc3a23e648d880 (diff) | |
download | mongo-a04b6e222795b0389539bd1ea95b86937fb71244.tar.gz |
SERVER-59573 Add setParameter which can be used to restore inactive cursor timeout in sessions
(cherry picked from commit f40810c7c8706754b647992fbde1dc2745616b7c)
-rw-r--r-- | jstests/noPassthrough/agg_cursor_timeout.js | 20 | ||||
-rw-r--r-- | jstests/sharding/query/cursor_timeout.js | 51 | ||||
-rw-r--r-- | src/mongo/db/cursor_manager.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/query/query_knobs.idl | 7 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_cursor_manager.cpp | 10 |
5 files changed, 83 insertions, 9 deletions
diff --git a/jstests/noPassthrough/agg_cursor_timeout.js b/jstests/noPassthrough/agg_cursor_timeout.js index 307372cd29d..b82e1e44384 100644 --- a/jstests/noPassthrough/agg_cursor_timeout.js +++ b/jstests/noPassthrough/agg_cursor_timeout.js @@ -36,11 +36,7 @@ const testDB = conn.getDB('test'); const batchSize = 2; const numMatches = 5; -function assertCursorTimesOut(collName, pipeline) { - // Cursor timeout only occurs outside of sessions. Otherwise we rely on the session timeout - // mechanism to kill cursors. - TestData.disableImplicitSessions = true; - +function assertCursorTimesOutImpl(collName, pipeline) { const res = assert.commandWorked(testDB.runCommand({ aggregate: collName, pipeline: pipeline, @@ -72,8 +68,22 @@ function assertCursorTimesOut(collName, pipeline) { cursor.itcount(); }); assert.eq(ErrorCodes.CursorNotFound, err.code, tojson(err)); +} +function assertCursorTimesOut(collName, pipeline) { + // Confirm that cursor timeout occurs outside of sessions. + TestData.disableImplicitSessions = true; + assertCursorTimesOutImpl(collName, pipeline); TestData.disableImplicitSessions = false; + + // Confirm that cursor timeout occurs within sessions when the + // `enableTimeoutOfInactiveSessionCursors` parameter is set to true. If false, we rely on + // session expiration to cleanup outstanding cursors. + assert.commandWorked( + testDB.adminCommand({setParameter: 1, enableTimeoutOfInactiveSessionCursors: true})); + assertCursorTimesOutImpl(collName, pipeline); + assert.commandWorked( + testDB.adminCommand({setParameter: 1, enableTimeoutOfInactiveSessionCursors: false})); } assert.commandWorked(testDB.source.insert({local: 1})); diff --git a/jstests/sharding/query/cursor_timeout.js b/jstests/sharding/query/cursor_timeout.js index a987d9f0925..763832cd198 100644 --- a/jstests/sharding/query/cursor_timeout.js +++ b/jstests/sharding/query/cursor_timeout.js @@ -14,7 +14,7 @@ // the session cursors #5 and #6 are attached to to simulate that session timing out, and ensures // that cursors #5 and #6 are killed as a result. // -// @tags: [requires_sharding, requires_find_command, requires_fcv_47] +// @tags: [requires_sharding, requires_find_command, requires_fcv_50] (function() { 'use strict'; @@ -170,5 +170,54 @@ assert.eq(killRes.cursorsUnknown, []); assert.eq(routerColl.count(), routerCursorWithNoTimeout.itcount() + 1); assert.eq(shardColl.count(), shardCursorWithNoTimeout.itcount() + 1); +// Confirm that cursors opened within a session will timeout when the +// 'enableTimeoutOfInactiveSessionCursors' setParameter has been enabled. +(function() { +assert.commandWorked( + mongosDB.adminCommand({setParameter: 1, enableTimeoutOfInactiveSessionCursors: true})); +assert.commandWorked( + shardDB.adminCommand({setParameter: 1, enableTimeoutOfInactiveSessionCursors: true})); + +// Open a session on mongos. +routerSession = mongosDB.getMongo().startSession(); +routerSessionDB = routerSession.getDatabase(mongosDB.getName()); +routerSessionCursor = routerSessionDB.user.find().batchSize(1); +const numRouterCursorsTimedOut = routerColl.getDB().serverStatus().metrics.cursor.timedOut; + +// Open a session on mongod. +shardSession = shardDB.getMongo().startSession(); +shardSessionDB = shardSession.getDatabase(shardDB.getName()); +shardSessionCursor = shardSessionDB.user.find().batchSize(1); +const numShardCursorsTimedOut = routerColl.getDB().serverStatus().metrics.cursor.timedOut; + +// Execute initial find on each cursor. +routerSessionCursor.next(); +shardSessionCursor.next(); + +// Wait until mongos reflects the newly timed out cursors. +assert.soon(function() { + return shardColl.getDB().serverStatus().metrics.cursor.timedOut >= + (numRouterCursorsTimedOut + 1); +}, "sharded cursor failed to time out"); + +// Wait until mongod reflects the newly timed out cursors. +assert.soon(function() { + return routerColl.getDB().serverStatus().metrics.cursor.timedOut >= + (numShardCursorsTimedOut + 1); +}, "router cursor failed to time out"); + +assert.throws(function() { + routerCursorWithTimeout.itcount(); +}); +assert.throws(function() { + shardCursorWithTimeout.itcount(); +}); + +assert.commandWorked( + mongosDB.adminCommand({setParameter: 1, enableTimeoutOfInactiveSessionCursors: false})); +assert.commandWorked( + shardDB.adminCommand({setParameter: 1, enableTimeoutOfInactiveSessionCursors: false})); +})(); + st.stop(); })(); diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp index d5dc80b445a..fdd0abda921 100644 --- a/src/mongo/db/cursor_manager.cpp +++ b/src/mongo/db/cursor_manager.cpp @@ -51,6 +51,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/query/plan_executor.h" +#include "mongo/db/query/query_knobs_gen.h" #include "mongo/db/service_context.h" #include "mongo/logv2/log.h" #include "mongo/platform/random.h" @@ -164,7 +165,8 @@ CursorManager::~CursorManager() { } bool CursorManager::cursorShouldTimeout_inlock(const ClientCursor* cursor, Date_t now) { - if (cursor->isNoTimeout() || cursor->_operationUsingCursor || cursor->getSessionId()) { + if (cursor->isNoTimeout() || cursor->_operationUsingCursor || + (cursor->getSessionId() && !enableTimeoutOfInactiveSessionCursors.load())) { return false; } return (now - cursor->_lastUseDate) >= Milliseconds(getCursorTimeoutMillis()); diff --git a/src/mongo/db/query/query_knobs.idl b/src/mongo/db/query/query_knobs.idl index 858ee54534c..84fb9dd216c 100644 --- a/src/mongo/db/query/query_knobs.idl +++ b/src/mongo/db/query/query_knobs.idl @@ -494,3 +494,10 @@ server_parameters: cpp_varname: "enableSearchMeta" cpp_vartype: AtomicWord<bool> default: true + + enableTimeoutOfInactiveSessionCursors: + description: "If true, cursors opened within sessions are eligible for inactive cursor timeout." + set_at: [ startup, runtime ] + cpp_varname: "enableTimeoutOfInactiveSessionCursors" + cpp_vartype: AtomicWord<bool> + default: false diff --git a/src/mongo/s/query/cluster_cursor_manager.cpp b/src/mongo/s/query/cluster_cursor_manager.cpp index d5744416e46..be5f905ff35 100644 --- a/src/mongo/s/query/cluster_cursor_manager.cpp +++ b/src/mongo/s/query/cluster_cursor_manager.cpp @@ -38,6 +38,7 @@ #include "mongo/db/kill_sessions_common.h" #include "mongo/db/logical_session_cache.h" +#include "mongo/db/query/query_knobs_gen.h" #include "mongo/logv2/log.h" #include "mongo/util/clock_source.h" #include "mongo/util/str.h" @@ -411,8 +412,13 @@ std::size_t ClusterCursorManager::killMortalCursorsInactiveSince(OperationContex stdx::unique_lock<Latch> lk(_mutex); auto pred = [cutoff](CursorId cursorId, const CursorEntry& entry) -> bool { - bool res = entry.getLifetimeType() == CursorLifetime::Mortal && !entry.getLsid() && - !entry.getOperationUsingCursor() && entry.getLastActive() <= cutoff; + if (entry.getLifetimeType() == CursorLifetime::Immortal || + entry.getOperationUsingCursor() || + (entry.getLsid() && !enableTimeoutOfInactiveSessionCursors.load())) { + return false; + } + + bool res = entry.getLastActive() <= cutoff; if (res) { LOGV2(22837, |