summaryrefslogtreecommitdiff
path: root/jstests/replsets/initial_sync_fcv.js
blob: 29bea77d1454c51ba491f15d6fa77e12ba32736a (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
/**
 * Tests that setting the feature compatibility version during initial sync leads to initial sync
 * restarting. This test also ensures that even if initial sync takes two attempts to complete,
 * that the fCV is reset between attempts.
 *
 * This tests behavior centered around both upgrading and downgrading FCV.
 * @tags: [multiversion_incompatible]
 */

(function() {
'use strict';

load("jstests/libs/fail_point_util.js");

const rst = new ReplSetTest({nodes: 2});
rst.startSet();

// We disallow the secondary node from voting so that the primary's featureCompatibilityVersion
// can be modified while the secondary node is still waiting to complete its initial sync.
const replSetConfig = rst.getReplSetConfig();
replSetConfig.members[1].priority = 0;
replSetConfig.members[1].votes = 0;
rst.initiate(replSetConfig);

const primary = rst.getPrimary();
const dbName = 'foo';
const collName = 'bar';
// The default WC is majority and this test can't satisfy majority writes.
assert.commandWorked(primary.adminCommand(
    {setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}}));

assert.commandWorked(primary.getDB(dbName).getCollection(collName).insert({a: 1}));

function runInitialSync(cmd, initialFCV) {
    assert.commandWorked(primary.adminCommand({setFeatureCompatibilityVersion: initialFCV}));

    jsTestLog('Testing setting fCV with ' + tojson(cmd));

    const failPointOptions = tojson({
        mode: 'alwaysOn',
        data: {cloner: "DatabaseCloner", stage: "listCollections", database: dbName}
    });
    rst.restart(1, {
        startClean: true,
        setParameter: {
            'failpoint.hangBeforeClonerStage': failPointOptions,
            'failpoint.skipClearInitialSyncState': tojson({mode: 'alwaysOn'}),
            numInitialSyncAttempts: 2
        }
    });
    const secondary = rst.nodes[1];

    // Initial sync clones the 'admin' database first, which will set the fCV on the
    // secondary to initialFCV. We then block the secondary before issuing 'listCollections' on
    // the test database.
    assert.commandWorked(secondary.adminCommand({
        waitForFailPoint: "hangBeforeClonerStage",
        timesEntered: 1,
        maxTimeMS: kDefaultWaitForFailPointTimeout
    }));

    // Initial sync is stopped right before 'listCollections' on the test database. We now run
    // the test command to modify the fCV.
    assert.commandWorked(primary.adminCommand(cmd));

    // Let initial sync finish, making sure that it fails due to the feature compatibility
    // version change.
    assert.commandWorked(
        secondary.adminCommand({configureFailPoint: 'hangBeforeClonerStage', mode: 'off'}));
    checkLog.contains(secondary, 'Applying operation on feature compatibility version document');

    jsTestLog('Wait for both nodes to be up-to-date');
    rst.awaitSecondaryNodes();
    rst.awaitReplication();

    let res = assert.commandWorked(secondary.adminCommand({replSetGetStatus: 1}));
    assert.eq(res.initialSyncStatus.failedInitialSyncAttempts, 1);

    // We check oplogs and data hashes before we restart the second node.
    rst.checkOplogs();
    rst.checkReplicatedDataHashes();
}

// Ensure that attempting to downgrade the featureCompatibilityVersion during initial sync
// fails.
runInitialSync({setFeatureCompatibilityVersion: lastLTSFCV}, /*initialFCV*/ latestFCV);

// Ensure that attempting to upgrade the featureCompatibilityVersion during initial sync fails.
runInitialSync({setFeatureCompatibilityVersion: latestFCV}, /*initialFCV*/ lastLTSFCV);

// Modifications to the featureCompatibilityVersion document during initial sync should be
// caught and cause initial sync to fail.
runInitialSync({
    update: 'system.version',
    updates: [{q: {_id: 'featureCompatibilityVersion'}, u: {'version': lastLTSFCV}}]
},
               /*initialFCV*/ latestFCV);

rst.stopSet();
})();