Skip to content

Commit

Permalink
SERVER-60694 Move collMod to DDL coordinator infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
lriuui0x0 authored and Evergreen Agent committed Dec 22, 2021
1 parent d269dd5 commit d6f2b64
Show file tree
Hide file tree
Showing 41 changed files with 949 additions and 183 deletions.
15 changes: 9 additions & 6 deletions jstests/concurrency/fsm_workloads/agg_out.js
Expand Up @@ -107,14 +107,17 @@ var $config = extendWorkload($config, function($config, $super) {
// Change the validation level.
const validationLevels = ['off', 'strict', 'moderate'];
const newValidationLevel = validationLevels[Random.randInt(validationLevels.length)];
assertWhenOwnDB.commandWorked(
db.runCommand({collMod: this.outputCollName, validationLevel: newValidationLevel}));
assertWhenOwnDB.commandWorkedOrFailedWithCode(
db.runCommand({collMod: this.outputCollName, validationLevel: newValidationLevel}),
ErrorCodes.ConflictingOperationInProgress);
} else {
// Change the validation action.
assertWhenOwnDB.commandWorked(db.runCommand({
collMod: this.outputCollName,
validationAction: Random.rand() > 0.5 ? 'warn' : 'error'
}));
assertWhenOwnDB.commandWorkedOrFailedWithCode(
db.runCommand({
collMod: this.outputCollName,
validationAction: Random.rand() > 0.5 ? 'warn' : 'error'
}),
ErrorCodes.ConflictingOperationInProgress);
}
};

Expand Down
18 changes: 10 additions & 8 deletions jstests/concurrency/fsm_workloads/collmod.js
Expand Up @@ -23,21 +23,23 @@ var $config = (function() {
collMod: this.threadCollName,
index: {keyPattern: {createdAt: 1}, expireAfterSeconds: newTTL}
});
assertAlways.commandWorked(res);
assertAlways.commandWorkedOrFailedWithCode(res,
[ErrorCodes.ConflictingOperationInProgress]);
// only assert if new expireAfterSeconds differs from old one
if (res.hasOwnProperty('expireAfterSeconds_new')) {
if (res.ok === 1 && res.hasOwnProperty('expireAfterSeconds_new')) {
assertWhenOwnDB.eq(res.expireAfterSeconds_new, newTTL);
}

// Attempt an invalid collMod which should always fail regardless of whether a WCE
// occurred. This is meant to reproduce SERVER-56772.
const encryptSchema = {$jsonSchema: {properties: {_id: {encrypt: {}}}}};
assertAlways.commandFailedWithCode(db.runCommand({
collMod: this.threadCollName,
validator: encryptSchema,
validationAction: "warn"
}),
ErrorCodes.QueryFeatureNotAllowed);
assertAlways.commandFailedWithCode(
db.runCommand({
collMod: this.threadCollName,
validator: encryptSchema,
validationAction: "warn"
}),
[ErrorCodes.ConflictingOperationInProgress, ErrorCodes.QueryFeatureNotAllowed]);
}

