mirror of https://github.com/mongodb/mongo
197 lines
7.6 KiB
JavaScript
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();
|