summaryrefslogtreecommitdiff
path: root/jstests/replsets/unsafe_reconfig_to_psa_set_fails.js
blob: 264a54177b8d0e0849593c2ceccf029521624398 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
 * 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.
 */

(function() {
"use strict";
load("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.initiateWithHighElectionTimeout();

    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.initiateWithHighElectionTimeout();

    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.initiateWithHighElectionTimeout();

    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();
}
})();