summaryrefslogtreecommitdiff
path: root/jstests/replsets/newly_added_member_id_vs_index.js
blob: a7bd0a6fc49eaf0fe72dfb5ac5e2f2eaac7b9d29 (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
/**
 * Test that removal of 'newlyAdded' fields is done by memberId, not memberIndex. We do this by
 * setting up two 'newlyAdded' nodes with inverse {memberId, memberIndex} pairs and checking that we
 * remove the 'newlyAdded' field for the node identified by _id.
 *
 * @tags: [
 * ]
 */

(function() {
"use strict";
load("jstests/libs/fail_point_util.js");
load('jstests/replsets/rslib.js');

const testName = jsTestName();
const dbName = "testdb";
const collName = "testcoll";

const rst = new ReplSetTest({
    name: testName,
    nodes: 1,
    settings: {chainingAllowed: false},
});
rst.startSet();
rst.initiateWithHighElectionTimeout();

const primary = rst.getPrimary();
const primaryDb = primary.getDB(dbName);
const primaryColl = primaryDb.getCollection(collName);

assert.commandWorked(primaryColl.insert({"starting": "doc"}));

jsTestName("Adding two members with alternating memberIds and memberIndex fields");

// This node will be at index 1 with _id 2.
const newNodeOne = rst.add({
    rsConfig: {_id: 2, priority: 0},
    setParameter: {
        'failpoint.initialSyncHangBeforeFinish': tojson({mode: 'alwaysOn'}),
        'numInitialSyncAttempts': 1,
    }
});

// This node will be at index 2 with _id 1.
const newNodeTwo = rst.add({
    rsConfig: {_id: 1, priority: 0},
    setParameter: {
        'failpoint.initialSyncHangBeforeFinish': tojson({mode: 'alwaysOn'}),
        'numInitialSyncAttempts': 1,
    }
});

rst.reInitiate();

jsTestName("Checking that the config has the ids and indexes flipped");
let configOnDisk = primary.getDB("local").system.replset.findOne();
assert.eq(2, configOnDisk.members[1]._id, configOnDisk);
assert.eq(1, configOnDisk.members[2]._id, configOnDisk);

assert.commandWorked(newNodeOne.adminCommand({
    waitForFailPoint: "initialSyncHangBeforeFinish",
    timesEntered: 1,
    maxTimeMS: kDefaultWaitForFailPointTimeout
}));
assert.commandWorked(newNodeTwo.adminCommand({
    waitForFailPoint: "initialSyncHangBeforeFinish",
    timesEntered: 1,
    maxTimeMS: kDefaultWaitForFailPointTimeout
}));

jsTestLog("Checking that the 'newlyAdded' field is set on both new nodes");
assert(isMemberNewlyAdded(primary, 1));
assert(isMemberNewlyAdded(primary, 2));

jsTestLog("Checking vote counts while secondaries are still in initial sync");
assertVoteCount(primary, {
    votingMembersCount: 1,
    majorityVoteCount: 1,
    writableVotingMembersCount: 1,
    writeMajorityCount: 1,
    totalMembersCount: 3
});

jsTestName("Allowing one of the secondaries to complete initial sync (_id 1, index 2)");
assert.commandWorked(
    newNodeTwo.adminCommand({configureFailPoint: "initialSyncHangBeforeFinish", mode: "off"}));
rst.waitForState(newNodeTwo, ReplSetTest.State.SECONDARY);

jsTestName("Waiting for its 'newlyAdded' to be removed");
waitForNewlyAddedRemovalForNodeToBeCommitted(primary, 2 /* memberIndex */);

jsTestName("Vefirying the results of the 'newlyAdded' removal");
configOnDisk = primary.getDB("local").system.replset.findOne();
assert.eq(1, configOnDisk.members[2]._id, configOnDisk);
assert.eq(false, configOnDisk.members[2].hasOwnProperty("newlyAdded"), configOnDisk);
assertVoteCount(primary, {
    votingMembersCount: 2,
    majorityVoteCount: 2,
    writableVotingMembersCount: 2,
    writeMajorityCount: 2,
    totalMembersCount: 3,
});

jsTestName("Making sure the other node still has its 'newlyAdded' field");
assert(isMemberNewlyAdded(primary, 1));
configOnDisk = primary.getDB("local").system.replset.findOne();
assert.eq(2, configOnDisk.members[1]._id, configOnDisk);
assert(configOnDisk.members[1]["newlyAdded"], configOnDisk);

jsTestName("Letting the other secondary node finish initial sync (_id 2, index 1)");
assert.commandWorked(
    newNodeOne.adminCommand({configureFailPoint: "initialSyncHangBeforeFinish", mode: "off"}));
rst.waitForState(newNodeOne, ReplSetTest.State.SECONDARY);

jsTestName("Waiting for the second 'newlyAdded' field to be removed.");
waitForNewlyAddedRemovalForNodeToBeCommitted(primary, 1 /* memberIndex */);

jsTestName("Verifying the results of the second 'newlyAdded' removal");
configOnDisk = primary.getDB("local").system.replset.findOne();
assert.eq(2, configOnDisk.members[1]._id, configOnDisk);
assert.eq(false, configOnDisk.members[1].hasOwnProperty("newlyAdded"), configOnDisk);
assertVoteCount(primary, {
    votingMembersCount: 3,
    majorityVoteCount: 2,
    writableVotingMembersCount: 3,
    writeMajorityCount: 2,
    totalMembersCount: 3,
});

jsTestLog("Making sure set can accept w:3 writes");
assert.commandWorked(primaryColl.insert({"steady": "state"}, {writeConcern: {w: 3}}));

rst.awaitReplication();
rst.stopSet();
})();