/*
|
* This test demonstrates 2 primaries existing in the same replica set and both primaries
|
* can satisfy majority write concern.
|
*
|
* Basically the test simulates below scenario
|
* Note: 'P' refers primary, 'S' refers to secondary.
|
* 1) [P, S0, S1, S2] // Start a 4 node replica set.
|
* 2) Partition A: [P] Partition B: [S0->P, S1, S2] // Create n/w partition A & B.
|
* 3) Partition A: [P] Partition B: [P, S1, S2, S3] // Add a new node S3 to Partition B.
|
* 4) Partition A: [P, S2] Partition B: [P, S1, S3] // Restart/resync S2 and move back to partition A pool.
|
* 5) Partition A: [P, S2, S4] Partition B: [P, S1, S3] // Add a new node S4 to Partition A.
|
*/
|
load('jstests/replsets/rslib.js');
|
(function() {
|
'use strict';
|
|
// Start a 4 node replica set.
|
// [P, S0, S1, S2]
|
const rst = new ReplSetTest({
|
nodes: [{}, {}, {rsConfig: {priority: 0}}, {rsConfig: {priority: 0}}],
|
nodeOptions: {setParameter: {enableAutomaticReconfig: false}},
|
useBridge: true
|
});
|
|
// Disable Chaining and disable automatic election from happening due to liveness timeout.
|
var config = rst.getReplSetConfig();
|
config.settings = config.settings || {};
|
config.settings["chainingAllowed"] = false;
|
config.settings["electionTimeoutMillis"] = ReplSetTest.kForeverMillis;
|
|
rst.startSet();
|
rst.initiate(config);
|
|
const dbName = jsTest.name();
|
const collName = "coll";
|
|
let primary1 = rst.getPrimary();
|
const primaryDB = primary1.getDB(dbName);
|
const primaryColl = primaryDB[collName];
|
const secondaries = rst.getSecondaries();
|
|
jsTestLog("Do a document write");
|
assert.commandWorked(primaryColl.insert({_id: 1, x: 1}, {"writeConcern": {"w": 4}}));
|
rst.awaitReplication();
|
|
// Create a n/w partition such that we result in this state [P] [S0, S1, S2].
|
jsTestLog("Disconnect primary1 from all secondaries");
|
primary1.disconnect([secondaries[0], secondaries[1], secondaries[2]]);
|
|
jsTestLog("Make secondary0 to be become primary");
|
assert.commandWorked(secondaries[0].adminCommand({"replSetStepUp": 1}));
|
|
// Now our network topology will be [P] [S0->P, S1, S2].
|
jsTestLog("Wait for secondary0 to become master");
|
checkLog.contains(secondaries[0], "Transition to primary complete");
|
let primary2 = secondaries[0];
|
|
jsTestLog("Adding a new voting node to the replica set");
|
const node5 = rst.add({
|
rsConfig: {priority: 0, votes: 1},
|
setParameter: {
|
'numInitialSyncAttempts': 1,
|
'enableAutomaticReconfig': false,
|
}
|
});
|
|
// Simulate this network topology [P] [P, S1, S2, S3].
|
node5.disconnect([primary1]);
|
|
// Run a reconfig command on the primary 2 to add node 5.
|
var config = rst.getReplSetConfigFromNode(1);
|
var newConfig = rst.getReplSetConfig();
|
config.members = newConfig.members;
|
config.version += 1;
|
assert.adminCommandWorkedAllowingNetworkError(
|
primary2, {replSetReconfig: config, maxTimeMS: ReplSetTest.kDefaultTimeoutMS});
|
|
// Make sure the new writes is able to propagate to the newly added node.
|
jsTestLog("Do a document write on the primary2");
|
assert.commandWorked(
|
primary2.getDB(dbName)[collName].insert({_id: 2, x: 2}, {"writeConcern": {"w": 4}}));
|
|
// Now make sure, we get into this state [P, S2] [P, S1, S3].
|
jsTestLog("Disconnect Secondary2 from primary2 and reconnect to primary1");
|
secondaries[2].disconnect([secondaries[0], secondaries[1], node5]);
|
secondaries[2].reconnect([primary1]);
|
|
jsTestLog("Kill and restart Secondary2");
|
rst.stop(3, 9, {allowedExitCode: MongoRunner.EXIT_SIGKILL}, {forRestart: true});
|
jsTestLog("Restarting the node.");
|
var restartNode = rst.start(3, {startClean: true}, true);
|
|
jsTestLog("wait for secondary state");
|
waitForState(restartNode, ReplSetTest.State.SECONDARY);
|
|
jsTestLog("Adding a new voting node to the replica set");
|
const node6 = rst.add({
|
rsConfig: {priority: 0, votes: 1},
|
setParameter: {
|
'numInitialSyncAttempts': 1,
|
'enableAutomaticReconfig': false,
|
}
|
});
|
|
// Simulate this network topology [P, S2, S4] [P, S1, S3].
|
node6.disconnect([secondaries[0], secondaries[1], node5]);
|
|
// Run a reconfig command on the primary1 to add node 6
|
config = rst.getReplSetConfigFromNode(0);
|
newConfig = rst.getReplSetConfig();
|
// Only reset members.
|
config.members[4] = newConfig.members[5];
|
config.version += 1;
|
assert.adminCommandWorkedAllowingNetworkError(
|
primary1, {replSetReconfig: config, maxTimeMS: ReplSetTest.kDefaultTimeoutMS});
|
|
jsTestLog(
|
"Do some document writes to verify we have 2 primaries and both satisfy write concern majority");
|
assert.commandWorked(primary1.getDB(dbName)[collName].insert({_id: 3, x: "primary1 Doc"},
|
{"writeConcern": {"w": "majority"}}));
|
assert.commandWorked(primary1.getDB(dbName)[collName].insert({_id: 4, x: "primary1 Doc"},
|
{"writeConcern": {"w": 3}}));
|
assert.commandWorked(primary1.getDB(dbName)[collName].insert({_id: 5, x: "primary1 Doc"},
|
{"writeConcern": {"w": "majority"}}));
|
assert.commandWorked(primary2.getDB(dbName)[collName].insert({_id: 6, x: "primary2 Doc"},
|
{"writeConcern": {"w": "majority"}}));
|
|
jsTestLog("Verify our primary1 can be get re-elected.");
|
assert.commandWorked(primary1.adminCommand({"replSetStepDown": 1000, "force": true}));
|
assert.commandWorked(primary1.adminCommand({replSetFreeze: 0}));
|
assert.commandWorked(primary1.adminCommand({"replSetStepUp": 1}));
|
|
jsTestLog("Test completed");
|
rst.stopSet();
|
}());
|