diff options
author | Charlie Swanson <charlie.swanson@mongodb.com> | 2018-03-13 17:40:50 -0400 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2018-03-16 18:03:34 -0400 |
commit | 9eea82ee2a0e37da90cbb549d55c5eac8aa69a55 (patch) | |
tree | f491a6dacfa553e2f872493b7ab4f3a37f913dcd | |
parent | 15840a84b769ffecc7a9f33cf003ecb58eb6bf87 (diff) | |
download | mongo-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.js | 16 | ||||
-rw-r--r-- | src/mongo/db/cursor_manager.cpp | 11 |
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); } } |