summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheahuychou Mao <cheahuychou.mao@mongodb.com>2020-02-24 22:48:22 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-02-28 19:54:40 +0000
commit0219d8e3489f7934c3eda10a18ac70de81d1e7b4 (patch)
tree9b29a85adfdf1470b84f72e8fdd2f03ca667403d
parentbd05da2372a83bdbe2efa1f488a893a9ea40530c (diff)
downloadmongo-0219d8e3489f7934c3eda10a18ac70de81d1e7b4.tar.gz
SERVER-9391 tailable cursors should throw an exception in DBClientCursor when a cursor is not found on the server
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/session_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml1
-rw-r--r--jstests/core/invalidated_legacy_cursors.js58
-rw-r--r--src/mongo/client/dbclient_cursor.cpp10
-rw-r--r--src/mongo/scripting/mozjs/cursor.cpp14
-rw-r--r--src/mongo/scripting/mozjs/cursor.h3
-rw-r--r--src/mongo/shell/query.js8
-rw-r--r--src/mongo/shell/utils.js1
16 files changed, 96 insertions, 8 deletions
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml
index d1ceab9830c..4912a169df9 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_100ms_refresh_jscore_passthrough.yml
@@ -29,6 +29,7 @@ selector:
- jstests/core/geo_update_btree2.js # notablescan.
- jstests/core/index_id_options.js # "local" database.
- jstests/core/index9.js # "local" database.
+ - jstests/core/invalidated_legacy_cursors.js # capped collections.
- jstests/core/queryoptimizera.js # "local" database.
- jstests/core/rename*.js # renameCollection.
- jstests/core/stages*.js # stageDebug.
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml
index 8101d1f00ce..11ee49f8abb 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_10sec_refresh_jscore_passthrough.yml
@@ -29,6 +29,7 @@ selector:
- jstests/core/geo_update_btree2.js # notablescan.
- jstests/core/index_id_options.js # "local" database.
- jstests/core/index9.js # "local" database.
+ - jstests/core/invalidated_legacy_cursors.js # capped collections.
- jstests/core/queryoptimizera.js # "local" database.
- jstests/core/rename*.js # renameCollection.
- jstests/core/stages*.js # stageDebug.
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml
index d9852dc4dc0..3a78d304f6b 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_1sec_refresh_jscore_passthrough.yml
@@ -29,6 +29,7 @@ selector:
- jstests/core/geo_update_btree2.js # notablescan.
- jstests/core/index_id_options.js # "local" database.
- jstests/core/index9.js # "local" database.
+ - jstests/core/invalidated_legacy_cursors.js # capped collections.
- jstests/core/queryoptimizera.js # "local" database.
- jstests/core/rename*.js # renameCollection.
- jstests/core/stages*.js # stageDebug.
diff --git a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml
index 5ac171b9af9..46d2c6deab2 100644
--- a/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/logical_session_cache_sharding_default_refresh_jscore_passthrough.yml
@@ -29,6 +29,7 @@ selector:
- jstests/core/geo_update_btree2.js # notablescan.
- jstests/core/index_id_options.js # "local" database.
- jstests/core/index9.js # "local" database.
+ - jstests/core/invalidated_legacy_cursors.js # capped collections.
- jstests/core/queryoptimizera.js # "local" database.
- jstests/core/rename*.js # renameCollection.
- jstests/core/stages*.js # stageDebug.
diff --git a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
index 0ae74afdd75..eeda0ca589f 100644
--- a/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/replica_sets_multi_stmt_txn_jscore_passthrough.yml
@@ -162,6 +162,7 @@ selector:
# SERVER-34772 Tailable Cursors are not allowed with snapshot readconcern.
- jstests/core/awaitdata_getmore_cmd.js
- jstests/core/getmore_cmd_maxtimems.js
+ - jstests/core/invalidated_legacy_cursors.js
- jstests/core/tailable_cursor_invalidation.js
- jstests/core/tailable_getmore_batch_size.js
diff --git a/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml
index 8b6e3ecdab3..0c0fe099f80 100644
--- a/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/retryable_writes_jscore_passthrough.yml
@@ -24,6 +24,7 @@ selector:
# These test run commands using legacy queries, which are not supported on sessions.
- jstests/core/comment_field.js
- jstests/core/exhaust.js
+ - jstests/core/invalidated_legacy_cursors.js
- jstests/core/validate_cmd_ns.js
# TODO SERVER-31249: getLastError should not be affected by no-op retries.
diff --git a/buildscripts/resmokeconfig/suites/session_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/session_jscore_passthrough.yml
index f7956032b07..877425132e5 100644
--- a/buildscripts/resmokeconfig/suites/session_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/session_jscore_passthrough.yml
@@ -10,6 +10,7 @@ selector:
# These test run commands using legacy queries, which are not supported on sessions.
- jstests/core/comment_field.js
- jstests/core/exhaust.js
+ - jstests/core/invalidated_legacy_cursors.js
- jstests/core/validate_cmd_ns.js
# Unacknowledged writes prohibited in an explicit session.
diff --git a/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml
index 49e12b91a40..aa0a3605ba3 100644
--- a/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_causally_consistent_jscore_passthrough.yml
@@ -31,6 +31,7 @@ selector:
- jstests/core/geo_update_btree2.js # notablescan.
- jstests/core/index_id_options.js # "local" database.
- jstests/core/index9.js # "local" database.
+ - jstests/core/invalidated_legacy_cursors.js # capped collections.
- jstests/core/queryoptimizera.js # "local" database.
- jstests/core/rename*.js # renameCollection.
- jstests/core/stages*.js # stageDebug.
diff --git a/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml
index 4f4ebba5e8c..bf04f752de4 100644
--- a/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_collections_jscore_passthrough.yml
@@ -31,6 +31,7 @@ selector:
- jstests/core/geo_update_btree2.js # notablescan.
- jstests/core/index_id_options.js # "local" database.
- jstests/core/index9.js # "local" database.
+ - jstests/core/invalidated_legacy_cursors.js # capped collections.
- jstests/core/queryoptimizera.js # "local" database.
- jstests/core/rename*.js # renameCollection.
- jstests/core/stages*.js # stageDebug.
diff --git a/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
index b3b6898667d..0e079adc52d 100644
--- a/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/sharded_multi_stmt_txn_jscore_passthrough.yml
@@ -195,6 +195,7 @@ selector:
# SERVER-34772 Tailable Cursors are not allowed with snapshot readconcern.
- jstests/core/awaitdata_getmore_cmd.js
- jstests/core/getmore_cmd_maxtimems.js
+ - jstests/core/invalidated_legacy_cursors.js
- jstests/core/tailable_cursor_invalidation.js
- jstests/core/tailable_getmore_batch_size.js
diff --git a/jstests/core/invalidated_legacy_cursors.js b/jstests/core/invalidated_legacy_cursors.js
new file mode 100644
index 00000000000..95cb1a0802f
--- /dev/null
+++ b/jstests/core/invalidated_legacy_cursors.js
@@ -0,0 +1,58 @@
+/*
+ * Test that all DBClientCursor cursor types throw an exception when the server returns
+ * CursorNotFound.
+ * @tags: [requires_getmore, requires_non_retryable_commands, assumes_balancer_off]
+ */
+(function() {
+'use strict';
+
+const testDB = db.getSiblingDB("invalidated_legacy_cursors");
+const coll = testDB.test;
+const nDocs = 10;
+const batchSize = 2; // The minimum DBClientCursor batch size is 2.
+
+function setupCollection(isCapped) {
+ coll.drop();
+ if (isCapped) {
+ assert.commandWorked(testDB.createCollection(coll.getName(), {capped: true, size: 4096}));
+ }
+ const bulk = coll.initializeUnorderedBulkOp();
+ for (let i = 0; i < nDocs; ++i) {
+ bulk.insert({_id: i, x: i});
+ }
+ assert.commandWorked(bulk.execute());
+ assert.commandWorked(coll.createIndex({x: 1}));
+}
+
+function testLegacyCursorThrowsCursorNotFound(isTailable) {
+ coll.getMongo().forceReadMode("legacy");
+ setupCollection(isTailable);
+
+ // Create a cursor and consume the docs in the first batch.
+ let cursor = coll.find().batchSize(batchSize);
+ if (isTailable) {
+ cursor = cursor.tailable();
+ }
+ for (let i = 0; i < batchSize; i++) {
+ cursor.next();
+ }
+
+ // Kill the cursor and assert that the cursor throws CursorNotFound on the first next() call.
+ // Use killCursors instead of cursor.close() since we still want to send getMore requests
+ // through the existing cursor.
+ assert.commandWorked(
+ testDB.runCommand({killCursors: coll.getName(), cursors: [cursor.getId()]}));
+ const error = assert.throws(() => cursor.next());
+ assert.eq(error.code, ErrorCodes.CursorNotFound);
+
+ // Check the state of the cursor.
+ assert(!cursor.hasNext());
+ assert.eq(0, cursor.getId());
+ assert.throws(() => cursor.next());
+}
+
+testLegacyCursorThrowsCursorNotFound(false);
+if (!jsTest.options().mixedBinVersions) {
+ testLegacyCursorThrowsCursorNotFound(true);
+}
+}());
diff --git a/src/mongo/client/dbclient_cursor.cpp b/src/mongo/client/dbclient_cursor.cpp
index 7af7d314769..77d5c802eb7 100644
--- a/src/mongo/client/dbclient_cursor.cpp
+++ b/src/mongo/client/dbclient_cursor.cpp
@@ -359,13 +359,11 @@ void DBClientCursor::dataReceived(const Message& reply, bool& retry, string& hos
// cursor id no longer valid at the server.
invariant(qr.getCursorId() == 0);
- if (!(opts & QueryOption_CursorTailable)) {
- uasserted(ErrorCodes::CursorNotFound,
- str::stream() << "cursor id " << cursorId << " didn't exist on server.");
- }
-
- // 0 indicates no longer valid (dead)
+ // 0 indicates no longer valid (dead).
cursorId = 0;
+
+ uasserted(ErrorCodes::CursorNotFound,
+ str::stream() << "cursor id " << cursorId << " didn't exist on server.");
}
if (cursorId == 0 || !(opts & QueryOption_CursorTailable)) {
diff --git a/src/mongo/scripting/mozjs/cursor.cpp b/src/mongo/scripting/mozjs/cursor.cpp
index dc6d65dfb1d..f90bcb924db 100644
--- a/src/mongo/scripting/mozjs/cursor.cpp
+++ b/src/mongo/scripting/mozjs/cursor.cpp
@@ -41,11 +41,12 @@
namespace mongo {
namespace mozjs {
-const JSFunctionSpec CursorInfo::methods[7] = {
+const JSFunctionSpec CursorInfo::methods[8] = {
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(close, CursorInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(hasNext, CursorInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(next, CursorInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(objsLeftInBatch, CursorInfo),
+ MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(getId, CursorInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(readOnly, CursorInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(isClosed, CursorInfo),
JS_FS_END,
@@ -119,6 +120,17 @@ void CursorInfo::Functions::readOnly::call(JSContext* cx, JS::CallArgs args) {
args.rval().set(args.thisv());
}
+void CursorInfo::Functions::getId::call(JSContext* cx, JS::CallArgs args) {
+ auto cursor = getCursor(args);
+
+ if (!cursor) {
+ ValueReader(cx, args.rval()).fromInt64(0);
+ return;
+ }
+
+ ValueReader(cx, args.rval()).fromInt64(cursor->getCursorId());
+}
+
void CursorInfo::Functions::close::call(JSContext* cx, JS::CallArgs args) {
auto cursor = getCursor(args);
diff --git a/src/mongo/scripting/mozjs/cursor.h b/src/mongo/scripting/mozjs/cursor.h
index 110653747e7..6aa610f80b6 100644
--- a/src/mongo/scripting/mozjs/cursor.h
+++ b/src/mongo/scripting/mozjs/cursor.h
@@ -51,9 +51,10 @@ struct CursorInfo : public BaseInfo {
MONGO_DECLARE_JS_FUNCTION(next);
MONGO_DECLARE_JS_FUNCTION(objsLeftInBatch);
MONGO_DECLARE_JS_FUNCTION(readOnly);
+ MONGO_DECLARE_JS_FUNCTION(getId);
};
- static const JSFunctionSpec methods[7];
+ static const JSFunctionSpec methods[8];
static const char* const className;
static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
diff --git a/src/mongo/shell/query.js b/src/mongo/shell/query.js
index bd451166e9d..cbdbeda2929 100644
--- a/src/mongo/shell/query.js
+++ b/src/mongo/shell/query.js
@@ -328,6 +328,11 @@ DBQuery.prototype.readOnly = function() {
return this;
};
+DBQuery.prototype.getId = function() {
+ this._exec();
+ return this._cursor.getId();
+};
+
DBQuery.prototype.toArray = function() {
if (this._arr)
return this._arr;
@@ -876,6 +881,9 @@ DBCommandCursor.prototype.objsLeftInBatch = function() {
return this._cursor.objsLeftInBatch();
}
};
+DBCommandCursor.prototype.getId = function() {
+ return this._cursorid;
+};
DBCommandCursor.prototype.getResumeToken = function() {
// Return the most recent recorded resume token, if such a token exists.
return this._resumeToken;
diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js
index dcfface166e..9ae58f3910c 100644
--- a/src/mongo/shell/utils.js
+++ b/src/mongo/shell/utils.js
@@ -301,6 +301,7 @@ jsTestOptions = function() {
// Note: does not support the array version
mongosBinVersion: TestData.mongosBinVersion || "",
shardMixedBinVersions: TestData.shardMixedBinVersions || false,
+ mixedBinVersions: TestData.mixedBinVersions || false,
networkMessageCompressors: TestData.networkMessageCompressors,
replSetFeatureCompatibilityVersion: TestData.replSetFeatureCompatibilityVersion,
skipRetryOnNetworkError: TestData.skipRetryOnNetworkError,