Files
mongo/jstests/multiVersion/genericSetFCVUsage/do_upgrade_downgrade.js
Steve McClure 1ffbc6c2e9 SERVER-109432: Autofix JS var usage to favor let (#40637)
GitOrigin-RevId: 9674b7db36a0f3f650d39c1e3fb2ad6ff2141cfb
2025-08-28 19:21:01 +00:00

372 lines
14 KiB
JavaScript

// Perform the upgrade/downgrade procedure by first setting the featureCompatibilityVersion and
// then switching the binary.
import {checkCollectionUUIDs} from "jstests/libs/check_uuids.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {checkUniqueIndexFormatVersion} from "jstests/libs/unique_indexes/check_unique_indexes.js";
const latestBinary = "latest";
let setFCV = function (adminDB, version) {
assert.commandWorked(
adminDB.runCommand({setFeatureCompatibilityVersion: version, confirm: true, writeConcern: {w: 1}}),
);
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, {writeConcern: {w: 1}}));
}
}
}
// 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") {
assert.commandWorked(
testDB.runCommand({
createIndexes: "foo",
indexes: [{key: {id: 1}, name: "id_1", unique: true}],
writeConcern: {w: 1},
}),
);
assert.commandWorked(
testDB.runCommand({
createIndexes: "foo",
indexes: [{key: {sno: 1}, name: "sno_1", unique: true, v: 1}],
writeConcern: {w: 1},
}),
);
}
});
}
};
let recreateUniqueIndexes = function (db, secondary) {
// Obtain list of all v1 and v2 unique indexes
let unique_idx = [];
let 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 || spec.clustered) {
return;
}
if (spec.v === 1) {
unique_idx_v1.push({dbName: d.name, collName: c.name, spec: spec});
} else {
unique_idx.push({dbName: d.name, collName: c.name, spec: spec});
}
});
});
});
// Drop and create all v:2 indexes
for (let pair of unique_idx) {
const idx = pair.spec;
const dbName = pair.dbName;
const collName = pair.collName;
let res = db.getSiblingDB(dbName).runCommand({dropIndexes: collName, index: idx.name, writeConcern: {w: 1}});
assert.commandWorked(res);
res = db.getSiblingDB(dbName).runCommand({
createIndexes: collName,
indexes: [{"key": idx.key, "name": idx.name, "unique": true}],
writeConcern: {w: 1},
});
assert.commandWorked(res);
}
// Drop and create all v:1 indexes
for (let pair of unique_idx_v1) {
const idx = pair.spec;
const dbName = pair.dbName;
const collName = pair.collName;
let res = db.getSiblingDB(dbName).runCommand({dropIndexes: collName, index: idx.name, writeConcern: {w: 1}});
assert.commandWorked(res);
res = db.getSiblingDB(dbName).runCommand({
createIndexes: collName,
indexes: [{"key": idx.key, "name": idx.name, "unique": true, "v": 1}],
writeConcern: {w: 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);
} else {
checkFCV(adminDB, lastLTSFCV);
// Transitioning from last-lts to last-continuous is only allowed when
// setFeatureCompatibilityVersion is called with fromConfigServer: true.
assert.commandWorked(
adminDB.runCommand({
setFeatureCompatibilityVersion: downgradeFCV,
confirm: true,
fromConfigServer: true,
writeConcern: {w: 1},
}),
);
checkFCV(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, numNodes = 3) {
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: numNodes, 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);
} else {
checkFCV(primaryAdminDB, lastLTSFCV);
// Transitioning from last-lts to last-continuous is only allowed when
// setFeatureCompatibilityVersion is called with fromConfigServer: true.
assert.commandWorked(
primaryAdminDB.runCommand({
setFeatureCompatibilityVersion: downgradeFCV,
confirm: true,
fromConfigServer: true,
writeConcern: {w: 1},
}),
);
}
// Ensure featureCompatibilityVersion is 'downgradeVersion' and all collections still have
// UUIDs.
checkFCV(primaryAdminDB, downgradeFCV);
// Ensure all replica set members have finished setting the FCV before checking secondaries.
rst.awaitReplication();
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 replica sets started with --shardsvr.
replicaSetTest({shardsvr: ""}, "last-continuous");
replicaSetTest({shardsvr: ""}, "last-lts");
// Do tests for replica sets started with --configsvr.
replicaSetTest({configsvr: ""}, "last-continuous");
replicaSetTest({configsvr: ""}, "last-lts");