return {collMod: collMod};
Expand Down
Expand Up @@ -90,7 +90,8 @@ var $config = (function() {
const acceptableCodes = [
ErrorCodes.Interrupted,
ErrorCodes.DuplicateKey,
ErrorCodes.BackgroundOperationInProgressForNamespace
ErrorCodes.BackgroundOperationInProgressForNamespace,
ErrorCodes.LockBusy,
];
if (e.code && acceptableCodes.includes(e.code) ||
// Indexes may be transiently inconsistent across shards, which can lead a
Expand Down Expand Up @@ -167,7 +168,8 @@ var $config = (function() {
collMod: this.collName,
index: {keyPattern: indexToModify, expireAfterSeconds: data.expireAfterSeconds}
});
assertAlways.commandWorked(result);
assertAlways.commandWorkedOrFailedWithCode(result,
ErrorCodes.ConflictingOperationInProgress);
},

// Verify that the indexes that we expect to be on disk are actually there and that indexes
Expand Down
14 changes: 12 additions & 2 deletions jstests/concurrency/fsm_workloads/view_catalog_cycle_lookup.js
Expand Up @@ -96,7 +96,12 @@ var $config = (function() {
const toName = this.getRandomView(this.viewList);
const res = db.runCommand(
{collMod: fromName, viewOn: toName, pipeline: this.getRandomViewPipeline()});
assertAlways(res.ok === 1 || res.code === ErrorCodes.GraphContainsCycle, tojson(res));
assertAlways(res.ok === 1 ||
[
ErrorCodes.GraphContainsCycle,
ErrorCodes.ConflictingOperationInProgress
].includes(res.code),
tojson(res));
}

/**
Expand All @@ -112,7 +117,12 @@ var $config = (function() {
const fromName = this.getRandomView(this.viewList);
const res = db.runCommand(
{collMod: fromName, viewOn: collName, pipeline: this.getRandomViewPipeline()});
assertAlways(res.ok === 1 || res.code === ErrorCodes.GraphContainsCycle, tojson(res));
assertAlways(res.ok === 1 ||
[
ErrorCodes.GraphContainsCycle,
ErrorCodes.ConflictingOperationInProgress
].includes(res.code),
tojson(res));
}

function readFromView(db, collName) {
Expand Down
Expand Up @@ -31,7 +31,11 @@ var $config = (function() {
const toName = this.getRandomView(this.viewList);
const cmd = {collMod: fromName, viewOn: toName, pipeline: []};
const res = db.runCommand(cmd);
const errorCodes = [ErrorCodes.GraphContainsCycle, ErrorCodes.NamespaceNotFound];
const errorCodes = [
ErrorCodes.GraphContainsCycle,
ErrorCodes.NamespaceNotFound,
ErrorCodes.ConflictingOperationInProgress
];
assertAlways.commandWorkedOrFailedWithCode(
res, errorCodes, () => `cmd: ${tojson(cmd)}`);
}
Expand Down
27 changes: 5 additions & 22 deletions jstests/core/collmod_convert_to_unique_violations.js
Expand Up @@ -30,23 +30,6 @@ if (!collModIndexUniqueEnabled) {
return;
}

function extractResult(obj) {
if (!FixtureHelpers.isMongos(db)) {
return obj;
}

let numFields = 0;
let result = null;
for (let field in obj.raw) {
result = obj.raw[field];
numFields++;
}

assert.neq(null, result);
assert.eq(1, numFields);
return result;
}

function sortViolationsArray(arr) {
// Sorting unsorted arrays of unsorted arrays -- Sort subarrays, then sort main array by first
// key of subarray.
Expand All @@ -66,11 +49,11 @@ function sortViolationsArray(arr) {

// Checks that the violations match what we expect.
function assertFailedWithViolations(result, violations) {
const error = extractResult(result);
assert.commandFailedWithCode(error, ErrorCodes.CannotEnableIndexConstraint);
assert.eq(bsonWoCompare(sortViolationsArray(error.violations), sortViolationsArray(violations)),
0,
tojson(error));
assert.commandFailedWithCode(result, ErrorCodes.CannotEnableIndexConstraint);
assert.eq(
bsonWoCompare(sortViolationsArray(result.violations), sortViolationsArray(violations)),
0,
tojson(result));
}

const collName = 'collmod_convert_to_unique_violations';
Expand Down
4 changes: 3 additions & 1 deletion jstests/core/hidden_index.js
Expand Up @@ -98,7 +98,9 @@ assert.eq(idxSpec.hidden, true);
// Can't hide any index in a system collection.
const systemColl = db.getSiblingDB('admin').system.version;
assert.commandWorked(systemColl.createIndex({a: 1}));
assert.commandFailedWithCode(systemColl.hideIndex("a_1"), 2);
assert.commandFailedWithCode(
systemColl.hideIndex("a_1"),
FixtureHelpers.isMongos(db) ? ErrorCodes.NoShardingEnabled : ErrorCodes.BadValue);
assert.commandFailedWithCode(systemColl.createIndex({a: 1}, {hidden: true}), 2);

// Can't hide the '_id' index.
Expand Down
2 changes: 2 additions & 0 deletions jstests/core/views/views_all_commands.js
Expand Up @@ -153,6 +153,8 @@ let viewsCommandTests = {
_shardsvrSetAllowMigrations: {skip: isAnInternalCommand},
_shardsvrShardCollection:
{skip: isAnInternalCommand}, // TODO SERVER-58843: Remove once 6.0 becomes last LTS
_shardsvrCollMod: {skip: isAnInternalCommand},
_shardsvrCollModParticipant: {skip: isAnInternalCommand},
_transferMods: {skip: isAnInternalCommand},
_vectorClockPersist: {skip: isAnInternalCommand},
abortReshardCollection: {skip: isUnrelated},
Expand Down
50 changes: 50 additions & 0 deletions jstests/multiVersion/sharded_timeseries_collmod_mixed_version.js
@@ -0,0 +1,50 @@
/**
* Tests latest shards can process collMod on time-series collection sent from old 5.0 mongos.
*/

(function() {
"use strict";

const dbName = 'testDB';
const collName = 'testColl';
const timeField = 'tm';
const metaField = 'mt';
const indexName = 'index';
const viewNss = `${dbName}.${collName}`;

const st = new ShardingTest(
{shards: 2, rs: {nodes: 3}, mongos: [{binVersion: 'latest'}, {binVersion: '5.0'}]});
const mongos = st.s0;
const db = mongos.getDB(dbName);

assert.commandWorked(
mongos.adminCommand({setFeatureCompatibilityVersion: binVersionToFCV('latest')}));

assert.commandWorked(
db.createCollection(collName, {timeseries: {timeField: timeField, metaField: metaField}}));
assert.commandWorked(mongos.adminCommand({enableSharding: dbName}));
assert.commandWorked(db[collName].createIndex({[metaField]: 1}, {name: indexName}));
assert.commandWorked(mongos.adminCommand({
shardCollection: viewNss,
key: {[metaField]: 1},
}));

assert.commandWorked(mongos.adminCommand({setFeatureCompatibilityVersion: '5.0'}));

const oldDb = st.s1.getDB(dbName);
// Assert that collMod works with matching versions of mongos and mongod.
assert.commandWorked(db.runCommand({collMod: collName, index: {name: indexName, hidden: true}}));
// Assert that collMod still works with old version of mongos.
assert.commandWorked(
oldDb.runCommand({collMod: collName, index: {name: indexName, hidden: false}}));

// Assert that collMod with granularity update fails with matching versions of mongos and mongod.
assert.commandFailedWithCode(db.runCommand({collMod: collName, timeseries: {granularity: 'hours'}}),
ErrorCodes.NotImplemented);
// Assert that collMod with granularity update still fails with old version of mongos.
assert.commandFailedWithCode(
oldDb.runCommand({collMod: collName, timeseries: {granularity: 'hours'}}),
ErrorCodes.NotImplemented);

st.stop();
})();
2 changes: 2 additions & 0 deletions jstests/replsets/db_reads_while_recovering_all_commands.js
Expand Up @@ -88,6 +88,8 @@ const allCommands = {
_shardsvrReshardingOperationTime: {skip: isPrimaryOnly},
_shardsvrRefineCollectionShardKey: {skip: isPrimaryOnly},
_shardsvrSetAllowMigrations: {skip: isPrimaryOnly},
_shardsvrCollMod: {skip: isPrimaryOnly},
_shardsvrCollModParticipant: {skip: isAnInternalCommand},
_transferMods: {skip: isPrimaryOnly},
_vectorClockPersist: {skip: isPrimaryOnly},
abortReshardCollection: {skip: isPrimaryOnly},
Expand Down
10 changes: 6 additions & 4 deletions jstests/sharding/catalog_cache_refresh_counters.js
Expand Up @@ -103,10 +103,12 @@ runTest(() => assert.commandWorked(mongos1Coll.remove({x: 250})), [
/**
* Verify that non-CRUD commands get logged when blocked by a refresh.
*/
runTest(() => assert.commandWorked(mongos1DB.runCommand({collMod: collName})), [
{opType: 'countAllOperations', increase: 1},
{opType: 'countCommands', increase: 1},
]);
runTest(() => assert.commandWorked(mongos1DB.runCommand(
{createIndexes: collName, indexes: [{key: {a: 1}, name: 'index'}]})),
[
{opType: 'countAllOperations', increase: 1},
{opType: 'countCommands', increase: 1},
]);

st.stop();
})();
16 changes: 2 additions & 14 deletions jstests/sharding/index_commands_shard_targeting.js
Expand Up @@ -55,7 +55,8 @@ function assertCommandChecksShardVersions(st, dbName, collName, testCase) {
// (no chunks).
ShardVersioningUtil.assertCollectionVersionOlderThan(st.shard0, ns, latestCollectionVersion);

// Assert that the targeted shards have the latest collection version after the command is run.
// Assert that the targeted shards have the latest collection version after the command is
// run.
ShardVersioningUtil.assertCollectionVersionEquals(st.shard1, ns, latestCollectionVersion);
ShardVersioningUtil.assertCollectionVersionEquals(st.shard2, ns, latestCollectionVersion);
}
Expand Down Expand Up @@ -180,19 +181,6 @@ const testCases = {
}
};
},
collMod: collName => {
return {
command: {collMod: collName, validator: {x: {$type: "string"}}},
assertCommandRanOnShard: (shard) => {
assert.commandFailedWithCode(
shard.getCollection(dbName + "." + collName).insert({x: 1}),
ErrorCodes.DocumentValidationFailure);
},
assertCommandDidNotRunOnShard: (shard) => {
assert.commandWorked(shard.getCollection(dbName + "." + collName).insert({x: 1}));
}
};
},
};

assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
Expand Down
Expand Up @@ -129,33 +129,5 @@ stepNames.forEach((stepName) => {
}
});

