diff options
author | Pavi Vetriselvan <pvselvan@umich.edu> | 2020-02-28 12:26:40 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-03-17 20:45:56 +0000 |
commit | 4d91fac171cbe3f2af53d9258965399e648a1947 (patch) | |
tree | f2add0447402bbbfefbe16ba803daf77e0141a54 /jstests/hooks | |
parent | a6350e13bb18eab3b00624d8c8d82e550a382906 (diff) | |
download | mongo-4d91fac171cbe3f2af53d9258965399e648a1947.tar.gz |
SERVER-45094 add replica_sets_reconfig_jscore_passthrough suite
Diffstat (limited to 'jstests/hooks')
-rw-r--r-- | jstests/hooks/run_reconfig_background.js | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/jstests/hooks/run_reconfig_background.js b/jstests/hooks/run_reconfig_background.js new file mode 100644 index 00000000000..2836a8bf68d --- /dev/null +++ b/jstests/hooks/run_reconfig_background.js @@ -0,0 +1,133 @@ +/** + * This hook runs two reconfig commands against the primary of a replica set: + * (1): The first reconfig command changes the votes and priority of a node to 0, which will + * change the voting majority of the set. + * (2): The second reconfig command runs against the new voting majority and should succeed. + * + * This hook will run concurrently with tests. + */ + +'use strict'; + +(function() { +load('jstests/libs/discover_topology.js'); // For Topology and DiscoverTopology. +load('jstests/libs/parallelTester.js'); // For Thread. + +if (typeof db === 'undefined') { + throw new Error( + "Expected mongo shell to be connected a mongod, but global 'db' object isn't defined"); +} + +const conn = db.getMongo(); +const topology = DiscoverTopology.findConnectedNodes(conn); + +/** + * Returns true if the error code is transient. + */ +const isIgnorableError = function ignorableError(codeName) { + if (codeName == "ConfigurationInProgress" || codeName == "NotMaster") { + return true; + } + return false; +}; + +/** + * Runs two reconfig commands against the primary of a replica set. + * + * The first reconfig command randomly chooses a node to change it's votes and priority to 0 or 1 + * based on what the node's current votes and priority fields are. We always check to see that + * there exists at least one voting node in the set, which ensures that we can always have a + * primary. + * We also want to avoid changing the votes and priority of the current primary to 0 to avoid + * unnecessary stepdowns. + * + * The number of voting nodes in the replica set determines what the config majority is for both + * reconfig config commitment and reconfig oplog commitment. + * + * This function should not throw if everything is working properly. + */ +const reconfigBackgroundThread = function reconfigBackground( + primary, isIgnorableErrorFunc, numNodes, primaryIndex) { + // Calls 'func' with the print() function overridden to be a no-op. + Random.setRandomSeed(); + const quietly = (func) => { + const printOriginal = print; + try { + print = Function.prototype; + func(); + } finally { + print = printOriginal; + } + }; + + // Suppress the log messages generated establishing new mongo connections. The + // run_reconfig_background.js hook is executed frequently by resmoke.py and + // could lead to generating an overwhelming amount of log messages. + let conn; + quietly(() => { + conn = new Mongo(primary); + }); + assert.neq( + null, conn, "Failed to connect to primary '" + primary + "' for background reconfigs"); + + jsTestLog("Running reconfig to change votes."); + + var config = conn.getDB("local").system.replset.findOne(); + config.version++; + + // Calculate the total number of voting nodes in this set so that we make sure we + // always have at least one voting node. + const numVotingNodes = config.members.filter(member => member.votes === 1).length; + + // Randomly change the vote of a node to 1 or 0 depending on its current value. Do not + // change the primary's votes. + var indexToChange = primaryIndex; + while (indexToChange === primaryIndex) { + // randInt is exclusive of the upper bound. + indexToChange = Random.randInt(numNodes); + } + + // Change the priority to correspond to the votes. If the member's current votes field + // is 1, only change it to 0 if there is another voting member in this set. Otherwise, + // we risk having no voting members (and no suitable primary). + config.members[indexToChange].votes = (config.members[indexToChange].votes === 1) ? 0 : 1; + config.members[indexToChange].priority = config.members[indexToChange].votes; + + let votingRes = conn.getDB("admin").runCommand({replSetReconfig: config}); + if (!votingRes.ok && !isIgnorableErrorFunc(votingRes.codeName)) { + jsTestLog("Reconfig to change votes FAILED."); + return votingRes; + } + + jsTestLog("Running reconfig after changing voting majority."); + + config = conn.getDB("local").system.replset.findOne(); + config.version++; + let reconfigRes = conn.getDB("admin").runCommand({replSetReconfig: config}); + if (!reconfigRes.ok && !isIgnorableErrorFunc(reconfigRes.codeName)) { + jsTestLog("Reconfig after changing voting majority FAILED."); + return reconfigRes; + } + + return {ok: 1}; +}; + +if (topology.type === Topology.kReplicaSet) { + var numNodes = topology.nodes.length; + var primaryIndex = topology.nodes.indexOf(topology.primary); + const thread = new Thread( + reconfigBackgroundThread, topology.primary, isIgnorableError, numNodes, primaryIndex); + try { + thread.start(); + } finally { + // Wait for thread to finish and throw an error if it fails. + let res; + thread.join(); + res = thread.returnData(); + + assert.commandWorked(res, () => "reconfig hook failed: " + tojson(res)); + } +} else { + throw new Error('Unsupported topology configuration: ' + tojson(topology)); +} +})(); |