summaryrefslogtreecommitdiff
path: root/jstests/multiVersion/genericSetFCVUsage/do_upgrade_downgrade.js
blob: e5888c675653f400d91df11edafd95aab46ebdfc (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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
// Perform the upgrade/downgrade procedure by first setting the featureCompatibilityVersion and
// then switching the binary.
(function() {
"use strict";

load("jstests/replsets/rslib.js");
load("jstests/libs/get_index_helpers.js");
load("jstests/libs/check_uuids.js");
load("jstests/libs/check_unique_indexes.js");

const latestBinary = "latest";

let setFCV = function(adminDB, version) {
    assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: version}));
    checkFCV(adminDB, version);
};

let insertDataForConn = function(conn, dbs, nodeOptions) {
    for (let i = 0; i < 20; i++) {
        let doc = {id: i, sno: i, a: "foo", conn: conn.name};
        for (let j in dbs) {
            if (nodeOptions.hasOwnProperty("configsvr")) {
                if (j !== "admin" && j !== "local") {
                    // We can't create user databases on a --configsvr instance.
                    continue;
                }
                // Config servers have a majority write concern.
                assert.commandWorked(
                    conn.getDB(dbs[j]).foo.insert(doc, {writeConcern: {w: "majority"}}));
            } else {
                assert.commandWorked(conn.getDB(dbs[j]).foo.insert(doc));
            }
        }
    }

    // Create unique indexes on collection "foo" with two index formatVersions.
    // Providing index version explicitly allows index creation with corresponding
    // formatVersion.
    for (let j in dbs) {
        let testDB = conn.getDB(dbs[j]);
        testDB.getCollectionInfos().forEach(function(c) {
            if (c.name === "foo") {
                let foo = testDB.getCollection(c.name);
                assert.commandWorked(foo.createIndex({id: 1}, {unique: true}));
                assert.commandWorked(foo.createIndex({sno: 1}, {unique: true, v: 1}));
            }
        });
    }
};

let recreateUniqueIndexes = function(db, secondary) {
    // Obtain list of all v1 and v2 unique indexes
    var unique_idx = [];
    var unique_idx_v1 = [];
    db.adminCommand("listDatabases").databases.forEach(function(d) {
        if (secondary && !(d.name === "local")) {
            // All replicated indexes will be dropped on the primary, and have that
            // drop propogated. Secondary nodes need to recreate unique indexes
            // associated with local collections.
            return;
        }
        let mdb = db.getSiblingDB(d.name);
        mdb.getCollectionInfos().forEach(function(c) {
            let currentCollection = mdb.getCollection(c.name);
            if (c.type == "view") {
                return;
            }

            currentCollection.getIndexes().forEach(function(spec) {
                if (!spec.unique) {
                    return;
                }

                const ns = d.name + "." + c.name;
                if (spec.v === 1) {
                    unique_idx_v1.push({ns: ns, spec: spec});
                } else {
                    unique_idx.push({ns: ns, spec: spec});
                }
            });
        });
    });

    // Drop and create all v:2 indexes
    for (let pair of unique_idx) {
        const ns = pair.ns;
        const idx = pair.spec;
        let [dbName, collName] = ns.split(".");
        let res = db.getSiblingDB(dbName).runCommand({dropIndexes: collName, index: idx.name});
        assert.commandWorked(res);
        res = db.getSiblingDB(dbName).runCommand({
            createIndexes: collName,
            indexes: [{"key": idx.key, "name": idx.name, "unique": true}]
        });
        assert.commandWorked(res);
    }

    // Drop and create all v:1 indexes
    for (let pair of unique_idx_v1) {
        const ns = pair.ns;
        const idx = pair.spec;
        let [dbName, collName] = ns.split(".");
        let res = db.getSiblingDB(dbName).runCommand({dropIndexes: collName, index: idx.name});
        assert.commandWorked(res);
        res = db.getSiblingDB(dbName).runCommand({
            createIndexes: collName,
            indexes: [{"key": idx.key, "name": idx.name, "unique": true, "v": 1}]
        });
        assert.commandWorked(res);
    }
};

// Create and clear dbpath
let sharedDbPath = MongoRunner.dataPath + "do_upgrade_downgrade";
resetDbpath(sharedDbPath);

// Return a mongodb connection with startup options, version and dbpath options
let startMongodWithVersion = function(nodeOptions, ver, path) {
    let version = ver || latestBinary;
    let dbpath = path || sharedDbPath;
    let conn = MongoRunner.runMongod(
        Object.assign({}, nodeOptions, {dbpath: dbpath, binVersion: version}));
    assert.neq(null,
               conn,
               "mongod was unable to start up with version=" + version + " and path=" + dbpath);
    return conn;
};

