summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Wahlin <james@mongodb.com>2021-08-25 15:25:30 -0400
committerJames Wahlin <james@mongodb.com>2021-08-27 13:41:21 +0000
commita04b6e222795b0389539bd1ea95b86937fb71244 (patch)
tree9c4846bdecb24a1702d5ad65342e9bc94d180ab7
parentfa47f037feca94fd8385a15dd1fc3a23e648d880 (diff)
downloadmongo-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.js20
-rw-r--r--jstests/sharding/query/cursor_timeout.js51
-rw-r--r--src/mongo/db/cursor_manager.cpp4
-rw-r--r--src/mongo/db/query/query_knobs.idl7
-rw-r--r--src/mongo/s/query/cluster_cursor_manager.cpp10
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,