summaryrefslogtreecommitdiff
path: root/jstests/replsets/sync_source_changes.js
blob: 38e55dac5e7ede4eac8ed5da3412a9ccbdca9d58 (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
/**
 * Tests that when the current sync source no longer meets the strict criteria for being a sync
 * source, and there is another node which does meet those criteria, we will change sync source
 * (eventually).
 */

(function() {
"use strict";

load("jstests/replsets/rslib.js");             // reconfig
load("jstests/replsets/libs/sync_source.js");  // assertSyncSourceMatchesSoon

// We need to wait for a heartbeat from the secondary to the sync source, then run sync
// source selection, because:
// 1) The sync source changes only after retrieving a batch and
// 2) The sync source won't change if the secondary isn't behind the expected sync source, as
//    determined by heartbeats.
function assertSyncSourceChangesTo(rst, secondary, expectedSyncSource) {
    // Insert a document while 'secondary' is not replicating to force it to run
    // shouldChangeSyncSource.
    stopServerReplication(secondary);
    assert.commandWorked(
        rst.getPrimary().getDB("testSyncSourceChangesDb").getCollection("coll").insert({a: 1}, {
            writeConcern: {w: 1}
        }));
    const sourceId = rst.getNodeId(expectedSyncSource);
    // Waits for the secondary to see the expected sync source advance beyond it.
    assert.soon(function() {
        const status = assert.commandWorked(secondary.adminCommand({replSetGetStatus: 1}));
        const appliedTimestamp = status.optimes.appliedOpTime.ts;
        const sourceMember = status.members.find((x) => x._id == sourceId);
        return timestampCmp(sourceMember.optime.ts, appliedTimestamp) > 0;
    });
    restartServerReplication(secondary);
    assertSyncSourceMatchesSoon(secondary, expectedSyncSource.host);
}

// Replication verbosity 2 includes the sync source change debug logs.
TestData["setParameters"]["logComponentVerbosity"]["replication"]["verbosity"] = 2;

// Start RST with only one voting node, node 0 -- this will be the only valid voting node and sync
// source
const rst = new ReplSetTest({nodes: [{}, {rsConfig: {priority: 0, votes: 0}}]});
rst.startSet();
rst.initiate();

// Make sure that node 0 is primary as expected
const primary = rst.getPrimary();
assert.eq(primary, rst.nodes[0]);

// Add a new voting node, node 2 -- voting nodes will choose voting nodes as sync sources.
jsTestLog("Adding node 2");
const newNode = rst.add({});
rst.reInitiate();
rst.waitForState(newNode, ReplSetTest.State.SECONDARY);
rst.awaitReplication();
rst.awaitSecondaryNodes();

// Assure that node 2 will set node 0 as its sync source, since it is the best option.
assertSyncSourceChangesTo(rst, newNode, rst.nodes[0]);

// Make node 1 a voter so that it will be a valid option for sync source
let cfg = rst.getReplSetConfigFromNode();
cfg.members[1].priority = 1;
cfg.members[1].votes = 1;
reconfig(rst, cfg);

// Force a stepup of node 1 -- we need to step node 0 down so that we can set it as a non-voter
// without causing errors.
jsTestLog("Stepping up node 1");
rst.stepUp(rst.nodes[1]);

jsTestLog("Reconfiguring node 0 as nonvoter");
// Make node 0 a nonvoter so that it will be an invalid option for sync source
cfg = rst.getReplSetConfigFromNode();
cfg.members[0].priority = 0;
cfg.members[0].votes = 0;
reconfig(rst, cfg);
jsTestLog("Reconfig complete");

assertSyncSourceChangesTo(rst, newNode, rst.nodes[1]);

rst.stopSet();
})();