diff options
author | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2018-10-20 12:47:00 -0400 |
---|---|---|
committer | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2018-10-20 12:47:00 -0400 |
commit | 2a850f337a76523eba2fd1a40299f81d64ea0083 (patch) | |
tree | 4d61937fb199566f217dd7634067b8a670c149c9 | |
parent | fdffd3097be3be7bcc590843bf1f1b775de7dc9c (diff) | |
download | mongo-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.js | 79 | ||||
-rw-r--r-- | src/mongo/shell/bridge.js | 5 | ||||
-rw-r--r-- | src/mongo/shell/replsettest.js | 37 | ||||
-rw-r--r-- | src/mongo/shell/servers_misc.js | 21 | ||||
-rw-r--r-- | src/mongo/shell/shardingtest.js | 54 |
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, |