stepNames.forEach((stepName) => {
jsTest.log(`Testing that collMod aborts concurrent outgoing migrations that are in step ${
stepName}...`);
const collName = "testCollModMoveChunkStep" + stepName;
const ns = dbName + "." + collName;

assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: shardKey}));

assertCommandAbortsConcurrentOutgoingMigration(st, stepName, ns, () => {
assert.commandWorked(
testDB.runCommand({collMod: collName, validator: {x: {$type: "string"}}}));
});

// Verify that the index command succeeds.
assert.commandFailedWithCode(st.shard0.getCollection(ns).insert({x: 1}),
ErrorCodes.DocumentValidationFailure);

// If collMod is run after the migration has reached the steady state, shard1
// will not perform schema validation because the validation rule just does not
// exist when shard1 clones the collection options from shard0. However, if collMod
// is run after the cloning step starts but before the steady state is reached,
// shard0 may have the validation rule when shard1 does the cloning so shard1 may
// or may not perform schema validation.
if (stepName == moveChunkStepNames.reachedSteadyState) {
assert.commandWorked(st.shard1.getCollection(ns).insert({x: 1}));
}
});

st.stop();
})();
2 changes: 1 addition & 1 deletion jstests/sharding/libs/mongos_api_params_util.js
Expand Up @@ -217,7 +217,7 @@ let MongosAPIParametersUtil = (function() {
commandName: "collMod",
run: {
inAPIVersion1: true,
shardCommandName: "collMod",
shardCommandName: "_shardsvrCollMod",
permittedInTxn: false,
command: () => ({collMod: "collection"}),
}
Expand Down
6 changes: 4 additions & 2 deletions jstests/sharding/move_primary_with_writes.js
Expand Up @@ -144,11 +144,13 @@ function buildCommands(collName, shouldFail) {
},
{
command: {collMod: collName, index: {keyPattern: {c: 1}, expireAfterSeconds: 3600}},
shouldFail: shouldFail
shouldFail: true,
errorCodes: [ErrorCodes.LockBusy, ErrorCodes.MovePrimaryInProgress]
},
{
command: {collMod: collName + "View", viewOn: collName, pipeline: [{$match: {_id: 1}}]},
shouldFail: true
shouldFail: true,
errorCodes: [ErrorCodes.LockBusy, ErrorCodes.MovePrimaryInProgress]
},
{command: {convertToCapped: "unshardedFoo", size: 1000000}, shouldFail: true},
{command: {dropIndexes: collName, index: collName + "Index"}, shouldFail: shouldFail},
Expand Down
2 changes: 2 additions & 0 deletions jstests/sharding/read_write_concern_defaults_application.js
Expand Up @@ -153,6 +153,8 @@ let testCases = {
_shardsvrReshardCollection: {skip: "internal command"},
_shardsvrReshardingOperationTime: {skip: "internal command"},
_shardsvrSetAllowMigrations: {skip: "internal command"},
_shardsvrCollMod: {skip: "internal command"},
_shardsvrCollModParticipant: {skip: "internal command"},
_shardsvrShardCollection:
{skip: "internal command"}, // TODO SERVER-58843: Remove once 6.0 becomes last LTS
_transferMods: {skip: "internal command"},
Expand Down
6 changes: 4 additions & 2 deletions jstests/sharding/resharding_disallow_writes.js
Expand Up @@ -72,8 +72,10 @@ reshardingTest.withReshardingInBackground(

jsTestLog("Attempting collMod");
assert.commandFailedWithCode(
sourceCollection.runCommand({collMod: sourceCollection.getName()}),
ErrorCodes.ReshardCollectionInProgress);
// 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.
sourceCollection.runCommand({collMod: sourceCollection.getName(), maxTimeMS: 5000}),
[ErrorCodes.ReshardCollectionInProgress, ErrorCodes.MaxTimeMSExpired]);

jsTestLog("Attempting drop index");
assert.commandFailedWithCode(
Expand Down
9 changes: 6 additions & 3 deletions jstests/sharding/resharding_prohibited_commands.js
Expand Up @@ -33,7 +33,9 @@ const indexDroppedByTest = {
};

const prohibitedCommands = [
{collMod: collectionName},
// 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.
{collMod: collectionName, maxTimeMS: 5000},
{createIndexes: collectionName, indexes: [{name: "idx1", key: indexCreatedByTest}]},
{dropIndexes: collectionName, index: indexDroppedByTest},
];
Expand Down Expand Up @@ -63,8 +65,9 @@ const assertCommandsSucceedAfterReshardingOpFinishes = (database) => {
const assertCommandsFailDuringReshardingOp = (database) => {
prohibitedCommands.forEach((command) => {
jsTest.log(`Testing that ${tojson(command)} fails during resharding operation`);
assert.commandFailedWithCode(database.runCommand(command),
ErrorCodes.ReshardCollectionInProgress);
assert.commandFailedWithCode(
database.runCommand(command),
[ErrorCodes.ReshardCollectionInProgress, ErrorCodes.MaxTimeMSExpired]);
});
};

Expand Down

0 comments on commit d6f2b64

Please sign in to comment.