summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAli Mir <ali.mir@mongodb.com>2020-02-24 18:01:15 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-03-26 23:36:48 +0000
commit98299f2f7eb295361cee2aea4dc03b952483d715 (patch)
treef6886fa934501b0dd781dd9baf4c2c32f44dfa98
parentc1b56a30d3b445715bbbe302215dedc748ceb86f (diff)
downloadmongo-98299f2f7eb295361cee2aea4dc03b952483d715.tar.gz
SERVER-45088 Simplify safe reconfig avoids diverging configs test
-rw-r--r--jstests/replsets/reconfig_avoids_diverging_configs.js125
1 files changed, 48 insertions, 77 deletions
diff --git a/jstests/replsets/reconfig_avoids_diverging_configs.js b/jstests/replsets/reconfig_avoids_diverging_configs.js
index 236b7b76bc8..6697b0497a5 100644
--- a/jstests/replsets/reconfig_avoids_diverging_configs.js
+++ b/jstests/replsets/reconfig_avoids_diverging_configs.js
@@ -5,16 +5,13 @@
* C2: {n1,n3,n4}
* The C1 quorum {n1,n2} and the C2 quorum {n3,n4} do not overlap.
*
- * 1. Node1 is the initial primary.
- * 2. Disconnect node4 from all three other nodes.
- * 3. Step down node1 and step up node2.
- * 4. Disconnect the current primary, node2, from all other nodes.
- * 5. Issue a reconfig to node2 that removes node4.
- * 6. Reconnect node4 to the current secondaries, node1 and node3.
- * 7. Step up node3, which creates a two primary scenario.
- * 8. Issue a reconfig to node3 that removes node2. We now have diverging configs
+ * 1. Node0 is the initial primary.
+ * 2. Disconnect node0 from all other nodes.
+ * 3. Issue a reconfig to node0 that removes node3.
+ * 4. Step up node1, which creates a two primary scenario.
+ * 5. Issue a reconfig to node1 that removes node2. We now have diverging configs
* from two different primaries.
- * 9. Reconnect node2 to the rest of the set and verify that its reconfig fails.
+ * 6. Reconnect node0 to the rest of the set and verify that its reconfig fails.
*
* @tags: [requires_fcv_44]
*/
@@ -25,100 +22,74 @@ load('jstests/libs/test_background_ops.js');
load("jstests/replsets/rslib.js");
load('jstests/aggregation/extras/utils.js');
-const dbName = "test";
-const collName = "coll";
let rst = new ReplSetTest({nodes: 4, useBridge: true});
rst.startSet();
rst.initiateWithHighElectionTimeout();
-const node1 = rst.getPrimary();
-const secondaries = rst.getSecondaries();
-const node2 = secondaries[0];
-const node3 = secondaries[1];
-const node4 = secondaries[2];
-const coll = node1.getDB(dbName)[collName];
+const node0 = rst.getPrimary();
+const [node1, node2, node3] = rst.getSecondaries();
+jsTestLog("Current replica set topology: [node0 (Primary), node1, node2, node3]");
-// Partition the 4th node.
-node4.disconnect([node1, node2, node3]);
-
-jsTestLog("Current replica set topology: [Primary-Secondary-Secondary] [Secondary]");
-assert.commandWorked(node1.adminCommand({replSetStepDown: 120}));
-// Step up a new primary in the partitioned repl set.
-assert.commandWorked(node2.adminCommand({replSetStepUp: 1}));
-
-// Wait until the config has been committed.
-assert.soon(() => isConfigCommitted(rst.getPrimary()));
// The quorum check places stricter bounds on the safe reconfig
// protocol and won't allow this specific scenario of diverging configs
// to happen. However, it's still worth testing the original reconfig
// protocol that omitted the check for correctness.
configureFailPoint(rst.getPrimary(), "omitConfigQuorumCheck");
-// Reconfig to remove the 4th node.
+// Reconfig to remove the node3. The new config, C1, is now {node0, node1, node2}.
const C1 = Object.assign({}, rst.getReplSetConfigFromNode());
C1.members = C1.members.slice(0, 3); // Remove the last node.
-C1.version++;
+// Increase the C1 version by a high number to ensure the following config
+// C2 will win the propagation by having a higher term.
+C1.version = C1.version + 1000;
+waitForConfigReplication(node0);
+rst.awaitReplication();
jsTestLog("Disconnecting the primary from other nodes");
-assert.eq(rst.getPrimary(), node2);
-node2.disconnect([node1, node3, node4]);
-jsTestLog("Current replica set topology: [Primary] [Secondary-Secondary] [Secondary]");
+assert.eq(rst.getPrimary(), node0);
+node0.disconnect([node1, node2, node3]);
+jsTestLog("Current replica set topology: [node0 (Primary)] [node1, node2, node3]");
// Create parallel shell to execute reconfig on partitioned primary.
-// This reconfig will succeed due to the omission of the quorum check, but
-// will not get propagated.
-startParallelShell(funWithArgs(function(config) {
- assert.commandWorked(db.getMongo().adminCommand({replSetReconfig: config}));
- }, C1), node2.port);
+// This reconfig will not get propagated.
+const parallelShell = startParallelShell(
+ funWithArgs(function(config) {
+ assert.commandFailedWithCode(db.getMongo().adminCommand({replSetReconfig: config}),
+ ErrorCodes.InterruptedDueToReplStateChange,
+ "Reconfig C1 should fail");
+ }, C1), node0.port);
-// Reconnect the 4th node to the secondaries.
-node4.reconnect([node1, node3]);
-// Make sure the config has been replicated to all connected voters so that stepup can succeed.
-waitForConfigReplication(node3, [node1, node3, node4]);
-assert.commandWorked(node3.adminCommand({replSetStepUp: 1}));
-rst.awaitNodesAgreeOnPrimary(rst.kDefaultTimeoutMS, [node1, node3, node4]);
-jsTestLog("Current replica set topology: [Primary-Secondary-Secondary] [Primary]");
-assert.soon(function() {
- return isConfigCommitted(node3);
-});
+assert.commandWorked(node1.adminCommand({replSetStepUp: 1}));
+rst.awaitNodesAgreeOnPrimary(rst.kDefaultTimeoutMS, [node1, node2, node3], 1);
+jsTestLog("Current replica set topology: [node0 (Primary)] [node1 (Primary), node2, node3]");
+assert.soon(() => node1.getDB('admin').runCommand({ismaster: 1}).ismaster);
+assert.soon(() => isConfigCommitted(node1));
// Reconfig to remove a secondary. We need to specify the node to get the original
-// config from as there are two primaries, node2 and node3, in the replset.
-let C2 = Object.assign({}, rst.getReplSetConfigFromNode(2));
-const removedSecondary = C2.members.splice(0, 1);
+// config from as there are two primaries, node0 and node1, in the replset.
+// The new config is now {node0, node1, node3}.
+let C2 = Object.assign({}, rst.getReplSetConfigFromNode(1));
+const removedSecondary = C2.members.splice(2, 1);
C2.version++;
-assert.commandWorked(node3.adminCommand({replSetReconfig: C2}));
-assert.soon(() => isConfigCommitted(node3));
+assert.commandWorked(node1.adminCommand({replSetReconfig: C2}));
+assert.soon(() => isConfigCommitted(node1));
-// Reconnect partitioned node to the other nodes.
-node2.reconnect([node3, node4]);
+// Reconnect the partitioned primary, node0, to the other nodes.
+node0.reconnect([node1, node2, node3]);
// The newly connected node will receive a heartbeat with a higher term, and
-// step down from being primary. The reconfig command issued to this node will fail.
-rst.waitForState(node2, ReplSetTest.State.SECONDARY);
-
-// Make sure the newly connected secondary has updated its config.
-assert.soon(function() {
- const node2TermUpdated = bsonWoCompare(node2.adminCommand({replSetGetStatus: 1}).term,
- node3.adminCommand({replSetGetStatus: 1}).term) == 0;
- const node2ConfigTermUpdated =
- node2.adminCommand({replSetGetStatus: 1}).members[1].configTerm ==
- node3.adminCommand({replSetGetStatus: 1}).members[2].configTerm;
- const node2ConfigVersionUpdated =
- node2.adminCommand({replSetGetStatus: 1}).members[1].configVersion ==
- node3.adminCommand({replSetGetStatus: 1}).members[2].configVersion;
- return node2TermUpdated && node2ConfigTermUpdated && node2ConfigVersionUpdated;
-});
+// step down from being primary. The reconfig command issued to this node, C1, will fail.
+rst.waitForState(node0, ReplSetTest.State.SECONDARY);
+rst.awaitNodesAgreeOnPrimary(rst.kDefaultTimeoutMS, [node0, node1, node3]);
+waitForConfigReplication(node1);
+assert.eq(C2, rst.getReplSetConfigFromNode());
-// Reconnect the 4th node to return to a stable state.
-let C3 = Object.assign({}, rst.getReplSetConfigFromNode(2));
+// The new config is now {node0, node1, node2, node3}.
+let C3 = Object.assign({}, rst.getReplSetConfigFromNode(1));
C3.members.push(removedSecondary[0]);
C3.version++;
-node1.reconnect([node2, node3, node4]);
-assert.commandWorked(node3.adminCommand({replSetReconfig: C3}));
-assert.soon(function() {
- return isConfigCommitted(node3);
-});
+assert.commandWorked(node1.adminCommand({replSetReconfig: C3}));
+assert.soon(() => isConfigCommitted(node1));
rst.awaitNodesAgreeOnPrimary();
-rst.awaitNodesAgreeOnConfigVersion();
+parallelShell();
rst.stopSet();
}());