mirror of https://github.com/mongodb/mongo
168 lines
7.1 KiB
JavaScript
168 lines
7.1 KiB
JavaScript
/**
|
|
* Fixture to test rollback permutations with index builds.
|
|
*/
|
|
|
|
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
|
import {IndexBuildTest} from "jstests/noPassthrough/libs/index_builds/index_build.js";
|
|
import {RollbackTest} from "jstests/replsets/libs/rollback_test.js";
|
|
|
|
export class RollbackIndexBuildsTest {
|
|
constructor(expectedErrors) {
|
|
jsTestLog("Set up a Rollback Test.");
|
|
const replTest = new ReplSetTest({
|
|
name: jsTestName(),
|
|
nodes: 3,
|
|
useBridge: true,
|
|
});
|
|
replTest.startSet();
|
|
let config = replTest.getReplSetConfig();
|
|
config.members[2].priority = 0;
|
|
config.settings = {chainingAllowed: false};
|
|
replTest.initiate(config);
|
|
this.rollbackTest = new RollbackTest(jsTestName(), replTest);
|
|
this.expectedErrors = expectedErrors;
|
|
}
|
|
|
|
// Given two ordered arrays, returns all permutations of the two using all elements of each.
|
|
static makeSchedules(rollbackOps, indexBuildOps) {
|
|
// Appends to the 'result' array all permutations of the interleavings between two ordered
|
|
// arrays.
|
|
function makeCombinations(listA, listB, result, accum = []) {
|
|
if (listA.length == 0 && listB.length == 0) {
|
|
result.push(accum);
|
|
accum = [];
|
|
}
|
|
|
|
if (listA.length) {
|
|
let copy = accum.slice();
|
|
copy.push(listA[0]);
|
|
makeCombinations(listA.slice(1), listB, result, copy);
|
|
}
|
|
if (listB.length) {
|
|
let copy = accum.slice();
|
|
copy.push(listB[0]);
|
|
makeCombinations(listA, listB.slice(1), result, copy);
|
|
}
|
|
}
|
|
|
|
let schedules = [];
|
|
// This function is exponential. Limit the number of operations to prevent generating
|
|
// extremely large schedules.
|
|
assert.lte(rollbackOps.length, 5);
|
|
assert.lte(indexBuildOps.length, 5);
|
|
makeCombinations(rollbackOps, indexBuildOps, schedules);
|
|
jsTestLog("Generated " + schedules.length + " schedules ");
|
|
return schedules;
|
|
}
|
|
|
|
runSchedules(schedules) {
|
|
const self = this;
|
|
let i = 0;
|
|
schedules.forEach(function (schedule) {
|
|
const collName = "coll_" + i;
|
|
const indexSpec = {a: 1};
|
|
|
|
jsTestLog("iteration: " + i + " collection: " + collName + " schedule: " + tojson(schedule));
|
|
|
|
const primary = self.rollbackTest.getPrimary();
|
|
const primaryDB = primary.getDB("test");
|
|
const collection = primaryDB.getCollection(collName);
|
|
|
|
let transitionedToSteadyState = false;
|
|
let createdColl = false;
|
|
let indexBuilds = [];
|
|
|
|
schedule.forEach(function (op) {
|
|
print("Running operation: " + op);
|
|
switch (op) {
|
|
case "holdStableTimestamp":
|
|
assert.commandWorked(
|
|
primary.adminCommand({configureFailPoint: "disableSnapshotting", mode: "alwaysOn"}),
|
|
);
|
|
break;
|
|
case "transitionToRollback": {
|
|
const curPrimary = self.rollbackTest.transitionToRollbackOperations();
|
|
assert.eq(curPrimary, primary);
|
|
break;
|
|
}
|
|
case "transitionToSteadyState":
|
|
self.rollbackTest.transitionToSyncSourceOperationsBeforeRollback();
|
|
|
|
// After transitioning to rollback, allow the index build to complete on
|
|
// the rolling-back node so that rollback can finish.
|
|
IndexBuildTest.resumeIndexBuilds(primary);
|
|
|
|
self.rollbackTest.transitionToSyncSourceOperationsDuringRollback();
|
|
|
|
// To speed up the test, defer data validation until the fixture shuts down.
|
|
self.rollbackTest.transitionToSteadyStateOperations({skipDataConsistencyChecks: true});
|
|
transitionedToSteadyState = true;
|
|
break;
|
|
case "createColl":
|
|
assert.commandWorked(collection.insert({a: "created collection explicitly"}));
|
|
createdColl = true;
|
|
break;
|
|
case "start":
|
|
if (!createdColl) {
|
|
assert.commandWorked(collection.insert({a: "created collection with start"}));
|
|
createdColl = true;
|
|
}
|
|
IndexBuildTest.pauseIndexBuilds(primary);
|
|
|
|
var errcodes = self.expectedErrors ? self.expectedErrors : [];
|
|
// This test creates indexes with majority of nodes not available for
|
|
// replication, so set index build commit quorum to 1.
|
|
indexBuilds.push(
|
|
IndexBuildTest.startIndexBuild(
|
|
primary,
|
|
collection.getFullName(),
|
|
indexSpec,
|
|
{},
|
|
errcodes,
|
|
1,
|
|
),
|
|
);
|
|
|
|
IndexBuildTest.waitForIndexBuildToScanCollection(primaryDB, collName, "a_1");
|
|
break;
|
|
case "commit":
|
|
IndexBuildTest.resumeIndexBuilds(primary);
|
|
IndexBuildTest.waitForIndexBuildToStop(primaryDB, collName, "a_1");
|
|
break;
|
|
case "abort": {
|
|
const opId = IndexBuildTest.getIndexBuildOpId(primaryDB, collName, "a_1");
|
|
assert.commandWorked(primaryDB.killOp(opId));
|
|
IndexBuildTest.resumeIndexBuilds(primary);
|
|
IndexBuildTest.waitForIndexBuildToStop(primaryDB, collName, "a_1");
|
|
break;
|
|
}
|
|
case "drop":
|
|
collection.dropIndexes(indexSpec);
|
|
break;
|
|
default:
|
|
assert(false, "unknown operation for test: " + op);
|
|
}
|
|
});
|
|
|
|
// Check for success -- any expected Error failures were passed
|
|
// as parameters to the startIndexBuild() call
|
|
indexBuilds.forEach((indexBuild) => indexBuild({checkExitSuccess: true}));
|
|
|
|
if (!transitionedToSteadyState) {
|
|
self.rollbackTest.transitionToSyncSourceOperationsBeforeRollback();
|
|
self.rollbackTest.transitionToSyncSourceOperationsDuringRollback();
|
|
|
|
// To speed up the test, defer data validation until the fixture shuts down.
|
|
self.rollbackTest.transitionToSteadyStateOperations({skipDataConsistencyChecks: true});
|
|
}
|
|
|
|
assert.commandWorked(primary.adminCommand({configureFailPoint: "disableSnapshotting", mode: "off"}));
|
|
i++;
|
|
});
|
|
}
|
|
|
|
stop() {
|
|
this.rollbackTest.stop();
|
|
}
|
|
}
|