mongo/jstests/sharding/resharding_prohibited_comma...

197 lines
7.6 KiB
JavaScript

/**
* Tests that chunk migrations, collMod, createIndexes, and dropIndexes are prohibited on a
* collection that is undergoing a resharding operation. Also tests that concurrent resharding
* operations are prohibited.
*
* @tags: [
* uses_atclustertime,
* ]
*/
import {DiscoverTopology} from "jstests/libs/discover_topology.js";
import {funWithArgs} from "jstests/libs/parallel_shell_helpers.js";
import {ReshardingTest} from "jstests/sharding/libs/resharding_test_fixture.js";
const databaseName = "reshardingDb";
const collectionName = "coll";
const otherDatabaseName = "reshardingDb2";
const otherCollectionName = "coll2";
const otherNamespace = `${otherDatabaseName}.${otherCollectionName}`;
const donorOperationNamespace = "config.localReshardingOperations.donor";
const indexCreatedByTest = {
newKey: 1,
oldKey: 1,
};
const indexDroppedByTest = {
x: 1,
};
// TODO SERVER-108241 Re-enable dropIndexes once investigation is done.
const prohibitedCommands = [
{collMod: collectionName},
{createIndexes: collectionName, indexes: [{name: "idx1", key: indexCreatedByTest}]},
// {dropIndexes: collectionName, index: indexDroppedByTest},
];
/**
* @summary Goes through each of the commands specified in the prohibitedCommands array, executes
* the command against the provided database and asserts that the command succeeded.
* @param {*} database
*/
const assertCommandsSucceedAfterReshardingOpFinishes = (database) => {
prohibitedCommands.forEach((command) => {
jsTest.log(`Testing that ${tojson(command)} succeeds after the resharding operation finishes.`);
assert.commandWorked(database.runCommand(command));
});
};
/**
* @summary Goes through each of the commands specified in the prohibitedCommands array,
* executes the commands and asserts that the command failed with ReshardCollectionInProgress.
* - In order for the dropIndexes command to fail correctly, the index its attempting to drop must
* exist.
* - In order for the createIndexes command to fail correctly, the index its attempting to create
* must not exist already.
* @param {*} database
*/
const assertCommandsFailDuringReshardingOp = (database) => {
prohibitedCommands.forEach((command) => {
jsTest.log(`Testing that ${tojson(command)} fails during resharding operation`);
// The collMod is serialized with the resharding command, so we explicitly add an timeout to
// the command so that it doesn't get blocked and timeout the test.
if (command.hasOwnProperty("collMod") || command.hasOwnProperty("dropIndexes")) {
command = Object.assign({}, command);
command.maxTimeMS = 5000;
}
assert.commandFailedWithCode(database.runCommand(command), [
ErrorCodes.ReshardCollectionInProgress,
ErrorCodes.MaxTimeMSExpired,
]);
});
};
const reshardingTest = new ReshardingTest({numDonors: 2});
reshardingTest.setup();
const donorShardNames = reshardingTest.donorShardNames;
const recipientShardNames = reshardingTest.recipientShardNames;
const sourceCollection = reshardingTest.createShardedCollection({
ns: `${databaseName}.${collectionName}`,
shardKeyPattern: {oldKey: 1},
chunks: [
{min: {oldKey: MinKey}, max: {oldKey: 0}, shard: donorShardNames[0]},
{min: {oldKey: 0}, max: {oldKey: MaxKey}, shard: donorShardNames[1]},
],
});
const mongos = sourceCollection.getMongo();
const topology = DiscoverTopology.findConnectedNodes(mongos);
const sourceNamespace = sourceCollection.getFullName();
const waitUntilReshardingInitializedOnDonor = () => {
const donor = new Mongo(topology.shards[donorShardNames[0]].primary);
assert.soon(() => {
let res = donor.getCollection(donorOperationNamespace).find().toArray();
return res.length === 1;
}, "timed out waiting for resharding initialization on donor shard");
};
/**
* @summary The function that gets passed into reshardingTest.withReshardingInBackground
* @callback DuringReshardingCallback
* @param {String} tempNs - The temporary namespace used during resharding operations.
*/
/**
* @summary A function that defines the surrounding environment for the tests surrounding the
* prohibited commands during resharding. It sets up the resharding configuration and asserts that
* the prohibited commands succeed once the resharding operation has completed.
* @param {DuringReshardingCallback} duringReshardingFn
* @param {Object} config
* @param {number} config.expectedErrorCode
* @param {Function} config.setup
* @param {AfterReshardingCallback} afterReshardingFn
*/
const withReshardingInBackground = (
duringReshardingFn,
{setup = () => {}, expectedErrorCode, afterReshardingFn = () => {}} = {},
) => {
assert.commandWorked(sourceCollection.createIndex(indexDroppedByTest));
setup();
reshardingTest.withReshardingInBackground(
{
newShardKeyPattern: {newKey: 1},
newChunks: [{min: {newKey: MinKey}, max: {newKey: MaxKey}, shard: recipientShardNames[0]}],
},
duringReshardingFn,
{expectedErrorCode: expectedErrorCode, afterReshardingFn: afterReshardingFn},
);
assertCommandsSucceedAfterReshardingOpFinishes(mongos.getDB(databaseName));
assert.commandWorked(sourceCollection.dropIndex(indexCreatedByTest));
};
// Tests that the prohibited commands work if the resharding operation is aborted.
let awaitAbort;
withReshardingInBackground(
() => {
waitUntilReshardingInitializedOnDonor();
assert.neq(null, mongos.getCollection("config.reshardingOperations").findOne({ns: sourceNamespace}));
awaitAbort = startParallelShell(
funWithArgs(function (sourceNamespace) {
db.adminCommand({abortReshardCollection: sourceNamespace});
}, sourceNamespace),
mongos.port,
);
// Wait for the coordinator to remove coordinator document from config.reshardingOperations
// as a result of the recipients and donors transitioning to done due to abort.
assert.soon(() => {
const coordinatorDoc = mongos.getCollection("config.reshardingOperations").findOne({ns: sourceNamespace});
return coordinatorDoc === null || coordinatorDoc.state === "aborting";
});
},
{
expectedErrorCode: ErrorCodes.ReshardCollectionAborted,
},
);
awaitAbort();
// Tests that the prohibited commands succeed if the resharding operation succeeds. During the
// operation it makes sure that the prohibited commands are rejected during the resharding
// operation.
withReshardingInBackground(
() => {
waitUntilReshardingInitializedOnDonor();
jsTest.log("About to test that the admin commands do not work while the resharding operation is in progress.");
assert.commandFailedWithCode(
mongos.adminCommand({moveChunk: sourceNamespace, find: {oldKey: -10}, to: donorShardNames[1]}),
[ErrorCodes.ConflictingOperationInProgress],
);
assert.commandFailedWithCode(
mongos.adminCommand({reshardCollection: otherNamespace, key: {newKey: 1}}),
ErrorCodes.ReshardCollectionInProgress,
);
assertCommandsFailDuringReshardingOp(sourceCollection.getDB());
},
{
setup: () => {
assert.commandWorked(mongos.adminCommand({enableSharding: otherDatabaseName}));
assert.commandWorked(mongos.adminCommand({shardCollection: otherNamespace, key: {oldKey: 1}}));
},
afterReshardingFn: () => {
jsTest.log("Join possible ongoing collMod command");
assert.commandWorked(sourceCollection.runCommand("collMod"));
},
},
);
reshardingTest.teardown();