//
// Standalone tests.
//
let standaloneTest = function(nodeOptions, downgradeVersion) {
    jsTestLog("Running standalone test with 'downgradeVersion': " + downgradeVersion);
    const downgradeFCV = binVersionToFCV(downgradeVersion);
    let noCleanDataOptions = Object.assign({noCleanData: true}, nodeOptions);

    // New latest binary version standalone.
    jsTest.log("Starting a latest binVersion standalone");
    let conn = startMongodWithVersion(nodeOptions, latestBinary);
    let adminDB = conn.getDB("admin");

    // Insert some data.
    insertDataForConn(conn, ["admin", "local", "test"], nodeOptions);

    if (!nodeOptions.hasOwnProperty("shardsvr")) {
        // Initially featureCompatibilityVersion is latest except for when we run with shardsvr.
        // We expect featureCompatibilityVersion to be downgradeFCV for shardsvr.
        checkFCV(adminDB, latestFCV);

        // Ensure all collections have UUIDs and all unique indexes have new version in latest
        // featureCompatibilityVersion mode.
        checkCollectionUUIDs(adminDB);
        checkUniqueIndexFormatVersion(adminDB);

        setFCV(adminDB, downgradeFCV);
    }

    // Ensure featureCompatibilityVersion is downgraded and all collections still have UUIDs.
    checkFCV(adminDB, downgradeFCV);
    checkCollectionUUIDs(adminDB);

    // Drop and recreate unique indexes with the older FCV
    recreateUniqueIndexes(adminDB, false);

    // Stop latest binary version mongod.
    MongoRunner.stopMongod(conn);

    // Start the downgraded binary version mongod with same dbpath
    jsTest.log("Starting a downgraded binVersion standalone to test downgrade");
    let downgradedConn = startMongodWithVersion(noCleanDataOptions, downgradeVersion);
    let downgradedAdminDB = downgradedConn.getDB("admin");

    // Check FCV document.
    checkFCV(downgradedAdminDB, downgradeFCV);

    // Ensure all collections still have UUIDs on a downgraded mongod.
    checkCollectionUUIDs(downgradedAdminDB);

    // Stop downgraded binary version mongod.
    MongoRunner.stopMongod(downgradedConn);

    // Start latest binary version mongod again.
    jsTest.log("Starting a latest binVersion standalone to test upgrade");
    conn = startMongodWithVersion(noCleanDataOptions, latestBinary);
    adminDB = conn.getDB("admin");

    // Ensure setFeatureCompatibilityVersion to latest succeeds, all collections have UUIDs
    // and all unique indexes are in new version.
    setFCV(adminDB, latestFCV);
    checkFCV(adminDB, latestFCV);
    checkCollectionUUIDs(adminDB);
    checkUniqueIndexFormatVersion(adminDB);

    // Stop latest binary version mongod for the last time
    MongoRunner.stopMongod(conn);
};

