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.js994
1 files changed, 491 insertions, 503 deletions
diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js
index 1319746ea1a..60f6e83fac7 100644
--- a/src/mongo/shell/shardingtest.js
+++ b/src/mongo/shell/shardingtest.js
@@ -10,7 +10,6 @@
*
* {
* 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
*
@@ -114,9 +113,6 @@ var ShardingTest = function(params) {
// concern (5 minutes)
var kDefaultWTimeoutMs = 5 * 60 * 1000;
- // Oplog collection name
- const kOplogName = 'oplog.rs';
-
// Ensure we don't mutate the passed-in parameters.
params = Object.extend({}, params, true);
@@ -408,24 +404,6 @@ 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)
@@ -597,12 +575,53 @@ var ShardingTest = function(params) {
};
/**
- * 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);
+ * 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);
};
this.getShard = function(coll, query, includeEmpty) {
@@ -995,24 +1014,6 @@ var ShardingTest = function(params) {
};
/**
- * Waits for all operations to fully replicate on all shards.
- */
- this.awaitReplicationOnShards = function() {
- this._rs.forEach(replSet => replSet.test.awaitReplication());
- };
-
- /**
- * Query the oplog from a given node.
- */
- ShardingTest.prototype.findOplog = function(conn, query, limit) {
- return conn.getDB('local')
- .getCollection(kOplogName)
- .find(query)
- .sort({$natural: -1})
- .limit(limit);
- };
-
- /**
* 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.
*/
@@ -1034,7 +1035,8 @@ var ShardingTest = function(params) {
assert(isObject(params), 'ShardingTest configuration must be a JSON object');
var testName = params.name || jsTest.name();
- var otherParams = Object.deepMerge(params, params.other || {});
+ var otherParams = Object.merge(params, params.other || {});
+
var numShards = otherParams.hasOwnProperty('shards') ? otherParams.shards : 2;
var mongosVerboseLevel = otherParams.hasOwnProperty('verbose') ? otherParams.verbose : 1;
var numMongos = otherParams.hasOwnProperty('mongos') ? otherParams.mongos : 1;
@@ -1184,551 +1186,537 @@ var ShardingTest = function(params) {
randomSeedAlreadySet = true;
}
- 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}),
- };
+ //
+ // 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 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 || 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.shardOptions && otherParams.shardOptions.binVersion) {
- otherParams.shardOptions.binVersion =
- MongoRunner.versionIterator(otherParams.shardOptions.binVersion);
+ // 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);
}
+ }
- rsDefaults = Object.merge(rsDefaults, otherParams["d" + i]);
- rsDefaults = Object.merge(rsDefaults, otherParams.shardOptions);
+ if (otherParams.shardOptions && otherParams.shardOptions.binVersion) {
+ otherParams.shardOptions.binVersion =
+ MongoRunner.versionIterator(otherParams.shardOptions.binVersion);
}
- rsDefaults.setParameter = rsDefaults.setParameter || {};
- rsDefaults.setParameter.migrationLockAcquisitionMaxWaitMS =
- otherParams.migrationLockAcquisitionMaxWaitMS;
+ rsDefaults = Object.merge(rsDefaults, otherParams["d" + i]);
+ rsDefaults = Object.merge(rsDefaults, otherParams.shardOptions);
+ }
- var rsSettings = rsDefaults.settings;
- delete rsDefaults.settings;
+ rsDefaults.setParameter = rsDefaults.setParameter || {};
+ rsDefaults.setParameter.migrationLockAcquisitionMaxWaitMS =
+ otherParams.migrationLockAcquisitionMaxWaitMS;
- // 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);
-
- // 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()};
+ // 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;
- //
- // Start up the config server replica set.
- //
+ var protocolVersion = rsDefaults.protocolVersion;
+ delete rsDefaults.protocolVersion;
- var rstOptions = {
- useHostName: otherParams.useHostname,
+ 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,
- name: testName + "-configRS",
+ settings: rsSettings,
seedRandomNumberGenerator: !randomSeedAlreadySet,
- isConfigServer: true,
- };
+ });
- // 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",
- };
+ print("ShardingTest starting replica set for shard: " + setName);
- if (otherParams.configOptions && otherParams.configOptions.binVersion) {
- otherParams.configOptions.binVersion =
- MongoRunner.versionIterator(otherParams.configOptions.binVersion);
- }
+ // 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()};
+ }
- startOptions = Object.merge(startOptions, otherParams.configOptions);
- rstOptions = Object.merge(rstOptions, otherParams.configReplSetTestOptions);
+ //
+ // 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,
+ };
- var nodeOptions = [];
- for (var i = 0; i < numConfigs; ++i) {
- nodeOptions.push(otherParams["c" + i] || {});
- }
+ // 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",
+ };
- rstOptions.nodes = nodeOptions;
+ if (otherParams.configOptions && otherParams.configOptions.binVersion) {
+ otherParams.configOptions.binVersion =
+ MongoRunner.versionIterator(otherParams.configOptions.binVersion);
+ }
- // 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);
+ startOptions = Object.merge(startOptions, otherParams.configOptions);
+ rstOptions = Object.merge(rstOptions, otherParams.configReplSetTestOptions);
- //
- // 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;
- }
+ var nodeOptions = [];
+ for (var i = 0; i < numConfigs; ++i) {
+ nodeOptions.push(otherParams["c" + i] || {});
+ }
- if (rst === this.configRS) {
- rstConfig.configsvr = true;
- rstConfig.writeConcernMajorityJournalDefault = true;
- }
+ rstOptions.nodes = nodeOptions;
- 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,
- };
- });
+ // 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);
- const initiateReplicaSet = (rst, rstConfig) => {
- rst.initiateWithAnyNodeAsPrimary(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();
+ }
- // 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();
+ //
+ // 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,
};
+ });
- const isParallelSupported = (() => {
- if (!tryLoadParallelTester()) {
- return false;
- }
+ const initiateReplicaSet = (rst, rstConfig) => {
+ rst.initiateWithAnyNodeAsPrimary(rstConfig);
- 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;
- }
+ // 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();
+ };
- for (let n of Object.keys(rst.nodeOptions)) {
- const nodeOptions = rst.nodeOptions[n];
- if (nodeOptions && nodeOptions.clusterAuthMode === "x509") {
- return false;
- }
- }
- }
+ const isParallelSupported = (() => {
+ if (!tryLoadParallelTester()) {
+ return false;
+ }
- return true;
- })();
+ 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;
+ }
- 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);
+ for (let n of Object.keys(rst.nodeOptions)) {
+ const nodeOptions = rst.nodeOptions[n];
+ if (nodeOptions && nodeOptions.clusterAuthMode === "x509") {
+ return false;
}
- } 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;
-
- this["rs" + i] = rs;
- this._rsObjects[i] = rs;
-
- this._connections.push(null);
+ return true;
+ })();
- let rsConn = new Mongo(rs.getURL());
- rsConn.name = rs.getURL();
+ 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();
+ });
- this._connections[i] = rsConn;
- this["shard" + i] = rsConn;
- rsConn.rs = rs;
+ 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);
}
+ }
- // Wait for master to be elected before starting mongos
- this.configRS.awaitNodesAgreeOnPrimary();
- var csrsPrimary = this.configRS.getPrimary();
+ for (let i = 0; i < numShards; i++) {
+ let rs = this._rs[i].test;
- 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.");
+ this["rs" + i] = rs;
+ this._rsObjects[i] = rs;
- // 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,
- };
+ this._connections.push(null);
- if (otherParams.mongosOptions && otherParams.mongosOptions.binVersion) {
- otherParams.mongosOptions.binVersion =
- MongoRunner.versionIterator(otherParams.mongosOptions.binVersion);
- }
+ let rsConn = new Mongo(rs.getURL());
+ rsConn.name = rs.getURL();
- options = Object.merge(options, otherParams.mongosOptions);
- options = Object.merge(options, otherParams["s" + i]);
-
- // 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;
+ this._connections[i] = rsConn;
+ this["shard" + i] = rsConn;
+ rsConn.rs = rs;
+ }
- options.port = options.port || _allocatePortForMongos();
+ // 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,
+ };
- mongosOptions.push(options);
+ if (otherParams.mongosOptions && otherParams.mongosOptions.binVersion) {
+ otherParams.mongosOptions.binVersion =
+ MongoRunner.versionIterator(otherParams.mongosOptions.binVersion);
}
- 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 = Object.merge(options, otherParams.mongosOptions);
+ options = Object.merge(options, otherParams["s" + i]);
- // 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();
- }
+ // 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 (this.keyFile) {
- authutil.asCluster(
- this.configRS.nodes, this.keyFile, setFeatureCompatibilityVersion);
- } else {
- setFeatureCompatibilityVersion();
- }
- }
+ options.port = options.port || _allocatePortForMongos();
- // 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}}));
+ mongosOptions.push(options);
+ }
- configRS.awaitLastOpCommitted();
- }
+ 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}));
- if (this.keyFile) {
- authutil.asCluster(csrsPrimary, this.keyFile, setChunkSize);
- } else {
- setChunkSize();
- }
+ // 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();
}
- 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;
+ if (this.keyFile) {
+ authutil.asCluster(this.configRS.nodes, this.keyFile, setFeatureCompatibilityVersion);
+ } else {
+ setFeatureCompatibilityVersion();
}
+ }
- printjson('Config servers: ' + this._configDB);
+ // 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}}));
- print("ShardingTest " + this._testName + " :\n" +
- tojson({config: this._configDB, shards: this._connections}));
+ configRS.awaitLastOpCommitted();
+ }
- this._mongos = [];
+ if (this.keyFile) {
+ authutil.asCluster(csrsPrimary, this.keyFile, setChunkSize);
+ } else {
+ setChunkSize();
+ }
+ }
- // Start the MongoS servers
- for (var i = 0; i < numMongos; i++) {
- const options = mongosOptions[i];
- options.configdb = this._configDB;
+ 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;
+ }
- 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,
- });
+ printjson('Config servers: ' + this._configDB);
- var bridge = new MongoBridge(bridgeOptions);
- }
+ print("ShardingTest " + this._testName + " :\n" +
+ tojson({config: this._configDB, shards: this._connections}));
- var conn = MongoRunner.runMongos(options);
- if (!conn) {
- throw new Error("Failed to start mongos " + i);
- }
+ this._mongos = [];
- if (otherParams.causallyConsistent) {
- conn.setCausalConsistency(true);
- }
+ // Start the MongoS servers
+ for (var i = 0; i < numMongos; i++) {
+ const options = mongosOptions[i];
+ options.configdb = this._configDB;
- if (otherParams.useBridge) {
- bridge.connectToBridge();
- this._mongos.push(bridge);
- unbridgedMongos.push(conn);
- } else {
- this._mongos.push(conn);
- }
+ 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 (i === 0) {
- this.s = this._mongos[i];
- this.admin = this._mongos[i].getDB('admin');
- this.config = this._mongos[i].getDB('config');
- }
+ var bridge = new MongoBridge(bridgeOptions);
+ }
- this["s" + i] = this._mongos[i];
+ var conn = MongoRunner.runMongos(options);
+ if (!conn) {
+ throw new Error("Failed to start mongos " + i);
}
- _extendWithShMethods();
+ if (otherParams.causallyConsistent) {
+ conn.setCausalConsistency(true);
+ }
- // 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);
+ if (otherParams.useBridge) {
+ bridge.connectToBridge();
+ this._mongos.push(bridge);
+ unbridgedMongos.push(conn);
} 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();
+ this._mongos.push(conn);
}
- try {
- if (!otherParams.manualAddShard) {
- var testName = this._testName;
- var admin = this.admin;
+ if (i === 0) {
+ this.s = this._mongos[i];
+ this.admin = this._mongos[i].getDB('admin');
+ this.config = this._mongos[i].getDB('config');
+ }
- this._connections.forEach(function(z) {
- var n = z.name || z.host || z;
+ this["s" + i] = this._mongos[i];
+ }
- print("ShardingTest " + testName + " going to add shard : " + n);
+ _extendWithShMethods();
- 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.
+ // 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;
+
+ this._connections.forEach(function(z) {
+ var n = z.name || z.host || z;
- if (jsTestOptions().keyFile) {
- jsTest.authenticateNodes(this._mongos);
+ 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;
+ }
- // 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"}));
- };
+ // 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});
- 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);
- }
- };
+ // 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 (!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);
- }
+ 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"}));
+ };
- self.waitForShardingInitialized();
+ 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);
}
- } catch (e) {
- // this was expected to fail, so clean up appropriately
- if (params.shouldFailInit === true) {
- this.stopOnFail();
+ };
+
+ 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);
}
- throw e;
+
+ self.waitForShardingInitialized();
}
- // 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