mongo/jstests/replsets/libs/rollback_index_builds_test.js

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();
}
}