//
// Replica set tests.
//
let replicaSetTest = function(nodeOptions, downgradeVersion) {
    jsTestLog("Running replica set test with 'downgradeVersion': " + downgradeVersion);
    const downgradeFCV = binVersionToFCV(downgradeVersion);
    // New latest binary version replica set.
    jsTest.log("Starting a latest binVersion ReplSetTest");
    let rst = new ReplSetTest({nodes: 3, nodeOptions: nodeOptions});
    rst.startSet();
    rst.initiate();
    let primaryAdminDB = rst.getPrimary().getDB("admin");
    let secondaries = rst.getSecondaries();

    // Insert some data.
    insertDataForConn(rst.getPrimary(), ["admin", "local", "test"], nodeOptions);
    rst.awaitReplication();

    for (let j = 0; j < secondaries.length; j++) {
        let secondaryAdminDB = secondaries[j].getDB("admin");
        // Insert some data into the local DB.
        insertDataForConn(secondaries[j], ["local"], nodeOptions);
    }

    if (!nodeOptions.hasOwnProperty("shardsvr")) {
        // Initially featureCompatibilityVersion is latest on primary and secondaries except for
        // when we run with shardsvr. We expect featureCompatibilityVersion to be 'downgradeVersion'
        // for shardsvr.
        checkFCV(primaryAdminDB, latestFCV);

        for (let j = 0; j < secondaries.length; j++) {
            let secondaryAdminDB = secondaries[j].getDB("admin");
            checkFCV(secondaryAdminDB, latestFCV);
        }

        // Ensure all collections have UUIDs and unique indexes are in new version in latest
        // featureCompatibilityVersion mode on both primary and secondaries.
        checkCollectionUUIDs(primaryAdminDB);
        checkUniqueIndexFormatVersion(primaryAdminDB);
        for (let j = 0; j < secondaries.length; j++) {
            let secondaryAdminDB = secondaries[j].getDB("admin");
            checkCollectionUUIDs(secondaryAdminDB);
            checkUniqueIndexFormatVersion(secondaryAdminDB);
        }

        // Change featureCompatibilityVersion to downgradeFCV.
        setFCV(primaryAdminDB, downgradeFCV);
        rst.awaitReplication();
    }

    // Ensure featureCompatibilityVersion is 'downgradeVersion' and all collections still have
    // UUIDs.
    checkFCV(primaryAdminDB, downgradeFCV);
    for (let j = 0; j < secondaries.length; j++) {
        let secondaryAdminDB = secondaries[j].getDB("admin");
        checkFCV(secondaryAdminDB, downgradeFCV);
    }

    checkCollectionUUIDs(primaryAdminDB);
    for (let j = 0; j < secondaries.length; j++) {
        let secondaryAdminDB = secondaries[j].getDB("admin");
        checkCollectionUUIDs(secondaryAdminDB);
    }

    // Drop and recreate unique indexes with the older FCV
    recreateUniqueIndexes(primaryAdminDB, false);

    // Now drop and recreate unique indexes on secondaries' "local" database
    for (let j = 0; j < secondaries.length; j++) {
        let secondaryAdminDB = secondaries[j].getDB("admin");
        recreateUniqueIndexes(secondaryAdminDB, true);
    }

    // Stop latest binary version replica set.
    rst.stopSet(null /* signal */, true /* forRestart */);

    // Downgrade the ReplSetTest binaries and make sure everything is okay.
    jsTest.log("Starting a " + downgradeVersion + " binVersion ReplSetTest to test downgrade");
    rst.startSet({restart: true, binVersion: downgradeVersion});

    // Check that the featureCompatiblityVersion is set to downgradeFCV and all
    // collections still have UUIDs.
    let downgradedPrimaryAdminDB = rst.getPrimary().getDB("admin");
    let downgradedSecondaries = rst.getSecondaries();

    checkFCV(downgradedPrimaryAdminDB, downgradeFCV);
    for (let j = 0; j < downgradedSecondaries.length; j++) {
        let secondaryAdminDB = downgradedSecondaries[j].getDB("admin");
        checkFCV(secondaryAdminDB, downgradeFCV);
    }

    checkCollectionUUIDs(downgradedPrimaryAdminDB);
    for (let j = 0; j < secondaries.length; j++) {
        let secondaryAdminDB = downgradedSecondaries[j].getDB("admin");
        checkCollectionUUIDs(secondaryAdminDB);
    }

    rst.stopSet(null /* signal */, true /* forRestart */);

    // Start latest binary version replica set again.
    jsTest.log("Starting a latest binVersion ReplSetTest to test upgrade");
    rst.startSet({restart: true, binVersion: latestBinary});
    primaryAdminDB = rst.getPrimary().getDB("admin");
    secondaries = rst.getSecondaries();

    // Ensure all collections have UUIDs and unique indexes are in new version after switching
    // back to latest featureCompatibilityVersion on both primary and secondaries.
    setFCV(primaryAdminDB, latestFCV);
    rst.awaitReplication();

    checkFCV(primaryAdminDB, latestFCV);
    for (let j = 0; j < secondaries.length; j++) {
        let secondaryAdminDB = secondaries[j].getDB("admin");
        checkFCV(secondaryAdminDB, latestFCV);
    }

    checkCollectionUUIDs(primaryAdminDB);
    checkUniqueIndexFormatVersion(primaryAdminDB);
    for (let j = 0; j < secondaries.length; j++) {
        let secondaryAdminDB = secondaries[j].getDB("admin");
        checkCollectionUUIDs(secondaryAdminDB);
        checkUniqueIndexFormatVersion(secondaryAdminDB);
    }

    rst.stopSet();
};

// Do tests for regular standalones and replica sets.
standaloneTest({}, 'last-continuous');
standaloneTest({}, 'last-lts');
replicaSetTest({}, 'last-continuous');
replicaSetTest({}, 'last-lts');

// Do tests for standalones and replica sets started with --shardsvr.
standaloneTest({shardsvr: ""}, 'last-continuous');
standaloneTest({shardsvr: ""}, 'last-lts');
replicaSetTest({shardsvr: ""}, 'last-continuous');
replicaSetTest({shardsvr: ""}, 'last-lts');

// Do tests for standalones and replica sets started with --configsvr.
standaloneTest({configsvr: ""}, 'last-continuous');
standaloneTest({configsvr: ""}, 'last-lts');
replicaSetTest({configsvr: ""}, 'last-continuous');
standaloneTest({configsvr: ""}, 'last-lts');
})();