mongo/jstests/replsets/unsafe_reconfig_to_psa_set_...

170 lines
5.7 KiB
JavaScript

/**
* Asserts that a reconfig from a replica set with one writable voting node to a
* Primary-Secondary-Arbiter (PSA) topology fails if the secondary is electable. We test two
* reconfig scenarios, both of which should fail:
*
* 1) PA set to PSA set
* 2) PSA set with S having {votes: 0, priority: 0} to S with {votes: 1, priority: 1}
*
* Finally, we test the correct workflow for converting a replica set with only one writable voting
* node to a PSA architecture. This involves running two reconfigs. The first reconfig should
* add/configure the secondary to have {votes: 1, priority: 0}, to prevent it from being electable.
* The second reconfig should then increase its priority to the desired level.
*/
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {assertVoteCount, waitForNewlyAddedRemovalForNodeToBeCommitted} from "jstests/replsets/rslib.js";
{
jsTestLog("Testing reconfig from PA set to PSA set fails");
const rst = new ReplSetTest({
name: jsTestName(),
nodes: [{}, {rsConfig: {arbiterOnly: true}}],
});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
assertVoteCount(primary, {
votingMembersCount: 2,
majorityVoteCount: 2,
writableVotingMembersCount: 1,
writeMajorityCount: 1,
totalMembersCount: 2,
});
const config = rst.getReplSetConfigFromNode();
jsTestLog("Original config: " + tojson(config));
// This new node will be a secondary with {votes: 1, priority: 1}, which should not be able to
// be added in reconfig if the new topology has a PSA architecture.
rst.add({});
const newConfig = rst.getReplSetConfig();
config.members = newConfig.members;
config.version += 1;
jsTestLog(`New config with secondary added: ${tojson(config)}`);
assert.commandFailedWithCode(
primary.adminCommand({replSetReconfig: config}),
ErrorCodes.NewReplicaSetConfigurationIncompatible,
);
// Verify that the vote counts have not changed, since the reconfig did not successfully
// complete.
assertVoteCount(primary, {
votingMembersCount: 2,
majorityVoteCount: 2,
writableVotingMembersCount: 1,
writeMajorityCount: 1,
totalMembersCount: 2,
});
// Remove the node since it was not successfully added to the config, so we should not run
// validation checks on it when we shut down the replica set.
rst.remove(2);
rst.stopSet();
}
{
jsTestLog("Testing reconfig to remove {votes: 0} from secondary in PSA set fails");
const rst = new ReplSetTest({
nodes: [{}, {rsConfig: {votes: 0, priority: 0}}, {rsConfig: {arbiterOnly: true}}],
});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
assertVoteCount(primary, {
votingMembersCount: 2,
majorityVoteCount: 2,
writableVotingMembersCount: 1,
writeMajorityCount: 1,
totalMembersCount: 3,
});
const config = rst.getReplSetConfigFromNode();
jsTestLog("Original config: " + tojson(config));
// Modify the secondary to have {votes: 1, priority: 1}. This will also fail the reconfig.
config.members[1].votes = 1;
config.members[1].priority = 1;
jsTestLog(
`New config with secondary reconfigured to have {votes: 1, priority: 1}:
${tojson(config)}`,
);
assert.commandFailedWithCode(
primary.adminCommand({replSetReconfig: config}),
ErrorCodes.NewReplicaSetConfigurationIncompatible,
);
// Verify that the vote counts have not changed, since the reconfig did not successfully
// complete.
assertVoteCount(primary, {
votingMembersCount: 2,
majorityVoteCount: 2,
writableVotingMembersCount: 1,
writeMajorityCount: 1,
totalMembersCount: 3,
});
rst.stopSet();
}
{
jsTestLog(
"Testing that the correct workflow for converting a replica set with only one writable voting node to a PSA architecture succeeds",
);
const rst = new ReplSetTest({
nodes: [{}, {rsConfig: {arbiterOnly: true}}],
});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
assertVoteCount(primary, {
votingMembersCount: 2,
majorityVoteCount: 2,
writableVotingMembersCount: 1,
writeMajorityCount: 1,
totalMembersCount: 2,
});
let config = rst.getReplSetConfigFromNode();
jsTestLog("Original config: " + tojson(config));
// First, add the secondary with {priority: 0}, so that it is not electable.
rst.add({rsConfig: {votes: 1, priority: 0}});
const newConfig = rst.getReplSetConfig();
config.members = newConfig.members;
config.version += 1;
jsTestLog(`Reconfiguring set to add a secondary with {votes: 1: priority: 0. New config: ${tojson(config)}`);
assert.commandWorked(primary.adminCommand({replSetReconfig: config}));
waitForNewlyAddedRemovalForNodeToBeCommitted(primary, 2 /* memberIndex */);
assertVoteCount(primary, {
votingMembersCount: 3,
majorityVoteCount: 2,
writableVotingMembersCount: 2,
writeMajorityCount: 2,
totalMembersCount: 3,
});
// Second, give the secondary a non-zero priority level.
config = rst.getReplSetConfigFromNode();
config.members[1].priority = 1;
config.version += 1;
jsTestLog(`Reconfiguring set to give the secondary a positive priority. New config: ${tojson(config)}`);
assert.commandWorked(primary.adminCommand({replSetReconfig: config}));
assertVoteCount(primary, {
votingMembersCount: 3,
majorityVoteCount: 2,
writableVotingMembersCount: 2,
writeMajorityCount: 2,
totalMembersCount: 3,
});
rst.stopSet();
}