summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/sharding/cursor_timeout.js88
-rw-r--r--src/mongo/db/cursor_manager.cpp2
-rw-r--r--src/mongo/s/query/cluster_cursor_manager.cpp2
3 files changed, 73 insertions, 19 deletions
diff --git a/jstests/sharding/cursor_timeout.js b/jstests/sharding/cursor_timeout.js
index 8eb368c1aa4..85e45d41bde 100644
--- a/jstests/sharding/cursor_timeout.js
+++ b/jstests/sharding/cursor_timeout.js
@@ -1,17 +1,26 @@
// Basic integration tests for the background job that periodically kills idle cursors, in both
// mongod and mongos. This test creates the following four cursors:
//
-// 1. A no-timeout cursor through mongos.
-// 2. A no-timeout cursor through mongod.
-// 3. A normal cursor through mongos.
-// 4. A normal cursor through mongod.
+// 1. A no-timeout cursor through mongos, not attached to a session.
+// 2. A no-timeout cursor through mongod, not attached to a session.
+// 3. A normal cursor through mongos, not attached to a session.
+// 4. A normal cursor through mongod, not attached to a session.
+// 5. A normal cursor through mongos, attached to a session.
+// 6. A normal cursor through mongod, attached to a session.
+//
+// After a period of inactivity, the test asserts that cursors #1 and #2 are still alive, cursors #3
+// and #4 have been killed, and cursors #5 and #6 are still alive (as of SERVER-6036, only cursors
+// not opened as part of a session should be killed by the cursor-timeout mechanism). It then kills
+// 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.
//
-// After a period of inactivity, the test asserts that cursors #1 and #2 are still alive, and that
-// #3 and #4 have been killed.
// @tags: [requires_sharding]
(function() {
'use strict';
+// This test manually simulates a session, which is not compatible with implicit sessions.
+TestData.disableImplicitSessions = true;
+
// 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
@@ -49,11 +58,13 @@ const st = new ShardingTest({
});
const adminDB = st.admin;
-const routerColl = st.s.getDB('test').user;
+const mongosDB = st.s.getDB('test');
+const routerColl = mongosDB.user;
const shardHost = st.config.shards.findOne({_id: st.shard1.shardName}).host;
const mongod = new Mongo(shardHost);
const shardColl = mongod.getCollection(routerColl.getFullName());
+const shardDB = shardColl.getDB();
assert.commandWorked(adminDB.runCommand({enableSharding: routerColl.getDB().getName()}));
st.ensurePrimaryShard(routerColl.getDB().getName(), st.shard0.shardName);
@@ -77,32 +88,45 @@ const routerCursorWithTimeout = routerColl.find().batchSize(1);
const routerCursorWithNoTimeout = routerColl.find().batchSize(1);
routerCursorWithNoTimeout.addOption(DBQuery.Option.noTimeout);
+// Open a session on mongos.
+let routerSession = mongosDB.getMongo().startSession();
+let routerSessionDB = routerSession.getDatabase(mongosDB.getName());
+let routerSessionCursor = routerSessionDB.user.find().batchSize(1);
+
// Open both a normal and a no-timeout cursor on mongod. Batch size is 1 to ensure that
// cursor.next() performs only a single operation.
const shardCursorWithTimeout = shardColl.find().batchSize(1);
const shardCursorWithNoTimeout = shardColl.find().batchSize(1);
shardCursorWithNoTimeout.addOption(DBQuery.Option.noTimeout);
+// Open a session on mongod.
+let shardSession = shardDB.getMongo().startSession();
+let shardSessionDB = shardSession.getDatabase(shardDB.getName());
+let shardSessionCursor = shardSessionDB.user.find().batchSize(1);
+
// Execute initial find on each cursor.
routerCursorWithTimeout.next();
routerCursorWithNoTimeout.next();
shardCursorWithTimeout.next();
shardCursorWithNoTimeout.next();
+routerSessionCursor.next();
+shardSessionCursor.next();
-// Wait until the idle cursor background job has killed the cursors that do not have the "no
-// timeout" flag set. We use the "cursorTimeoutMillis" and "clientCursorMonitorFrequencySecs"
-// setParameters above to reduce the amount of time we need to wait here.
+// Wait until the idle cursor background job has killed the session-unattached cursors that do not
+// have the "no timeout" flag set. We use the "cursorTimeoutMillis" and
+// "clientCursorMonitorFrequencySecs" setParameters above to reduce the amount of time we need to
+// wait here.
assert.soon(function() {
return routerColl.getDB().serverStatus().metrics.cursor.timedOut > 0;
}, "sharded cursor failed to time out");
-// Wait for the shard to have two open cursors on it (routerCursorWithNoTimeout and
-// shardCursorWithNoTimeout).
-// We cannot reliably use metrics.cursor.timedOut here, because this will be 2 if
-// routerCursorWithTimeout is killed for timing out on the shard, and 1 if
-// routerCursorWithTimeout is killed by a killCursors command from the mongos.
+// Wait for the shard to have four open cursors on it (routerCursorWithNoTimeout,
+// routerSessionCursor, shardCursorWithNoTimeout, and shardSessionCursor). We cannot reliably use
+// metrics.cursor.timedOut here, because this will be 2 if routerCursorWithTimeout is killed for
+// timing out on the shard, and 1 if routerCursorWithTimeout is killed by a killCursors command from
+// the mongos.
assert.soon(function() {
- return shardColl.getDB().serverStatus().metrics.cursor.open.total == 2;
+ return shardColl.getDB().serverStatus().metrics.cursor.open.total == 4;
}, "cursor failed to time out");
assert.throws(function() {
@@ -112,7 +136,37 @@ assert.throws(function() {
shardCursorWithTimeout.itcount();
});
-// +1 because we already advanced once
+// Kill the session that routerSessionCursor and shardSessionCursor are attached to, to simulate
+// that session's expiration. The cursors should be killed and cleaned up once the session is.
+assert.commandWorked(mongosDB.runCommand({killSessions: [routerSession.getSessionId()]}));
+assert.commandWorked(shardDB.runCommand({killSessions: [shardSession.getSessionId()]}));
+
+// Wait for the shard to have two open cursors on it (routerCursorWithNoTimeout,
+// shardCursorWithNoTimeout).
+assert.soon(function() {
+ return shardColl.getDB().serverStatus().metrics.cursor.open.total == 2;
+}, "session cursor failed to time out");
+
+assert.throws(function() {
+ routerSessionCursor.itcount();
+});
+assert.throws(function() {
+ shardSessionCursor.itcount();
+});
+
+// Verify that the session cursors are really gone by running a killCursors command, and checking
+// that the cursorors are reported as "not found". Credit to kill_pinned_cursor_js_test for this
+// idea.
+let killRes = mongosDB.runCommand({
+ killCursors: routerColl.getName(),
+ cursors: [routerSessionCursor.getId(), shardSessionCursor.getId()]
+});
+assert.commandWorked(killRes);
+assert.eq(killRes.cursorsAlive, []);
+assert.eq(killRes.cursorsNotFound, [routerSessionCursor.getId(), shardSessionCursor.getId()]);
+assert.eq(killRes.cursorsUnknown, []);
+
+// +1 because we already advanced one. Ensure that the session-unattached cursors are still valid.
assert.eq(routerColl.count(), routerCursorWithNoTimeout.itcount() + 1);
assert.eq(shardColl.count(), shardCursorWithNoTimeout.itcount() + 1);
diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp
index 790f91472b4..bd2800964fb 100644
--- a/src/mongo/db/cursor_manager.cpp
+++ b/src/mongo/db/cursor_manager.cpp
@@ -120,7 +120,7 @@ CursorManager::~CursorManager() {
}
bool CursorManager::cursorShouldTimeout_inlock(const ClientCursor* cursor, Date_t now) {
- if (cursor->isNoTimeout() || cursor->_operationUsingCursor) {
+ if (cursor->isNoTimeout() || cursor->_operationUsingCursor || cursor->getSessionId()) {
return false;
}
return (now - cursor->_lastUseDate) >= Milliseconds(getCursorTimeoutMillis());
diff --git a/src/mongo/s/query/cluster_cursor_manager.cpp b/src/mongo/s/query/cluster_cursor_manager.cpp
index 73ae523f978..64fcb8e4d87 100644
--- a/src/mongo/s/query/cluster_cursor_manager.cpp
+++ b/src/mongo/s/query/cluster_cursor_manager.cpp
@@ -411,7 +411,7 @@ 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 &&
+ bool res = entry.getLifetimeType() == CursorLifetime::Mortal && !entry.getLsid() &&
!entry.getOperationUsingCursor() && entry.getLastActive() <= cutoff;
if (res) {