summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Swanson <charlie.swanson@mongodb.com>2018-03-13 17:40:50 -0400
committerCharlie Swanson <charlie.swanson@mongodb.com>2018-03-16 18:03:34 -0400
commit9eea82ee2a0e37da90cbb549d55c5eac8aa69a55 (patch)
treef491a6dacfa553e2f872493b7ab4f3a37f913dcd
parent15840a84b769ffecc7a9f33cf003ecb58eb6bf87 (diff)
downloadmongo-9eea82ee2a0e37da90cbb549d55c5eac8aa69a55.tar.gz
SERVER-33542 Avoid timing out cursor between batches
This will provide a guarantee that if a cursor times out, the error returned to the client will be ExceededTimeLimit.
-rw-r--r--jstests/core/max_time_ms.js16
-rw-r--r--src/mongo/db/cursor_manager.cpp11
2 files changed, 12 insertions, 15 deletions
diff --git a/jstests/core/max_time_ms.js b/jstests/core/max_time_ms.js
index d7a99dae4f3..9b9b11ed7d5 100644
--- a/jstests/core/max_time_ms.js
+++ b/jstests/core/max_time_ms.js
@@ -29,9 +29,7 @@ cursor.maxTimeMS(100);
error = assert.throws(function() {
cursor.itcount();
}, [], "expected query to abort due to time limit");
-if (error.code !== ErrorCodes.CursorNotFound) {
- assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
-}
+assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
//
// Simple negative test for query: a ~300ms query with a 10s time limit should not hit the time
@@ -81,9 +79,7 @@ error = assert.throws(function() {
cursor.next();
cursor.next();
}, [], "expected batch 2 (getmore) to abort due to time limit");
-if (error.code !== ErrorCodes.CursorNotFound) {
- assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
-}
+assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
//
// Simple negative test for getmore:
@@ -139,9 +135,7 @@ cursor.maxTimeMS(6 * 1000);
error = assert.throws(function() {
cursor.itcount();
}, [], "expected find() to abort due to time limit");
-if (error.code !== ErrorCodes.CursorNotFound) {
- assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
-}
+assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
//
// Many-batch negative test for getmore:
@@ -414,9 +408,7 @@ assert.commandWorked(
error = assert.throws(function() {
cursor.itcount();
}, [], "expected query to abort due to time limit");
-if (error.code !== ErrorCodes.CursorNotFound) {
- assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
-}
+assert.eq(ErrorCodes.ExceededTimeLimit, error.code);
assert.commandWorked(
t.getDB().adminCommand({configureFailPoint: "maxTimeAlwaysTimeOut", mode: "off"}));
diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp
index 7f2b9fbe199..4ae9bafd9ed 100644
--- a/src/mongo/db/cursor_manager.cpp
+++ b/src/mongo/db/cursor_manager.cpp
@@ -637,14 +637,19 @@ void CursorManager::unpin(OperationContext* opCtx, ClientCursor* cursor) {
auto interruptStatus = cursor->_operationUsingCursor->checkForInterruptNoAssert();
cursor->_operationUsingCursor = nullptr;
cursor->_lastUseDate = now;
- if (!interruptStatus.isOK()) {
- // If an interrupt occurred after the batch was completed, we remove the now-unpinned cursor
- // from the CursorManager, then dispose of and delete it.
+
+ // If someone was trying to kill this cursor with a killOp or a killCursors, they are likely
+ // interesting in proactively cleaning up that cursor's resources. In these cases, we
+ // proactively delete the cursor. In other cases we preserve the error code so that the client
+ // will see the reason the cursor was killed when asking for the next batch.
+ if (interruptStatus == ErrorCodes::Interrupted || interruptStatus == ErrorCodes::CursorKilled) {
LOG(0) << "removing cursor " << cursor->cursorid()
<< " after completing batch: " << interruptStatus;
partition->erase(cursor->cursorid());
cursor->dispose(opCtx);
delete cursor;
+ } else if (!interruptStatus.isOK()) {
+ cursor->markAsKilled(interruptStatus);
}
}