diff options
-rw-r--r-- | jstests/aggregation/bugs/server18198.js | 9 | ||||
-rw-r--r-- | jstests/auth/copyauth.js | 5 | ||||
-rw-r--r-- | jstests/auth/upgrade_noauth_to_keyfile.js | 5 | ||||
-rw-r--r-- | jstests/concurrency/fsm_libs/runner.js | 23 | ||||
-rw-r--r-- | jstests/concurrency/fsm_libs/worker_thread.js | 15 | ||||
-rw-r--r-- | jstests/core/getlog2.js | 5 | ||||
-rw-r--r-- | jstests/noPassthroughWithMongod/create_indexes_shell_helper.js | 9 | ||||
-rw-r--r-- | jstests/noPassthroughWithMongod/default_read_pref.js | 9 | ||||
-rw-r--r-- | jstests/replsets/auth2.js | 5 | ||||
-rw-r--r-- | src/mongo/shell/session.js | 68 | ||||
-rw-r--r-- | src/mongo/shell/utils.js | 1 |
11 files changed, 154 insertions, 0 deletions
diff --git a/jstests/aggregation/bugs/server18198.js b/jstests/aggregation/bugs/server18198.js index a5847ad30dd..a182195a864 100644 --- a/jstests/aggregation/bugs/server18198.js +++ b/jstests/aggregation/bugs/server18198.js @@ -30,9 +30,18 @@ getMaxWireVersion: function() { return mongo.getMaxWireVersion(); }, + isReplicaSetMember: function() { + return mongo.isReplicaSetMember(); + }, + isMongos: function() { + return mongo.isMongos(); + }, isCausalConsistency: function() { return false; }, + getClusterTime: function() { + return mongo.getClusterTime(); + }, }; db._mongo = mockMongo; diff --git a/jstests/auth/copyauth.js b/jstests/auth/copyauth.js index d93e726eaa1..0bad123cb98 100644 --- a/jstests/auth/copyauth.js +++ b/jstests/auth/copyauth.js @@ -4,6 +4,11 @@ TestData.authMechanism = "SCRAM-SHA-1"; // SERVER-11428 DB.prototype._defaultAuthenticationMechanism = "SCRAM-SHA-1"; // SERVER-11428 +// We turn off gossiping the mongo shell's clusterTime because this test connects to replica sets +// and sharded clusters as a user other than __system. Attempting to advance the clusterTime while +// it has been signed with a dummy key results in an authorization error. +TestData.skipGossipingClusterTime = true; + var baseName = "jstests_clone_copyauth"; /* diff --git a/jstests/auth/upgrade_noauth_to_keyfile.js b/jstests/auth/upgrade_noauth_to_keyfile.js index da517fef031..080c7217c00 100644 --- a/jstests/auth/upgrade_noauth_to_keyfile.js +++ b/jstests/auth/upgrade_noauth_to_keyfile.js @@ -7,6 +7,11 @@ load('jstests/multiVersion/libs/multi_rs.js'); +// We turn off gossiping the mongo shell's clusterTime because this test connects to replica sets +// and sharded clusters as a user other than __system. Attempting to advance the clusterTime while +// it has been signed with a dummy key results in an authorization error. +TestData.skipGossipingClusterTime = true; + (function() { 'use strict'; var keyFilePath = 'jstests/libs/key1'; diff --git a/jstests/concurrency/fsm_libs/runner.js b/jstests/concurrency/fsm_libs/runner.js index 117d1754419..4997a7bf775 100644 --- a/jstests/concurrency/fsm_libs/runner.js +++ b/jstests/concurrency/fsm_libs/runner.js @@ -537,6 +537,29 @@ var runner = (function() { cleanup.push(workload); }); + // Since the worker threads may be running with causal consistency enabled, we set the + // initial clusterTime and initial operationTime for the sessions they'll create so that + // they are guaranteed to observe the effects of the workload's $config.setup() function + // being called. + if (typeof executionOptions.sessionOptions === 'object' && + executionOptions.sessionOptions !== null) { + // We only start a session for the worker threads and never start one for the main + // thread. We can therefore get the clusterTime and operationTime tracked by the + // underlying DummyDriverSession through any DB instance (i.e. the "test" database + // here was chosen arbitrarily). + const session = cluster.getDB('test').getSession(); + + // JavaScript objects backed by C++ objects (e.g. BSON values from a command + // response) do not serialize correctly when passed through the ScopedThread + // constructor. To work around this behavior, we instead pass a stringified form of + // the JavaScript object through the ScopedThread constructor and use eval() to + // rehydrate it. + executionOptions.sessionOptions.initialClusterTime = + tojson(session.getClusterTime()); + executionOptions.sessionOptions.initialOperationTime = + tojson(session.getOperationTime()); + } + if (cluster.shouldPerformContinuousStepdowns()) { cluster.startContinuousFailover(); } diff --git a/jstests/concurrency/fsm_libs/worker_thread.js b/jstests/concurrency/fsm_libs/worker_thread.js index 44bdb870d0c..d03e7393214 100644 --- a/jstests/concurrency/fsm_libs/worker_thread.js +++ b/jstests/concurrency/fsm_libs/worker_thread.js @@ -38,6 +38,21 @@ var workerThread = (function() { } if (typeof args.sessionOptions !== 'undefined') { + // JavaScript objects backed by C++ objects (e.g. BSON values from a command + // response) do not serialize correctly when passed through the ScopedThread + // constructor. To work around this behavior, we instead pass a stringified form + // of the JavaScript object through the ScopedThread constructor and use eval() + // to rehydrate it. + if (typeof args.sessionOptions.initialClusterTime === 'string') { + args.sessionOptions.initialClusterTime = + eval('(' + args.sessionOptions.initialClusterTime + ')'); + } + + if (typeof args.sessionOptions.initialOperationTime === 'string') { + args.sessionOptions.initialOperationTime = + eval('(' + args.sessionOptions.initialOperationTime + ')'); + } + myDB = new Mongo(args.host) .startSession(args.sessionOptions) .getDatabase(args.dbName); diff --git a/jstests/core/getlog2.js b/jstests/core/getlog2.js index 639a1457f31..ee9d86198c6 100644 --- a/jstests/core/getlog2.js +++ b/jstests/core/getlog2.js @@ -1,5 +1,10 @@ // tests getlog as well as slow querying logging +// We turn off gossiping the mongo shell's clusterTime because it causes the slow command log +// messages to get truncated since they'll exceed 512 characters. The truncated log messages will +// fail to match the find and update patterns defined later on in this test. +TestData.skipGossipingClusterTime = true; + glcol = db.getLogTest2; glcol.drop(); diff --git a/jstests/noPassthroughWithMongod/create_indexes_shell_helper.js b/jstests/noPassthroughWithMongod/create_indexes_shell_helper.js index 01f891b0810..6d9937139ae 100644 --- a/jstests/noPassthroughWithMongod/create_indexes_shell_helper.js +++ b/jstests/noPassthroughWithMongod/create_indexes_shell_helper.js @@ -41,9 +41,18 @@ getMaxWireVersion: function() { return mongo.getMaxWireVersion(); }, + isReplicaSetMember: function() { + return mongo.isReplicaSetMember(); + }, + isMongos: function() { + return mongo.isMongos(); + }, isCausalConsistency: function() { return false; }, + getClusterTime: function() { + return null; + }, }; db._mongo = mockMongo; diff --git a/jstests/noPassthroughWithMongod/default_read_pref.js b/jstests/noPassthroughWithMongod/default_read_pref.js index b5171f559ff..e5daba20d8a 100644 --- a/jstests/noPassthroughWithMongod/default_read_pref.js +++ b/jstests/noPassthroughWithMongod/default_read_pref.js @@ -27,9 +27,18 @@ getMaxWireVersion: function() { return mongo.getMaxWireVersion(); }, + isReplicaSetMember: function() { + return mongo.isReplicaSetMember(); + }, + isMongos: function() { + return mongo.isMongos(); + }, isCausalConsistency: function() { return false; }, + getClusterTime: function() { + return null; + }, }; db._session = new _DummyDriverSession(db._mongo); diff --git a/jstests/replsets/auth2.js b/jstests/replsets/auth2.js index 529dd5257c7..c8d8971a735 100644 --- a/jstests/replsets/auth2.js +++ b/jstests/replsets/auth2.js @@ -3,6 +3,11 @@ // This test requires users to persist across a restart. // @tags: [requires_persistence] +// We turn off gossiping the mongo shell's clusterTime because this test connects to replica sets +// and sharded clusters as a user other than __system. Attempting to advance the clusterTime while +// it has been signed with a dummy key results in an authorization error. +TestData.skipGossipingClusterTime = true; + (function() { var testInvalidAuthStates = function(replSetTest, expectedState) { print("check that 0 is in recovering"); diff --git a/src/mongo/shell/session.js b/src/mongo/shell/session.js index 9b55a26051c..ae49db63f72 100644 --- a/src/mongo/shell/session.js +++ b/src/mongo/shell/session.js @@ -140,6 +140,26 @@ var { return true; } + function gossipClusterTime(cmdObj, clusterTime) { + cmdObj = Object.assign({}, cmdObj); + + const cmdName = Object.keys(cmdObj)[0]; + + // If the command is in a wrapped form, then we look for the actual command object + // inside the query/$query object. + let cmdObjUnwrapped = cmdObj; + if (cmdName === "query" || cmdName === "$query") { + cmdObj[cmdName] = Object.assign({}, cmdObj[cmdName]); + cmdObjUnwrapped = cmdObj[cmdName]; + } + + if (!cmdObjUnwrapped.hasOwnProperty("$clusterTime")) { + cmdObjUnwrapped.$clusterTime = clusterTime; + } + + return cmdObj; + } + function injectAfterClusterTime(cmdObj, operationTime) { cmdObj = Object.assign({}, cmdObj); @@ -165,12 +185,52 @@ var { return cmdObj; } + function isNonNullObject(obj) { + return typeof obj === "object" && obj !== null; + } + function prepareCommandRequest(driverSession, cmdObj) { if (serverSupports(kWireVersionSupportingLogicalSession)) { cmdObj = driverSession._serverSession.injectSessionId(cmdObj); } if (serverSupports(kWireVersionSupportingCausalConsistency) && + (client.isReplicaSetMember() || client.isMongos()) && + !jsTest.options().skipGossipingClusterTime) { + // The `clientClusterTime` is the highest clusterTime observed by any connection + // within this mongo shell. + const clientClusterTime = client.getClusterTime(); + // The `sessionClusterTime` is the highest clusterTime tracked by the + // `driverSession` session and may lag behind `clientClusterTime` if operations on + // other sessions or connections are advancing the clusterTime. + const sessionClusterTime = driverSession.getClusterTime(); + + // We gossip the greater of the client's clusterTime and the session's clusterTime. + // If this is the first command being sent on this connection and/or session, then + // it's possible that either clusterTime hasn't been initialized yet. Additionally, + // if the user specified a malformed clusterTime as part of initialClusterTime, then + // we want the server to be the one to reject it and therefore write our comparisons + // using bsonWoCompare() accordingly. + if (isNonNullObject(clientClusterTime) || isNonNullObject(sessionClusterTime)) { + let clusterTimeToGossip; + + if (!isNonNullObject(sessionClusterTime)) { + clusterTimeToGossip = clientClusterTime; + } else if (!isNonNullObject(clientClusterTime)) { + clusterTimeToGossip = sessionClusterTime; + } else { + clusterTimeToGossip = + (bsonWoCompare({_: clientClusterTime.clusterTime}, + {_: sessionClusterTime.clusterTime}) >= 0) + ? clientClusterTime + : sessionClusterTime; + } + + cmdObj = gossipClusterTime(cmdObj, clusterTimeToGossip); + } + } + + if (serverSupports(kWireVersionSupportingCausalConsistency) && (driverSession.getOptions().isCausalConsistency() || client.isCausalConsistency()) && canUseReadConcern(cmdObj)) { @@ -493,6 +553,14 @@ var { return originalSession.getOptions(); }; + this.getOperationTime = function getOperationTime() { + return this._operationTime; + }; + + this.getClusterTime = function getClusterTime() { + return originalSession.getClusterTime(); + }; + this.getDatabase = function(dbName) { const db = client.getDB(dbName); db._session = this; diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js index 7bdc79411fc..fdabcc1554f 100644 --- a/src/mongo/shell/utils.js +++ b/src/mongo/shell/utils.js @@ -258,6 +258,7 @@ jsTestOptions = function() { jsonSchemaTestFile: TestData.jsonSchemaTestFile, excludedDBsFromDBHash: TestData.excludedDBsFromDBHash, alwaysInjectTransactionNumber: TestData.alwaysInjectTransactionNumber, + skipGossipingClusterTime: TestData.skipGossipingClusterTime || false, }); } return _jsTestOptions; |