summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Hirschhorn <max.hirschhorn@mongodb.com>2018-10-20 12:47:00 -0400
committerMax Hirschhorn <max.hirschhorn@mongodb.com>2018-10-20 12:47:00 -0400
commit2a850f337a76523eba2fd1a40299f81d64ea0083 (patch)
tree4d61937fb199566f217dd7634067b8a670c149c9
parentfdffd3097be3be7bcc590843bf1f1b775de7dc9c (diff)
downloadmongo-2a850f337a76523eba2fd1a40299f81d64ea0083.tar.gz
SERVER-31570 Stagger mongod/mongos ports +10 from mongobridge port.
Also introduces a resetAllocatedPorts() function so that tests which allocate a large number of ports as part of starting many MongoDB deployments can succeed despite the limited range of 250 - 20 = 230 ports given by resmoke.py to the job for the mongo shell. (cherry picked from commit 60c9cbf7b6ab033930003c0ef434d64136bddd02) (cherry picked from commit 1f306300a246c94b3274f6741b9ff92572e91a09)
-rw-r--r--jstests/noPassthrough/shell_mongobridge_port_allocation.js79
-rw-r--r--src/mongo/shell/bridge.js5
-rw-r--r--src/mongo/shell/replsettest.js37
-rw-r--r--src/mongo/shell/servers_misc.js21
-rw-r--r--src/mongo/shell/shardingtest.js54
5 files changed, 185 insertions, 11 deletions
diff --git a/jstests/noPassthrough/shell_mongobridge_port_allocation.js b/jstests/noPassthrough/shell_mongobridge_port_allocation.js
new file mode 100644
index 00000000000..a61eda2fc87
--- /dev/null
+++ b/jstests/noPassthrough/shell_mongobridge_port_allocation.js
@@ -0,0 +1,79 @@
+/**
+ * Tests that the ports assigned to the mongobridge and mongod/mongos processes make it easy to
+ * reason about which mongobridge process corresponds to a particular mongod/mongos process in the
+ * logs.
+ *
+ * @tags: [requires_replication, requires_sharding]
+ */
+(function() {
+ "use strict";
+
+ function checkBridgeOffset(node, processType) {
+ const bridgePort = node.port;
+ const serverPort =
+ assert.commandWorked(node.adminCommand({getCmdLineOpts: 1})).parsed.net.port;
+ assert.neq(bridgePort,
+ serverPort,
+ node + " is a connection to " + processType + " rather than to mongobridge");
+ assert.eq(bridgePort + MongoBridge.kBridgeOffset,
+ serverPort,
+ "corresponding mongobridge and " + processType +
+ " ports should be staggered by a multiple of 10");
+ }
+
+ // We use >5 nodes to ensure that allocating twice as many ports doesn't interfere with having
+ // the corresponding mongobridge and mongod ports staggered by a multiple of 10.
+ const rst = new ReplSetTest({nodes: 7, useBridge: true});
+ rst.startSet();
+
+ // Rig the election so that the primary remains stable throughout this test despite the replica
+ // set having a larger number of members.
+ const replSetConfig = rst.getReplSetConfig();
+ for (let i = 1; i < rst.nodes.length; ++i) {
+ replSetConfig.members[i].priority = 0;
+ replSetConfig.members[i].votes = 0;
+ }
+ rst.initiate(replSetConfig);
+
+ for (let node of rst.nodes) {
+ checkBridgeOffset(node, "mongod");
+ }
+
+ rst.stopSet();
+
+ // We run ShardingTest under mongobridge with both 1-node replica set shards and stand-alone
+ // mongod shards.
+ for (let options of[{rs: {nodes: 1}}, {rs: false, shardAsReplicaSet: false}]) {
+ resetAllocatedPorts();
+
+ const numMongos = 5;
+ const numShards = 5;
+ const st = new ShardingTest(Object.assign({
+ mongos: numMongos,
+ shards: numShards,
+ config: {nodes: 1},
+ useBridge: true,
+ },
+ options));
+
+ for (let i = 0; i < numMongos; ++i) {
+ checkBridgeOffset(st["s" + i], "mongos");
+ }
+
+ for (let configServer of st.configRS.nodes) {
+ checkBridgeOffset(configServer, "config server");
+ }
+
+ for (let i = 0; i < numShards; ++i) {
+ if (options.rs) {
+ for (let node of st["rs" + i].nodes) {
+ checkBridgeOffset(node, "shard");
+ }
+ } else {
+ checkBridgeOffset(st["d" + i], "shard");
+ }
+ }
+
+ st.stop();
+ }
+})();
diff --git a/src/mongo/shell/bridge.js b/src/mongo/shell/bridge.js
index 702d823bf97..28831ba7a76 100644
--- a/src/mongo/shell/bridge.js
+++ b/src/mongo/shell/bridge.js
@@ -298,3 +298,8 @@ function MongoBridge(options) {
},
});
}
+
+// The number of ports that ReplSetTest and ShardingTest should stagger the port number of the
+// mongobridge process and its corresponding mongod/mongos process by. The resulting port number of
+// the mongod/mongos process is MongoBridge#port + MongoBridge.kBridgeOffset.
+MongoBridge.kBridgeOffset = 10;
diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js
index 421d733153e..f1050c27328 100644
--- a/src/mongo/shell/replsettest.js
+++ b/src/mongo/shell/replsettest.js
@@ -89,6 +89,8 @@ var ReplSetTest = function(opts) {
var _bridgeOptions;
var _unbridgedPorts;
var _unbridgedNodes;
+ var _allocatePortForNode;
+ var _allocatePortForBridge;
var _causalConsistency;
@@ -764,14 +766,14 @@ var ReplSetTest = function(opts) {
* Adds a node to the replica set managed by this instance.
*/
this.add = function(config) {
- var nextPort = allocatePort();
+ var nextPort = _allocatePortForNode();
print("ReplSetTest Next port: " + nextPort);
this.ports.push(nextPort);
printjson(this.ports);
if (_useBridge) {
- _unbridgedPorts.push(allocatePort());
+ _unbridgedPorts.push(_allocatePortForBridge());
}
var nextId = this.nodes.length;
@@ -2164,12 +2166,39 @@ var ReplSetTest = function(opts) {
numNodes = opts.nodes;
}
- self.ports = allocatePorts(numNodes);
+ if (_useBridge) {
+ let makeAllocatePortFn = (preallocatedPorts) => {
+ let idxNextNodePort = 0;
+
+ return function() {
+ if (idxNextNodePort >= preallocatedPorts.length) {
+ throw new Error("Cannot use a replica set larger than " +
+ preallocatedPorts.length + " members with useBridge=true");
+ }
+
+ const nextPort = preallocatedPorts[idxNextNodePort];
+ ++idxNextNodePort;
+ return nextPort;
+ };
+ };
+
+ _allocatePortForBridge = makeAllocatePortFn(allocatePorts(MongoBridge.kBridgeOffset));
+ _allocatePortForNode = makeAllocatePortFn(allocatePorts(MongoBridge.kBridgeOffset));
+ } else {
+ _allocatePortForBridge = function() {
+ throw new Error("Using mongobridge isn't enabled for this replica set");
+ };
+ _allocatePortForNode = allocatePort;
+ }
+
self.nodes = [];
if (_useBridge) {
- _unbridgedPorts = allocatePorts(numNodes);
+ self.ports = Array.from({length: numNodes}, _allocatePortForBridge);
+ _unbridgedPorts = Array.from({length: numNodes}, _allocatePortForNode);
_unbridgedNodes = [];
+ } else {
+ self.ports = Array.from({length: numNodes}, _allocatePortForNode);
}
}
diff --git a/src/mongo/shell/servers_misc.js b/src/mongo/shell/servers_misc.js
index 379bffd3677..115520a1583 100644
--- a/src/mongo/shell/servers_misc.js
+++ b/src/mongo/shell/servers_misc.js
@@ -176,14 +176,26 @@ ReplTest.prototype.stop = function(master, signal) {
/**
* Returns a port number that has not been given out to any other caller from the same mongo shell.
*/
-allocatePort = (function() {
+var allocatePort;
+
+/**
+ * Resets the range of ports which have already been given out to callers of allocatePort().
+ *
+ * This function can be used to allow a test to allocate a large number of ports as part of starting
+ * many MongoDB deployments without worrying about hitting the configured maximum. Callers of this
+ * function should take care to ensure MongoDB deployments started earlier have been terminated and
+ * won't be reused.
+ */
+var resetAllocatedPorts;
+
+(function() {
// Defer initializing these variables until the first call, as TestData attributes may be
// initialized as part of the --eval argument (e.g. by resmoke.py), which will not be evaluated
// until after this has loaded.
var maxPort;
var nextPort;
- return function() {
+ allocatePort = function() {
// The default port was chosen in an attempt to have a large number of unassigned ports that
// are also outside the ephemeral port range.
nextPort = nextPort || jsTestOptions().minPort || 20000;
@@ -194,6 +206,11 @@ allocatePort = (function() {
}
return nextPort++;
};
+
+ resetAllocatedPorts = function() {
+ jsTest.log("Resetting the range of allocated ports");
+ maxPort = nextPort = undefined;
+ };
})();
/**
diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js
index 1f4dd7f7681..e9dd83a7a70 100644
--- a/src/mongo/shell/shardingtest.js
+++ b/src/mongo/shell/shardingtest.js
@@ -1110,10 +1110,52 @@ var ShardingTest = function(params) {
this._rs = [];
this._rsObjects = [];
+ let unbridgedConnections;
+ let unbridgedConfigServers;
+ let unbridgedMongos;
+ let _makeAllocatePortFn;
+ let _allocatePortForMongos;
+ let _allocatePortForBridgeForMongos;
+ let _allocatePortForShard;
+ let _allocatePortForBridgeForShard;
+
if (otherParams.useBridge) {
- var unbridgedConnections = [];
- var unbridgedConfigServers = [];
- var unbridgedMongos = [];
+ unbridgedConnections = [];
+ unbridgedConfigServers = [];
+ unbridgedMongos = [];
+
+ _makeAllocatePortFn = (preallocatedPorts, errorMessage) => {
+ let idxNextNodePort = 0;
+
+ return function() {
+ if (idxNextNodePort >= preallocatedPorts.length) {
+ throw new Error(errorMessage(preallocatedPorts.length));
+ }
+
+ const nextPort = preallocatedPorts[idxNextNodePort];
+ ++idxNextNodePort;
+ return nextPort;
+ };
+ };
+
+ let errorMessage = (length) =>
+ "Cannot use more than " + length + " mongos processes when useBridge=true";
+ _allocatePortForBridgeForMongos =
+ _makeAllocatePortFn(allocatePorts(MongoBridge.kBridgeOffset), errorMessage);
+ _allocatePortForMongos =
+ _makeAllocatePortFn(allocatePorts(MongoBridge.kBridgeOffset), errorMessage);
+
+ errorMessage = (length) =>
+ "Cannot use more than " + length + " stand-alone shards when useBridge=true";
+ _allocatePortForBridgeForShard =
+ _makeAllocatePortFn(allocatePorts(MongoBridge.kBridgeOffset), errorMessage);
+ _allocatePortForShard =
+ _makeAllocatePortFn(allocatePorts(MongoBridge.kBridgeOffset), errorMessage);
+ } else {
+ _allocatePortForBridgeForShard = _allocatePortForBridgeForMongos = function() {
+ throw new Error("Using mongobridge isn't enabled for this sharded cluster");
+ };
+ _allocatePortForShard = _allocatePortForMongos = allocatePort;
}
// Start the MongoD servers (shards)
@@ -1207,13 +1249,14 @@ var ShardingTest = function(params) {
options = Object.merge(options, otherParams.shardOptions);
options = Object.merge(options, otherParams["d" + i]);
- options.port = options.port || allocatePort();
+ options.port = options.port || _allocatePortForShard();
if (otherParams.useBridge) {
var bridgeOptions =
Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {});
bridgeOptions = Object.merge(bridgeOptions, {
hostName: otherParams.useHostname ? hostName : "localhost",
+ port: _allocatePortForBridgeForShard(),
// The mongod 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,
@@ -1340,7 +1383,7 @@ var ShardingTest = function(params) {
options = Object.merge(options, otherParams.mongosOptions);
options = Object.merge(options, otherParams["s" + i]);
- options.port = options.port || allocatePort();
+ options.port = options.port || _allocatePortForMongos();
mongosOptions.push(options);
}
@@ -1407,6 +1450,7 @@ var ShardingTest = function(params) {
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,