summaryrefslogtreecommitdiff
path: root/src/mongo/shell/shardingtest.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/shell/shardingtest.js')
-rw-r--r--src/mongo/shell/shardingtest.js977
1 files changed, 488 insertions, 489 deletions
diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js
index 60f6e83fac7..e8796a9a222 100644
--- a/src/mongo/shell/shardingtest.js
+++ b/src/mongo/shell/shardingtest.js
@@ -10,6 +10,7 @@
*
* {
* name {string}: name for this test
+ * shouldFailInit {boolean}: if set, assert that this will fail initialization
* verbose {number}: the verbosity for the mongos
* chunkSize {number}: the chunk size to use as configuration for the cluster
*
@@ -404,6 +405,24 @@ var ShardingTest = function(params) {
(timeMillis / 1000) + " seconds ***");
};
+ this.stopOnFail = function() {
+ try {
+ this.stopAllMongos();
+ } catch (e) {
+ print("Did not successfully stop all mongos.");
+ }
+ try {
+ this.stopAllShards();
+ } catch (e) {
+ print("Did not successfully stop all shards.");
+ }
+ try {
+ this.stopAllConfigServers();
+ } catch (e) {
+ print("Did not successfully stop all config servers.");
+ }
+ };
+
this.adminCommand = function(cmd) {
var res = this.admin.runCommand(cmd);
if (res && res.ok == 1)
@@ -575,53 +594,12 @@ var ShardingTest = function(params) {
};
/**
- * Waits up to the specified timeout (with a default of 60s) for the balancer to execute one
- * round. If no round has been executed, throws an error.
- *
- * The mongosConnection parameter is optional and allows callers to specify a connection
- * different than the first mongos instance in the list.
- */
- this.awaitBalancerRound = function(timeoutMs, mongosConnection) {
- timeoutMs = timeoutMs || 60000;
- mongosConnection = mongosConnection || self.s0;
-
- // Get the balancer section from the server status of the config server primary
- function getBalancerStatus() {
- var balancerStatus =
- assert.commandWorked(mongosConnection.adminCommand({balancerStatus: 1}));
- if (balancerStatus.mode !== 'full') {
- throw Error('Balancer is not enabled');
- }
-
- return balancerStatus;
- }
-
- var initialStatus = getBalancerStatus();
- var currentStatus;
- assert.soon(
- function() {
- currentStatus = getBalancerStatus();
- return (currentStatus.numBalancerRounds - initialStatus.numBalancerRounds) != 0;
- },
- function() {
- return 'Latest balancer status: ' + tojson(currentStatus);
- },
- timeoutMs);
- };
-
- /**
- * Waits up to one minute for the difference in chunks between the most loaded shard and
- * least loaded shard to be 0 or 1, indicating that the collection is well balanced. This should
- * only be called after creating a big enough chunk difference to trigger balancing.
- */
- this.awaitBalance = function(collName, dbName, timeToWait) {
- timeToWait = timeToWait || 60000;
-
- assert.soon(function() {
- var x = self.chunkDiff(collName, dbName);
- print("chunk diff: " + x);
- return x < 2;
- }, "no balance happened", timeToWait);
+ * Waits up to the specified timeout (with a default of 60s) for the collection to be
+ * considered well balanced.
+ **/
+ this.awaitBalance = function(collName, dbName, timeToWait, interval) {
+ const coll = this.s.getCollection(dbName + "." + collName);
+ this.awaitCollectionBalance(coll, timeToWait, interval);
};
this.getShard = function(coll, query, includeEmpty) {
@@ -1014,6 +992,13 @@ var ShardingTest = function(params) {
};
/**
+ * Waits for all operations to fully replicate on all shards.
+ */
+ this.awaitReplicationOnShards = function() {
+ this._rs.forEach(replSet => replSet.test.awaitReplication());
+ };
+
+ /**
* Returns if there is a new feature compatibility version for the "latest" version. This must
* be manually changed if and when there is a new feature compatibility version.
*/
@@ -1186,537 +1171,551 @@ var ShardingTest = function(params) {
randomSeedAlreadySet = true;
}
- //
- // Start each shard replica set.
- //
- let startTime = new Date(); // Measure the execution time of startup and initiate.
- for (var i = 0; i < numShards; i++) {
- var setName = testName + "-rs" + i;
-
- var rsDefaults = {
- useHostname: otherParams.useHostname,
- oplogSize: 16,
- shardsvr: '',
- pathOpts: Object.merge(pathOpts, {shard: i}),
- };
+ try {
+ //
+ // Start each shard replica set.
+ //
+ let startTime = new Date(); // Measure the execution time of startup and initiate.
+ for (var i = 0; i < numShards; i++) {
+ var setName = testName + "-rs" + i;
+
+ var rsDefaults = {
+ useHostname: otherParams.useHostname,
+ oplogSize: 16,
+ shardsvr: '',
+ pathOpts: Object.merge(pathOpts, {shard: i}),
+ };
- if (otherParams.rs || otherParams["rs" + i]) {
- if (otherParams.rs) {
- rsDefaults = Object.merge(rsDefaults, otherParams.rs);
- }
- if (otherParams["rs" + i]) {
- rsDefaults = Object.merge(rsDefaults, otherParams["rs" + i]);
- }
- rsDefaults = Object.merge(rsDefaults, otherParams.rsOptions);
- rsDefaults.nodes = rsDefaults.nodes || otherParams.numReplicas;
- } else {
- if (jsTestOptions().shardMixedBinVersions) {
- if (!otherParams.shardOptions) {
- otherParams.shardOptions = {};
+ if (otherParams.rs || otherParams["rs" + i]) {
+ if (otherParams.rs) {
+ rsDefaults = Object.merge(rsDefaults, otherParams.rs);
}
- // If the test doesn't depend on specific shard binVersions, create a mixed
- // version
- // shard cluster that randomly assigns shard binVersions, half "latest" and half
- // "last-continuous" or "last-lts".
- // shardMixedBinVersions.
- if (!otherParams.shardOptions.binVersion) {
- Random.setRandomSeed();
- otherParams.shardOptions.binVersion = MongoRunner.versionIterator(
- ["latest", jsTestOptions().shardMixedBinVersions], true);
+ if (otherParams["rs" + i]) {
+ rsDefaults = Object.merge(rsDefaults, otherParams["rs" + i]);
+ }
+ rsDefaults = Object.merge(rsDefaults, otherParams.rsOptions);
+ rsDefaults.nodes = rsDefaults.nodes || otherParams.numReplicas;
+ } else {
+ if (jsTestOptions().shardMixedBinVersions) {
+ if (!otherParams.shardOptions) {
+ otherParams.shardOptions = {};
+ }
+ // If the test doesn't depend on specific shard binVersions, create a mixed
+ // version
+ // shard cluster that randomly assigns shard binVersions, half "latest" and half
+ // "last-continuous" or "last-lts".
+ // shardMixedBinVersions.
+ if (!otherParams.shardOptions.binVersion) {
+ Random.setRandomSeed();
+ otherParams.shardOptions.binVersion = MongoRunner.versionIterator(
+ ["latest", jsTestOptions().shardMixedBinVersions], true);
+ }
}
- }
- if (otherParams.shardOptions && otherParams.shardOptions.binVersion) {
- otherParams.shardOptions.binVersion =
- MongoRunner.versionIterator(otherParams.shardOptions.binVersion);
+ if (otherParams.shardOptions && otherParams.shardOptions.binVersion) {
+ otherParams.shardOptions.binVersion =
+ MongoRunner.versionIterator(otherParams.shardOptions.binVersion);
+ }
+
+ rsDefaults = Object.merge(rsDefaults, otherParams["d" + i]);
+ rsDefaults = Object.merge(rsDefaults, otherParams.shardOptions);
}
- rsDefaults = Object.merge(rsDefaults, otherParams["d" + i]);
- rsDefaults = Object.merge(rsDefaults, otherParams.shardOptions);
- }
+ rsDefaults.setParameter = rsDefaults.setParameter || {};
+ rsDefaults.setParameter.migrationLockAcquisitionMaxWaitMS =
+ otherParams.migrationLockAcquisitionMaxWaitMS;
- rsDefaults.setParameter = rsDefaults.setParameter || {};
- rsDefaults.setParameter.migrationLockAcquisitionMaxWaitMS =
- otherParams.migrationLockAcquisitionMaxWaitMS;
+ var rsSettings = rsDefaults.settings;
+ delete rsDefaults.settings;
+
+ // The number of nodes in the rs field will take priority.
+ if (otherParams.rs || otherParams["rs" + i]) {
+ var numReplicas = rsDefaults.nodes || 3;
+ } else {
+ var numReplicas = 1;
+ }
+ delete rsDefaults.nodes;
+
+ var protocolVersion = rsDefaults.protocolVersion;
+ delete rsDefaults.protocolVersion;
+
+ var rs = new ReplSetTest({
+ name: setName,
+ nodes: numReplicas,
+ host: hostName,
+ useHostName: otherParams.useHostname,
+ useBridge: otherParams.useBridge,
+ bridgeOptions: otherParams.bridgeOptions,
+ keyFile: this.keyFile,
+ protocolVersion: protocolVersion,
+ waitForKeys: false,
+ settings: rsSettings,
+ seedRandomNumberGenerator: !randomSeedAlreadySet,
+ });
- var rsSettings = rsDefaults.settings;
- delete rsDefaults.settings;
+ print("ShardingTest starting replica set for shard: " + setName);
- // The number of nodes in the rs field will take priority.
- if (otherParams.rs || otherParams["rs" + i]) {
- var numReplicas = rsDefaults.nodes || 3;
- } else {
- var numReplicas = 1;
+ // Start up the replica set but don't wait for it to complete. This allows the startup
+ // of each shard to proceed in parallel.
+ this._rs[i] =
+ {setName: setName, test: rs, nodes: rs.startSetAsync(rsDefaults), url: rs.getURL()};
}
- delete rsDefaults.nodes;
- var protocolVersion = rsDefaults.protocolVersion;
- delete rsDefaults.protocolVersion;
+ //
+ // Start up the config server replica set.
+ //
- var rs = new ReplSetTest({
- name: setName,
- nodes: numReplicas,
- host: hostName,
+ var rstOptions = {
useHostName: otherParams.useHostname,
+ host: hostName,
useBridge: otherParams.useBridge,
bridgeOptions: otherParams.bridgeOptions,
keyFile: this.keyFile,
- protocolVersion: protocolVersion,
waitForKeys: false,
- settings: rsSettings,
+ name: testName + "-configRS",
seedRandomNumberGenerator: !randomSeedAlreadySet,
- });
+ isConfigServer: true,
+ };
- print("ShardingTest starting replica set for shard: " + setName);
+ // always use wiredTiger as the storage engine for CSRS
+ var startOptions = {
+ pathOpts: pathOpts,
+ // Ensure that journaling is always enabled for config servers.
+ journal: "",
+ configsvr: "",
+ storageEngine: "wiredTiger",
+ };
- // Start up the replica set but don't wait for it to complete. This allows the startup
- // of each shard to proceed in parallel.
- this._rs[i] =
- {setName: setName, test: rs, nodes: rs.startSetAsync(rsDefaults), url: rs.getURL()};
- }
+ if (otherParams.configOptions && otherParams.configOptions.binVersion) {
+ otherParams.configOptions.binVersion =
+ MongoRunner.versionIterator(otherParams.configOptions.binVersion);
+ }
- //
- // Start up the config server replica set.
- //
-
- var rstOptions = {
- useHostName: otherParams.useHostname,
- host: hostName,
- useBridge: otherParams.useBridge,
- bridgeOptions: otherParams.bridgeOptions,
- keyFile: this.keyFile,
- waitForKeys: false,
- name: testName + "-configRS",
- seedRandomNumberGenerator: !randomSeedAlreadySet,
- isConfigServer: true,
- };
+ startOptions = Object.merge(startOptions, otherParams.configOptions);
+ rstOptions = Object.merge(rstOptions, otherParams.configReplSetTestOptions);
- // always use wiredTiger as the storage engine for CSRS
- var startOptions = {
- pathOpts: pathOpts,
- // Ensure that journaling is always enabled for config servers.
- journal: "",
- configsvr: "",
- storageEngine: "wiredTiger",
- };
+ var nodeOptions = [];
+ for (var i = 0; i < numConfigs; ++i) {
+ nodeOptions.push(otherParams["c" + i] || {});
+ }
- if (otherParams.configOptions && otherParams.configOptions.binVersion) {
- otherParams.configOptions.binVersion =
- MongoRunner.versionIterator(otherParams.configOptions.binVersion);
- }
+ rstOptions.nodes = nodeOptions;
- startOptions = Object.merge(startOptions, otherParams.configOptions);
- rstOptions = Object.merge(rstOptions, otherParams.configReplSetTestOptions);
+ // Start the config server's replica set without waiting for it to complete. This allows it
+ // to proceed in parallel with the startup of each shard.
+ this.configRS = new ReplSetTest(rstOptions);
+ this.configRS.startSetAsync(startOptions);
- var nodeOptions = [];
- for (var i = 0; i < numConfigs; ++i) {
- nodeOptions.push(otherParams["c" + i] || {});
- }
+ //
+ // Wait for each shard replica set to finish starting up.
+ //
+ for (let i = 0; i < numShards; i++) {
+ print("Waiting for shard " + this._rs[i].setName + " to finish starting up.");
+ this._rs[i].test.startSetAwait();
+ }
+
+ //
+ // Wait for the config server to finish starting up.
+ //
+ print("Waiting for the config server to finish starting up.");
+ this.configRS.startSetAwait();
+ var config = this.configRS.getReplSetConfig();
+ config.configsvr = true;
+ config.settings = config.settings || {};
+
+ print("ShardingTest startup for all nodes took " + (new Date() - startTime) + "ms with " +
+ this.configRS.nodeList().length + " config server nodes and " + totalNumShardNodes() +
+ " total shard nodes.");
+
+ //
+ // Initiate each shard replica set and wait for replication. Also initiate the config
+ // replica set. Whenever possible, in parallel.
+ //
+ const shardsRS = this._rs.map(obj => obj.test);
+ const replicaSetsToInitiate = [...shardsRS, this.configRS].map(rst => {
+ const rstConfig = rst.getReplSetConfig();
+
+ // The mongo shell cannot authenticate as the internal __system user in tests that use
+ // x509 for cluster authentication. Choosing the default value for
+ // wcMajorityJournalDefault in ReplSetTest cannot be done automatically without the
+ // shell performing such authentication, so allow tests to pass the value in.
+ if (otherParams.hasOwnProperty("writeConcernMajorityJournalDefault")) {
+ rstConfig.writeConcernMajorityJournalDefault =
+ otherParams.writeConcernMajorityJournalDefault;
+ }
- rstOptions.nodes = nodeOptions;
+ if (rst === this.configRS) {
+ rstConfig.configsvr = true;
+ rstConfig.writeConcernMajorityJournalDefault = true;
+ }
- // Start the config server's replica set without waiting for it to complete. This allows it
- // to proceed in parallel with the startup of each shard.
- this.configRS = new ReplSetTest(rstOptions);
- this.configRS.startSetAsync(startOptions);
+ return {
+ rst,
+ // Arguments for creating instances of each replica set within parallel threads.
+ rstArgs: {
+ name: rst.name,
+ nodeHosts: rst.nodes.map(node => `127.0.0.1:${node.port}`),
+ nodeOptions: rst.nodeOptions,
+ // Mixed-mode SSL tests may specify a keyFile per replica set rather than one
+ // for the whole cluster.
+ keyFile: rst.keyFile ? rst.keyFile : this.keyFile,
+ host: otherParams.useHostname ? hostName : "localhost",
+ waitForKeys: false,
+ },
+ // Replica set configuration for initiating the replica set.
+ rstConfig,
+ };
+ });
- //
- // Wait for each shard replica set to finish starting up.
- //
- for (let i = 0; i < numShards; i++) {
- print("Waiting for shard " + this._rs[i].setName + " to finish starting up.");
- this._rs[i].test.startSetAwait();
- }
+ const initiateReplicaSet = (rst, rstConfig) => {
+ rst.initiateWithAnyNodeAsPrimary(rstConfig);
- //
- // Wait for the config server to finish starting up.
- //
- print("Waiting for the config server to finish starting up.");
- this.configRS.startSetAwait();
- var config = this.configRS.getReplSetConfig();
- config.configsvr = true;
- config.settings = config.settings || {};
-
- print("ShardingTest startup for all nodes took " + (new Date() - startTime) + "ms with " +
- this.configRS.nodeList().length + " config server nodes and " + totalNumShardNodes() +
- " total shard nodes.");
-
- //
- // Initiate each shard replica set and wait for replication. Also initiate the config replica
- // set. Whenever possible, in parallel.
- //
- const shardsRS = this._rs.map(obj => obj.test);
- const replicaSetsToInitiate = [...shardsRS, this.configRS].map(rst => {
- const rstConfig = rst.getReplSetConfig();
-
- // The mongo shell cannot authenticate as the internal __system user in tests that use x509
- // for cluster authentication. Choosing the default value for wcMajorityJournalDefault in
- // ReplSetTest cannot be done automatically without the shell performing such
- // authentication, so allow tests to pass the value in.
- if (otherParams.hasOwnProperty("writeConcernMajorityJournalDefault")) {
- rstConfig.writeConcernMajorityJournalDefault =
- otherParams.writeConcernMajorityJournalDefault;
- }
-
- if (rst === this.configRS) {
- rstConfig.configsvr = true;
- rstConfig.writeConcernMajorityJournalDefault = true;
- }
-
- return {
- rst,
- // Arguments for creating instances of each replica set within parallel threads.
- rstArgs: {
- name: rst.name,
- nodeHosts: rst.nodes.map(node => `127.0.0.1:${node.port}`),
- nodeOptions: rst.nodeOptions,
- // Mixed-mode SSL tests may specify a keyFile per replica set rather than one for
- // the whole cluster.
- keyFile: rst.keyFile ? rst.keyFile : this.keyFile,
- host: otherParams.useHostname ? hostName : "localhost",
- waitForKeys: false,
- },
- // Replica set configuration for initiating the replica set.
- rstConfig,
+ // Do replication.
+ rst.awaitNodesAgreeOnPrimary();
+ rst.getPrimary().getDB("admin").foo.save({x: 1});
+ if (rst.keyFile) {
+ authutil.asCluster(rst.nodes, rst.keyFile, function() {
+ rst.awaitReplication();
+ });
+ }
+ rst.awaitSecondaryNodes();
};
- });
-
- const initiateReplicaSet = (rst, rstConfig) => {
- rst.initiateWithAnyNodeAsPrimary(rstConfig);
- // Do replication.
- rst.awaitNodesAgreeOnPrimary();
- rst.getPrimary().getDB("admin").foo.save({x: 1});
- if (rst.keyFile) {
- authutil.asCluster(rst.nodes, rst.keyFile, function() {
- rst.awaitReplication();
- });
- }
- rst.awaitSecondaryNodes();
- };
-
- const isParallelSupported = (() => {
- if (!tryLoadParallelTester()) {
- return false;
- }
-
- for (let {rst} of replicaSetsToInitiate) {
- if (rst.startOptions && rst.startOptions.clusterAuthMode === "x509") {
- // The mongo shell performing X.509 authentication as a cluster member requires
- // starting a parallel shell and using the server's (not the client's) certificate.
- // The ReplSetTest instance constructed in a Thread wouldn't have copied the path to
- // the server's certificate. We therefore fall back to initiating the CSRS and
- // replica set shards sequentially when X.509 authentication is being used.
+ const isParallelSupported = (() => {
+ if (!tryLoadParallelTester()) {
return false;
}
- for (let n of Object.keys(rst.nodeOptions)) {
- const nodeOptions = rst.nodeOptions[n];
- if (nodeOptions && nodeOptions.clusterAuthMode === "x509") {
+ for (let {rst} of replicaSetsToInitiate) {
+ if (rst.startOptions && rst.startOptions.clusterAuthMode === "x509") {
+ // The mongo shell performing X.509 authentication as a cluster member requires
+ // starting a parallel shell and using the server's (not the client's)
+ // certificate. The ReplSetTest instance constructed in a Thread wouldn't have
+ // copied the path to the server's certificate. We therefore fall back to
+ // initiating the CSRS and replica set shards sequentially when X.509
+ // authentication is being used.
return false;
}
- }
- }
-
- return true;
- })();
- if (isParallelSupported) {
- const threads = [];
- try {
- for (let {rstArgs, rstConfig} of replicaSetsToInitiate) {
- const thread = new Thread((rstArgs, rstConfig, initiateReplicaSet) => {
- try {
- const rst = new ReplSetTest({rstArgs});
- initiateReplicaSet(rst, rstConfig);
- return {ok: 1};
- } catch (e) {
- return {
- ok: 0,
- hosts: rstArgs.nodeHosts,
- name: rstArgs.name,
- error: e.toString(),
- stack: e.stack,
- };
+ for (let n of Object.keys(rst.nodeOptions)) {
+ const nodeOptions = rst.nodeOptions[n];
+ if (nodeOptions && nodeOptions.clusterAuthMode === "x509") {
+ return false;
}
- }, rstArgs, rstConfig, initiateReplicaSet);
- thread.start();
- threads.push(thread);
+ }
}
- } finally {
- // Wait for each thread to finish. Throw an error if any thread fails.
- const returnData = threads.map(thread => {
- thread.join();
- return thread.returnData();
- });
- returnData.forEach(res => {
- assert.commandWorked(res,
- 'Initiating shard or config servers as a replica set failed');
- });
- }
- } else {
- for (let {rst, rstConfig} of replicaSetsToInitiate) {
- initiateReplicaSet(rst, rstConfig);
+ return true;
+ })();
+
+ if (isParallelSupported) {
+ const threads = [];
+ try {
+ for (let {rstArgs, rstConfig} of replicaSetsToInitiate) {
+ const thread = new Thread((rstArgs, rstConfig, initiateReplicaSet) => {
+ try {
+ const rst = new ReplSetTest({rstArgs});
+ initiateReplicaSet(rst, rstConfig);
+ return {ok: 1};
+ } catch (e) {
+ return {
+ ok: 0,
+ hosts: rstArgs.nodeHosts,
+ name: rstArgs.name,
+ error: e.toString(),
+ stack: e.stack,
+ };
+ }
+ }, rstArgs, rstConfig, initiateReplicaSet);
+ thread.start();
+ threads.push(thread);
+ }
+ } finally {
+ // Wait for each thread to finish. Throw an error if any thread fails.
+ const returnData = threads.map(thread => {
+ thread.join();
+ return thread.returnData();
+ });
+
+ returnData.forEach(res => {
+ assert.commandWorked(
+ res, 'Initiating shard or config servers as a replica set failed');
+ });
+ }
+ } else {
+ for (let {rst, rstConfig} of replicaSetsToInitiate) {
+ initiateReplicaSet(rst, rstConfig);
+ }
}
- }
- for (let i = 0; i < numShards; i++) {
- let rs = this._rs[i].test;
+ for (let i = 0; i < numShards; i++) {
+ let rs = this._rs[i].test;
- this["rs" + i] = rs;
- this._rsObjects[i] = rs;
+ this["rs" + i] = rs;
+ this._rsObjects[i] = rs;
- this._connections.push(null);
+ this._connections.push(null);
- let rsConn = new Mongo(rs.getURL());
- rsConn.name = rs.getURL();
+ let rsConn = new Mongo(rs.getURL());
+ rsConn.name = rs.getURL();
- this._connections[i] = rsConn;
- this["shard" + i] = rsConn;
- rsConn.rs = rs;
- }
+ this._connections[i] = rsConn;
+ this["shard" + i] = rsConn;
+ rsConn.rs = rs;
+ }
- // Wait for master to be elected before starting mongos
- this.configRS.awaitNodesAgreeOnPrimary();
- var csrsPrimary = this.configRS.getPrimary();
-
- print("ShardingTest startup and initiation for all nodes took " + (new Date() - startTime) +
- "ms with " + this.configRS.nodeList().length + " config server nodes and " +
- totalNumShardNodes() + " total shard nodes.");
-
- // If 'otherParams.mongosOptions.binVersion' is an array value, then we'll end up constructing a
- // version iterator.
- const mongosOptions = [];
- for (var i = 0; i < numMongos; ++i) {
- let options = {
- useHostname: otherParams.useHostname,
- pathOpts: Object.merge(pathOpts, {mongos: i}),
- verbose: mongosVerboseLevel,
- keyFile: this.keyFile,
- };
+ // Wait for master to be elected before starting mongos
+ this.configRS.awaitNodesAgreeOnPrimary();
+ var csrsPrimary = this.configRS.getPrimary();
- if (otherParams.mongosOptions && otherParams.mongosOptions.binVersion) {
- otherParams.mongosOptions.binVersion =
- MongoRunner.versionIterator(otherParams.mongosOptions.binVersion);
- }
+ print("ShardingTest startup and initiation for all nodes took " + (new Date() - startTime) +
+ "ms with " + this.configRS.nodeList().length + " config server nodes and " +
+ totalNumShardNodes() + " total shard nodes.");
- options = Object.merge(options, otherParams.mongosOptions);
- options = Object.merge(options, otherParams["s" + i]);
+ // If 'otherParams.mongosOptions.binVersion' is an array value, then we'll end up
+ // constructing a version iterator.
+ const mongosOptions = [];
+ for (var i = 0; i < numMongos; ++i) {
+ let options = {
+ useHostname: otherParams.useHostname,
+ pathOpts: Object.merge(pathOpts, {mongos: i}),
+ verbose: mongosVerboseLevel,
+ keyFile: this.keyFile,
+ };
- // The default time for mongos quiesce mode in response to SIGTERM is 15 seconds.
- // Reduce this to 0 for faster shutdown.
- options.setParameter = options.setParameter || {};
- options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown =
- options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown || 0;
+ if (otherParams.mongosOptions && otherParams.mongosOptions.binVersion) {
+ otherParams.mongosOptions.binVersion =
+ MongoRunner.versionIterator(otherParams.mongosOptions.binVersion);
+ }
- options.port = options.port || _allocatePortForMongos();
+ options = Object.merge(options, otherParams.mongosOptions);
+ options = Object.merge(options, otherParams["s" + i]);
- mongosOptions.push(options);
- }
+ // The default time for mongos quiesce mode in response to SIGTERM is 15 seconds.
+ // Reduce this to 0 for faster shutdown.
+ options.setParameter = options.setParameter || {};
+ options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown =
+ options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown || 0;
- const configRS = this.configRS;
- const clusterVersionInfo = this.getClusterVersionInfo();
- if (_hasNewFeatureCompatibilityVersion() && clusterVersionInfo.isMixedVersion) {
- const fcv = binVersionToFCV(clusterVersionInfo.oldestBinVersion);
- function setFeatureCompatibilityVersion() {
- assert.commandWorked(csrsPrimary.adminCommand(
- {setFeatureCompatibilityVersion: fcv, fromConfigServer: true}));
+ options.port = options.port || _allocatePortForMongos();
- // Wait for the new featureCompatibilityVersion to propagate to all nodes in the CSRS
- // to ensure that older versions of mongos can successfully connect.
- configRS.awaitReplication();
+ mongosOptions.push(options);
}
- if (this.keyFile) {
- authutil.asCluster(this.configRS.nodes, this.keyFile, setFeatureCompatibilityVersion);
- } else {
- setFeatureCompatibilityVersion();
+ const configRS = this.configRS;
+ const clusterVersionInfo = this.getClusterVersionInfo();
+ if (_hasNewFeatureCompatibilityVersion() && clusterVersionInfo.isMixedVersion) {
+ const fcv = binVersionToFCV(clusterVersionInfo.oldestBinVersion);
+ function setFeatureCompatibilityVersion() {
+ assert.commandWorked(csrsPrimary.adminCommand(
+ {setFeatureCompatibilityVersion: fcv, fromConfigServer: true}));
+
+ // Wait for the new featureCompatibilityVersion to propagate to all nodes in the
+ // CSRS to ensure that older versions of mongos can successfully connect.
+ configRS.awaitReplication();
+ }
+
+ if (this.keyFile) {
+ authutil.asCluster(
+ this.configRS.nodes, this.keyFile, setFeatureCompatibilityVersion);
+ } else {
+ setFeatureCompatibilityVersion();
+ }
}
- }
- // If chunkSize has been requested for this test, write the configuration
- if (otherParams.chunkSize) {
- function setChunkSize() {
- assert.commandWorked(csrsPrimary.getDB('config').settings.update(
- {_id: 'chunksize'},
- {$set: {value: otherParams.chunkSize}},
- {upsert: true, writeConcern: {w: 'majority', wtimeout: kDefaultWTimeoutMs}}));
+ // If chunkSize has been requested for this test, write the configuration
+ if (otherParams.chunkSize) {
+ function setChunkSize() {
+ assert.commandWorked(csrsPrimary.getDB('config').settings.update(
+ {_id: 'chunksize'},
+ {$set: {value: otherParams.chunkSize}},
+ {upsert: true, writeConcern: {w: 'majority', wtimeout: kDefaultWTimeoutMs}}));
+
+ configRS.awaitLastOpCommitted();
+ }
- configRS.awaitLastOpCommitted();
+ if (this.keyFile) {
+ authutil.asCluster(csrsPrimary, this.keyFile, setChunkSize);
+ } else {
+ setChunkSize();
+ }
}
- if (this.keyFile) {
- authutil.asCluster(csrsPrimary, this.keyFile, setChunkSize);
- } else {
- setChunkSize();
+ this._configDB = this.configRS.getURL();
+ for (var i = 0; i < numConfigs; ++i) {
+ var conn = this.configRS.nodes[i];
+ this["config" + i] = conn;
+ this["c" + i] = conn;
}
- }
- this._configDB = this.configRS.getURL();
- for (var i = 0; i < numConfigs; ++i) {
- var conn = this.configRS.nodes[i];
- this["config" + i] = conn;
- this["c" + i] = conn;
- }
+ printjson('Config servers: ' + this._configDB);
- printjson('Config servers: ' + this._configDB);
+ print("ShardingTest " + this._testName + " :\n" +
+ tojson({config: this._configDB, shards: this._connections}));
- print("ShardingTest " + this._testName + " :\n" +
- tojson({config: this._configDB, shards: this._connections}));
+ this._mongos = [];
- this._mongos = [];
+ // Start the MongoS servers
+ for (var i = 0; i < numMongos; i++) {
+ const options = mongosOptions[i];
+ options.configdb = this._configDB;
- // Start the MongoS servers
- for (var i = 0; i < numMongos; i++) {
- const options = mongosOptions[i];
- options.configdb = this._configDB;
+ if (otherParams.useBridge) {
+ var bridgeOptions =
+ Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {});
+ bridgeOptions = Object.merge(bridgeOptions, {
+ hostName: otherParams.useHostname ? hostName : "localhost",
+ port: _allocatePortForBridgeForMongos(),
+ // The mongos processes identify themselves to mongobridge as host:port, where
+ // the host is the actual hostname of the machine and not localhost.
+ dest: hostName + ":" + options.port,
+ });
- if (otherParams.useBridge) {
- var bridgeOptions =
- Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {});
- bridgeOptions = Object.merge(bridgeOptions, {
- hostName: otherParams.useHostname ? hostName : "localhost",
- port: _allocatePortForBridgeForMongos(),
- // The mongos processes identify themselves to mongobridge as host:port, where the
- // host is the actual hostname of the machine and not localhost.
- dest: hostName + ":" + options.port,
- });
+ var bridge = new MongoBridge(bridgeOptions);
+ }
- var bridge = new MongoBridge(bridgeOptions);
- }
+ var conn = MongoRunner.runMongos(options);
+ if (!conn) {
+ throw new Error("Failed to start mongos " + i);
+ }
- var conn = MongoRunner.runMongos(options);
- if (!conn) {
- throw new Error("Failed to start mongos " + i);
- }
+ if (otherParams.causallyConsistent) {
+ conn.setCausalConsistency(true);
+ }
- if (otherParams.causallyConsistent) {
- conn.setCausalConsistency(true);
- }
+ if (otherParams.useBridge) {
+ bridge.connectToBridge();
+ this._mongos.push(bridge);
+ unbridgedMongos.push(conn);
+ } else {
+ this._mongos.push(conn);
+ }
- if (otherParams.useBridge) {
- bridge.connectToBridge();
- this._mongos.push(bridge);
- unbridgedMongos.push(conn);
- } else {
- this._mongos.push(conn);
- }
+ if (i === 0) {
+ this.s = this._mongos[i];
+ this.admin = this._mongos[i].getDB('admin');
+ this.config = this._mongos[i].getDB('config');
+ }
- if (i === 0) {
- this.s = this._mongos[i];
- this.admin = this._mongos[i].getDB('admin');
- this.config = this._mongos[i].getDB('config');
+ this["s" + i] = this._mongos[i];
}
- this["s" + i] = this._mongos[i];
- }
+ _extendWithShMethods();
- _extendWithShMethods();
+ // If auth is enabled for the test, login the mongos connections as system in order to
+ // configure the instances and then log them out again.
+ if (this.keyFile) {
+ authutil.asCluster(this._mongos, this.keyFile, _configureCluster);
+ } else if (mongosOptions[0] && mongosOptions[0].keyFile) {
+ authutil.asCluster(this._mongos, mongosOptions[0].keyFile, _configureCluster);
+ } else {
+ _configureCluster();
+ // Ensure that all config server nodes are up to date with any changes made to balancer
+ // settings before adding shards to the cluster. This prevents shards, which read
+ // config.settings with readPreference 'nearest', from accidentally fetching stale
+ // values from secondaries that aren't up-to-date.
+ this.configRS.awaitLastOpCommitted();
+ }
- // If auth is enabled for the test, login the mongos connections as system in order to configure
- // the instances and then log them out again.
- if (this.keyFile) {
- authutil.asCluster(this._mongos, this.keyFile, _configureCluster);
- } else if (mongosOptions[0] && mongosOptions[0].keyFile) {
- authutil.asCluster(this._mongos, mongosOptions[0].keyFile, _configureCluster);
- } else {
- _configureCluster();
- // Ensure that all config server nodes are up to date with any changes made to balancer
- // settings before adding shards to the cluster. This prevents shards, which read
- // config.settings with readPreference 'nearest', from accidentally fetching stale values
- // from secondaries that aren't up-to-date.
- this.configRS.awaitLastOpCommitted();
- }
+ try {
+ if (!otherParams.manualAddShard) {
+ var testName = this._testName;
+ var admin = this.admin;
- try {
- if (!otherParams.manualAddShard) {
- var testName = this._testName;
- var admin = this.admin;
+ this._connections.forEach(function(z) {
+ var n = z.name || z.host || z;
- this._connections.forEach(function(z) {
- var n = z.name || z.host || z;
+ print("ShardingTest " + testName + " going to add shard : " + n);
- print("ShardingTest " + testName + " going to add shard : " + n);
+ var result = assert.commandWorked(admin.runCommand({addshard: n}),
+ "Failed to add shard " + n);
+ z.shardName = result.shardAdded;
+ });
+ }
+ } catch (e) {
+ // Clean up the running procceses on failure
+ print("Failed to add shards, stopping cluster.");
+ this.stop();
+ throw e;
+ }
+
+ // Ensure that the sessions collection exists so jstests can run things with
+ // logical sessions and test them. We do this by forcing an immediate cache refresh
+ // on the config server, which auto-shards the collection for the cluster.
+ this.configRS.getPrimary().getDB("admin").runCommand({refreshLogicalSessionCacheNow: 1});
+
+ // Ensure that all CSRS nodes are up to date. This is strictly needed for tests that use
+ // multiple mongoses. In those cases, the first mongos initializes the contents of the
+ // 'config' database, but without waiting for those writes to replicate to all the config
+ // servers then the secondary mongoses risk reading from a stale config server and seeing an
+ // empty config database.
+ this.configRS.awaitLastOpCommitted();
- var result = assert.commandWorked(admin.runCommand({addshard: n}),
- "Failed to add shard " + n);
- z.shardName = result.shardAdded;
- });
+ if (jsTestOptions().keyFile) {
+ jsTest.authenticateNodes(this._mongos);
}
- } catch (e) {
- // Clean up the running procceses on failure
- print("Failed to add shards, stopping cluster.");
- this.stop();
- throw e;
- }
-
- // Ensure that the sessions collection exists so jstests can run things with
- // logical sessions and test them. We do this by forcing an immediate cache refresh
- // on the config server, which auto-shards the collection for the cluster.
- this.configRS.getPrimary().getDB("admin").runCommand({refreshLogicalSessionCacheNow: 1});
-
- // Ensure that all CSRS nodes are up to date. This is strictly needed for tests that use
- // multiple mongoses. In those cases, the first mongos initializes the contents of the 'config'
- // database, but without waiting for those writes to replicate to all the config servers then
- // the secondary mongoses risk reading from a stale config server and seeing an empty config
- // database.
- this.configRS.awaitLastOpCommitted();
- if (jsTestOptions().keyFile) {
- jsTest.authenticateNodes(this._mongos);
- }
+ // Flushes the routing table cache on connection 'conn'. If 'keyFileLocal' is defined,
+ // authenticates the keyfile user.
+ const flushRT = function flushRoutingTableAndHandleAuth(conn, keyFileLocal) {
+ // Invokes the actual execution of cache refresh.
+ const execFlushRT = (conn) => {
+ assert.commandWorked(conn.getDB("admin").runCommand(
+ {_flushRoutingTableCacheUpdates: "config.system.sessions"}));
+ };
- // Flushes the routing table cache on connection 'conn'. If 'keyFileLocal' is defined,
- // authenticates the keyfile user.
- const flushRT = function flushRoutingTableAndHandleAuth(conn, keyFileLocal) {
- // Invokes the actual execution of cache refresh.
- const execFlushRT = (conn) => {
- assert.commandWorked(conn.getDB("admin").runCommand(
- {_flushRoutingTableCacheUpdates: "config.system.sessions"}));
+ const x509AuthRequired = (conn.fullOptions && conn.fullOptions.clusterAuthMode &&
+ conn.fullOptions.clusterAuthMode === "x509");
+
+ if (keyFileLocal) {
+ authutil.asCluster(conn, keyFileLocal, () => execFlushRT(conn));
+ } else if (x509AuthRequired) {
+ const exitCode =
+ _runMongoProgram(...["mongo",
+ conn.host,
+ "--tls",
+ "--tlsAllowInvalidHostnames",
+ "--tlsCertificateKeyFile",
+ conn.fullOptions.tlsCertificateKeyFile
+ ? conn.fullOptions.tlsCertificateKeyFile
+ : conn.fullOptions.sslPEMKeyFile,
+ "--tlsCAFile",
+ conn.fullOptions.tlsCAFile ? conn.fullOptions.tlsCAFile
+ : conn.fullOptions.sslCAFile,
+ "--authenticationDatabase=$external",
+ "--authenticationMechanism=MONGODB-X509",
+ "--eval",
+ `(${execFlushRT.toString()})(db.getMongo())`,
+ ]);
+ assert.eq(0, exitCode, "parallel shell for x509 auth failed");
+ } else {
+ execFlushRT(conn);
+ }
};
- const x509AuthRequired = (conn.fullOptions && conn.fullOptions.clusterAuthMode &&
- conn.fullOptions.clusterAuthMode === "x509");
-
- if (keyFileLocal) {
- authutil.asCluster(conn, keyFileLocal, () => execFlushRT(conn));
- } else if (x509AuthRequired) {
- const exitCode = _runMongoProgram(
- ...["mongo",
- conn.host,
- "--tls",
- "--tlsAllowInvalidHostnames",
- "--tlsCertificateKeyFile",
- conn.fullOptions.tlsCertificateKeyFile ? conn.fullOptions.tlsCertificateKeyFile
- : conn.fullOptions.sslPEMKeyFile,
- "--tlsCAFile",
- conn.fullOptions.tlsCAFile ? conn.fullOptions.tlsCAFile
- : conn.fullOptions.sslCAFile,
- "--authenticationDatabase=$external",
- "--authenticationMechanism=MONGODB-X509",
- "--eval",
- `(${execFlushRT.toString()})(db.getMongo())`,
- ]);
- assert.eq(0, exitCode, "parallel shell for x509 auth failed");
- } else {
- execFlushRT(conn);
- }
- };
-
- if (!otherParams.manualAddShard) {
- for (let i = 0; i < numShards; i++) {
- const keyFileLocal =
- (otherParams.shards && otherParams.shards[i] && otherParams.shards[i].keyFile)
- ? otherParams.shards[i].keyFile
- : this.keyFile;
+ if (!otherParams.manualAddShard) {
+ for (let i = 0; i < numShards; i++) {
+ const keyFileLocal =
+ (otherParams.shards && otherParams.shards[i] && otherParams.shards[i].keyFile)
+ ? otherParams.shards[i].keyFile
+ : this.keyFile;
+
+ const rs = this._rs[i].test;
+ flushRT(rs.getPrimary(), keyFileLocal);
+ }
- const rs = this._rs[i].test;
- flushRT(rs.getPrimary(), keyFileLocal);
+ self.waitForShardingInitialized();
}
-
- self.waitForShardingInitialized();
+ } catch (e) {
+ // this was expected to fail, so clean up appropriately
+ if (params.shouldFailInit === true) {
+ this.stopOnFail();
+ }
+ throw e;
}
+ // This initialization was expected to fail, but it did not.
+ assert.neq(
+ true, params.shouldFailInit, "This was expected to fail initialization, but it did not");
};
// Stub for a hook to check that collection UUIDs are consistent across shards and the config