summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Hirschhorn <max.hirschhorn@mongodb.com>2017-10-14 21:54:02 -0400
committerMax Hirschhorn <max.hirschhorn@mongodb.com>2017-10-14 21:54:02 -0400
commit0ed1b71a50b45b93f0952e1482643c0d9216731d (patch)
tree6ae06f4d49aa2b106dfb08fd48f3e52d397ed341
parent2c5511c8c411c70ac1063d1209764e71f83d0b1a (diff)
downloadmongo-0ed1b71a50b45b93f0952e1482643c0d9216731d.tar.gz
SERVER-31456 Set initial{Cluster,Operation}Time in concurrency suite.
Ensures that the FSM worker threads are guaranteed to observe the effects of the $config.setup() function being called since they'll specify an afterClusterTime beyond that point.
-rw-r--r--jstests/aggregation/bugs/server18198.js9
-rw-r--r--jstests/auth/copyauth.js5
-rw-r--r--jstests/auth/upgrade_noauth_to_keyfile.js5
-rw-r--r--jstests/concurrency/fsm_libs/runner.js23
-rw-r--r--jstests/concurrency/fsm_libs/worker_thread.js15
-rw-r--r--jstests/core/getlog2.js5
-rw-r--r--jstests/noPassthroughWithMongod/create_indexes_shell_helper.js9
-rw-r--r--jstests/noPassthroughWithMongod/default_read_pref.js9
-rw-r--r--jstests/replsets/auth2.js5
-rw-r--r--src/mongo/shell/session.js68
-rw-r--r--src/mongo/shell/utils.js1
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;