mirror of https://github.com/mongodb/mongo
6463 lines
295 KiB
JavaScript
6463 lines
295 KiB
JavaScript
/**
|
|
* Tests that commands that accept write concern correctly return write concern errors.
|
|
*
|
|
* Every command that accepts writeConcern should define a test case. Test cases should consist of a
|
|
* no-op case, success case, and failure case where applicable. The no-op and failure scenarios
|
|
* should mimic scenarios where it's necessary to wait for write concern because the outcome of the
|
|
* request indicates to the user that the data is in some definitive state, e.g. the write is
|
|
* identical to a previous write, or the user has set up schema validation rules that would cause
|
|
* the current write to fail.
|
|
*
|
|
*/
|
|
import {TimeseriesTest} from "jstests/core/timeseries/libs/timeseries.js";
|
|
import {AllCommandsTest} from "jstests/libs/all_commands_test.js";
|
|
import {getCommandName} from "jstests/libs/cmd_object_utils.js";
|
|
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
|
|
import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js";
|
|
import {Thread} from "jstests/libs/parallelTester.js";
|
|
import {assertWriteConcernError} from "jstests/libs/write_concern_util.js";
|
|
|
|
const dbName = "testDB";
|
|
const collName = "testColl";
|
|
const fullNs = dbName + "." + collName;
|
|
|
|
const timeValue = ISODate("2015-12-31T23:59:59.000Z");
|
|
|
|
// Set up txn state to be used in the transaction test cases
|
|
let _lsid = UUID();
|
|
let _txnNum = 0;
|
|
function getLSID() {
|
|
return {id: _lsid};
|
|
}
|
|
function getTxnNumber() {
|
|
return NumberLong(_txnNum);
|
|
}
|
|
function genNextTxnNumber() {
|
|
_txnNum++;
|
|
}
|
|
|
|
function getShardNames(cluster) {
|
|
return [cluster.shard0.shardName, cluster.shard1.shardName];
|
|
}
|
|
|
|
function getShardKeyMinRanges(coll) {
|
|
let a = coll.getDB().getSiblingDB("config").chunks.find({uuid: coll.getUUID()}, {min: 1, _id: 0}).sort({min: 1});
|
|
return a.toArray();
|
|
}
|
|
|
|
// TODO SERVER-97754 Remove these 2 functions, and do not stop the remaining secondaries once these
|
|
// commands no longer override user provided writeConcern
|
|
let stopAdditionalSecondariesIfSharded = function (clusterType, cluster, secondariesRunning) {
|
|
if (clusterType == "sharded") {
|
|
const shards = cluster.getAllShards();
|
|
for (let i = 0; i < shards.length; i++) {
|
|
secondariesRunning[i].getDB("admin").fsyncLock();
|
|
}
|
|
}
|
|
};
|
|
let restartAdditionalSecondariesIfSharded = function (clusterType, cluster, secondariesRunning) {
|
|
if (clusterType == "sharded") {
|
|
const shards = cluster.getAllShards();
|
|
for (let i = 0; i < shards.length; i++) {
|
|
secondariesRunning[i].getDB("admin").fsyncUnlock();
|
|
}
|
|
}
|
|
};
|
|
|
|
let getShardKey = (coll, fullNs) => {
|
|
let entry = coll.getDB().getSiblingDB("config").collections.findOne({_id: fullNs}, {key: 1});
|
|
|
|
// If there is no shard key, return an empty obj
|
|
if (!entry) {
|
|
return {};
|
|
}
|
|
|
|
return entry.key;
|
|
};
|
|
|
|
// All commands in the server.
|
|
const wcCommandsTests = {
|
|
_addShard: {skip: "internal command"},
|
|
_cloneCollectionOptionsFromPrimaryShard: {skip: "internal command"},
|
|
_clusterQueryWithoutShardKey: {skip: "internal command"},
|
|
_clusterWriteWithoutShardKey: {skip: "internal command"},
|
|
_configsvrAbortReshardCollection: {skip: "internal command"},
|
|
_configsvrAddShard: {skip: "internal command"},
|
|
_configsvrAddShardToZone: {skip: "internal command"},
|
|
_configsvrBalancerCollectionStatus: {skip: "internal command"},
|
|
_configsvrBalancerStart: {skip: "internal command"},
|
|
_configsvrBalancerStatus: {skip: "internal command"},
|
|
_configsvrBalancerStop: {skip: "internal command"},
|
|
_configsvrCheckClusterMetadataConsistency: {skip: "internal command"},
|
|
_configsvrCheckMetadataConsistency: {skip: "internal command"},
|
|
_configsvrCleanupReshardCollection: {skip: "internal command"},
|
|
_configsvrCollMod: {skip: "internal command"},
|
|
_configsvrClearJumboFlag: {skip: "internal command"},
|
|
_configsvrCommitChunksMerge: {skip: "internal command"},
|
|
_configsvrCommitChunkMigration: {skip: "internal command"},
|
|
_configsvrCommitChunkSplit: {skip: "internal command"},
|
|
_configsvrCommitMergeAllChunksOnShard: {skip: "internal command"},
|
|
_configsvrCommitMovePrimary: {skip: "internal command"},
|
|
_configsvrCommitRefineCollectionShardKey: {skip: "internal command"},
|
|
_configsvrCommitReshardCollection: {skip: "internal command"},
|
|
_configsvrCommitShardRemoval: {skip: "internal command"},
|
|
_configsvrConfigureCollectionBalancing: {skip: "internal command"},
|
|
_configsvrCreateDatabase: {skip: "internal command"},
|
|
_configsvrEnsureChunkVersionIsGreaterThan: {skip: "internal command"},
|
|
_configsvrGetHistoricalPlacement: {skip: "internal command"},
|
|
_configsvrMoveRange: {skip: "internal command"},
|
|
_configsvrRemoveChunks: {skip: "internal command"},
|
|
_configsvrRemoveShard: {skip: "internal command"},
|
|
_configsvrRemoveShardFromZone: {skip: "internal command"},
|
|
_configsvrRemoveTags: {skip: "internal command"},
|
|
_configsvrRenameCollection: {skip: "internal command"},
|
|
_configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"},
|
|
_configsvrResetPlacementHistory: {skip: "internal command"},
|
|
_configsvrReshardCollection: {skip: "internal command"},
|
|
_configsvrRunRestore: {skip: "internal command"},
|
|
_configsvrSetAllowMigrations: {skip: "internal command"},
|
|
_configsvrSetClusterParameter: {skip: "internal command"},
|
|
_configsvrSetUserWriteBlockMode: {skip: "internal command"},
|
|
_configsvrShardDrainingStatus: {skip: "internal command"},
|
|
_configsvrStartShardDraining: {skip: "internal command"},
|
|
_configsvrStopShardDraining: {skip: "internal command"},
|
|
_configsvrTransitionFromDedicatedConfigServer: {skip: "internal command"},
|
|
_configsvrTransitionToDedicatedConfigServer: {skip: "internal command"},
|
|
_configsvrUpdateZoneKeyRange: {skip: "internal command"},
|
|
_dropConnectionsToMongot: {skip: "internal command"},
|
|
_dropMirrorMaestroConnections: {skip: "internal command"},
|
|
_flushDatabaseCacheUpdates: {skip: "internal command"},
|
|
_flushDatabaseCacheUpdatesWithWriteConcern: {skip: "internal command"},
|
|
_flushReshardingStateChange: {skip: "internal command"},
|
|
_flushRoutingTableCacheUpdates: {skip: "internal command"},
|
|
_flushRoutingTableCacheUpdatesWithWriteConcern: {skip: "internal command"},
|
|
_getNextSessionMods: {skip: "internal command"},
|
|
_getUserCacheGeneration: {skip: "internal command"},
|
|
_hashBSONElement: {skip: "internal command"},
|
|
_isSelf: {skip: "internal command"},
|
|
_killOperations: {skip: "internal command"},
|
|
_mergeAuthzCollections: {skip: "internal command"},
|
|
_migrateClone: {skip: "internal command"},
|
|
_mirrorMaestroConnPoolStats: {skip: "internal command"},
|
|
_mongotConnPoolStats: {skip: "internal command"},
|
|
_recvChunkAbort: {skip: "internal command"},
|
|
_recvChunkCommit: {skip: "internal command"},
|
|
_recvChunkReleaseCritSec: {skip: "internal command"},
|
|
_recvChunkStart: {skip: "internal command"},
|
|
_recvChunkStatus: {skip: "internal command"},
|
|
_refreshQueryAnalyzerConfiguration: {skip: "internal command"},
|
|
_shardsvrAbortReshardCollection: {skip: "internal command"},
|
|
_shardsvrBeginMigrationBlockingOperation: {skip: "internal command"},
|
|
_shardsvrChangePrimary: {skip: "internal command"},
|
|
_shardsvrCleanupReshardCollection: {skip: "internal command"},
|
|
_shardsvrCloneAuthoritativeMetadata: {skip: "internal command"},
|
|
_shardsvrCloneCatalogData: {skip: "internal command"},
|
|
_shardsvrCommitCreateDatabaseMetadata: {skip: "internal command"},
|
|
_shardsvrCommitDropDatabaseMetadata: {skip: "internal command"},
|
|
_shardsvrCheckMetadataConsistency: {skip: "internal command"},
|
|
_shardsvrCheckMetadataConsistencyParticipant: {skip: "internal command"},
|
|
_shardsvrCleanupStructuredEncryptionData: {skip: "internal command"},
|
|
_shardsvrCommitReshardCollection: {skip: "internal command"},
|
|
_shardsvrCompactStructuredEncryptionData: {skip: "internal command"},
|
|
_shardsvrConvertToCapped: {skip: "internal command"},
|
|
_shardsvrCoordinateMultiUpdate: {skip: "internal command"},
|
|
_shardsvrCreateCollection: {skip: "internal command"},
|
|
_shardsvrCreateCollectionParticipant: {skip: "internal command"},
|
|
_shardsvrDrainOngoingDDLOperations: {skip: "internal command"},
|
|
_shardsvrDropCollection: {skip: "internal command"},
|
|
_shardsvrDropCollectionIfUUIDNotMatchingWithWriteConcern: {skip: "internal command"},
|
|
_shardsvrDropCollectionParticipant: {skip: "internal command"},
|
|
_shardsvrDropIndexes: {skip: "internal command"},
|
|
_shardsvrDropIndexesParticipant: {skip: "internal command"},
|
|
_shardsvrDropDatabase: {skip: "internal command"},
|
|
_shardsvrDropDatabaseParticipant: {skip: "internal command"},
|
|
_shardsvrEndMigrationBlockingOperation: {skip: "internal command"},
|
|
_shardsvrGetStatsForBalancing: {skip: "internal command"},
|
|
_shardsvrCheckCanConnectToConfigServer: {skip: "internal command"},
|
|
_shardsvrJoinDDLCoordinators: {skip: "internal command"},
|
|
_shardsvrJoinMigrations: {skip: "internal command"},
|
|
_shardsvrMergeAllChunksOnShard: {skip: "internal command"},
|
|
_shardsvrMergeChunks: {skip: "internal command"},
|
|
_shardsvrMovePrimary: {skip: "internal command"},
|
|
_shardsvrMovePrimaryEnterCriticalSection: {skip: "internal command"},
|
|
_shardsvrMovePrimaryExitCriticalSection: {skip: "internal command"},
|
|
_shardsvrMoveRange: {skip: "internal command"},
|
|
_shardsvrNotifyShardingEvent: {skip: "internal command"},
|
|
_shardsvrRefineCollectionShardKey: {skip: "internal command"},
|
|
_shardsvrRenameCollection: {skip: "internal command"},
|
|
_shardsvrRenameCollectionParticipant: {skip: "internal command"},
|
|
_shardsvrRenameCollectionParticipantUnblock: {skip: "internal command"},
|
|
_shardsvrRenameIndexMetadata: {skip: "internal command"},
|
|
_shardsvrReshardCollection: {skip: "internal command"},
|
|
_shardsvrReshardingDonorFetchFinalCollectionStats: {skip: "internal command"},
|
|
_shardsvrReshardingDonorStartChangeStreamsMonitor: {skip: "internal command"},
|
|
_shardsvrReshardingOperationTime: {skip: "internal command"},
|
|
_shardsvrReshardRecipientClone: {skip: "internal command"},
|
|
_shardsvrResolveView: {skip: "internal command"},
|
|
_shardsvrRunSearchIndexCommand: {skip: "internal command"},
|
|
_shardsvrSetAllowMigrations: {skip: "internal command"},
|
|
_shardsvrSetClusterParameter: {skip: "internal command"},
|
|
_shardsvrSetUserWriteBlockMode: {skip: "internal command"},
|
|
_shardsvrValidateShardKeyCandidate: {skip: "internal command"},
|
|
_shardsvrCollMod: {skip: "internal command"},
|
|
_shardsvrCollModParticipant: {skip: "internal command"},
|
|
_shardsvrConvertToCappedParticipant: {skip: "internal command"},
|
|
_shardsvrParticipantBlock: {skip: "internal command"},
|
|
_shardsvrUntrackUnsplittableCollection: {skip: "internal command"},
|
|
_shardsvrFetchCollMetadata: {skip: "internal command"},
|
|
streams_startStreamProcessor: {skip: "internal command"},
|
|
streams_startStreamSample: {skip: "internal command"},
|
|
streams_stopStreamProcessor: {skip: "internal command"},
|
|
streams_listStreamProcessors: {skip: "internal command"},
|
|
streams_getMoreStreamSample: {skip: "internal command"},
|
|
streams_getStats: {skip: "internal command"},
|
|
streams_testOnlyInsert: {skip: "internal command"},
|
|
streams_getMetrics: {skip: "internal command"},
|
|
streams_updateFeatureFlags: {skip: "internal command"},
|
|
streams_testOnlyGetFeatureFlags: {skip: "internal command"},
|
|
streams_writeCheckpoint: {skip: "internal command"},
|
|
streams_sendEvent: {skip: "internal command"},
|
|
streams_updateConnection: {skip: "internal command"},
|
|
_transferMods: {skip: "internal command"},
|
|
abortMoveCollection: {skip: "does not accept write concern"},
|
|
abortReshardCollection: {skip: "does not accept write concern"},
|
|
abortRewriteCollection: {skip: "does not accept write concern"},
|
|
abortTransaction: {
|
|
success: {
|
|
// Basic abort transaction
|
|
req: () => ({
|
|
abortTransaction: 1,
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
lsid: getLSID(),
|
|
}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 0}));
|
|
|
|
if (clusterType == "sharded" && bsonWoCompare(getShardKey(coll, fullNs), {}) == 0) {
|
|
// Set the primary shard to shard0 so we can assume that it's okay to run
|
|
// prepareTransaction on it
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({moveCollection: fullNs, toShard: cluster.shard0.shardName}),
|
|
);
|
|
}
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
insert: collName,
|
|
documents: [{_id: 1}],
|
|
lsid: getLSID(),
|
|
stmtIds: [NumberInt(0)],
|
|
txnNumber: getTxnNumber(),
|
|
startTransaction: true,
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
update: collName,
|
|
updates: [{q: {}, u: {$set: {a: 1}}}],
|
|
lsid: getLSID(),
|
|
stmtIds: [NumberInt(1)],
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
|
|
let primary = clusterType == "sharded" ? cluster.rs0.getPrimary() : cluster.getPrimary();
|
|
assert.commandWorked(
|
|
primary.getDB(dbName).adminCommand({
|
|
prepareTransaction: 1,
|
|
lsid: getLSID(),
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
genNextTxnNumber();
|
|
},
|
|
admin: true,
|
|
},
|
|
failure: {
|
|
// The transaction has already been committed
|
|
req: () => ({
|
|
abortTransaction: 1,
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
lsid: getLSID(),
|
|
}),
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
insert: collName,
|
|
documents: [{_id: 1}],
|
|
lsid: getLSID(),
|
|
stmtIds: [NumberInt(0)],
|
|
txnNumber: getTxnNumber(),
|
|
startTransaction: true,
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({
|
|
commitTransaction: 1,
|
|
lsid: getLSID(),
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.TransactionCommitted);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
genNextTxnNumber();
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
abortUnshardCollection: {skip: "does not accept write concern"},
|
|
addShard: {skip: "unrelated"},
|
|
addShardToZone: {skip: "does not accept write concern"},
|
|
aggregate: {
|
|
noop: {
|
|
// The pipeline will not match any docs, so nothing should be written to "out"
|
|
req: {aggregate: collName, pipeline: [{$match: {x: 1}}, {$out: "out"}], cursor: {}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 1}));
|
|
assert.commandWorked(coll.remove({_id: 1}));
|
|
assert.eq(coll.count({_id: 1}), 0);
|
|
assert.commandWorked(coll.getDB().createCollection("out"));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
assert.eq(coll.getDB().out.find().itcount(), 0);
|
|
coll.getDB().out.drop();
|
|
},
|
|
},
|
|
success: {
|
|
// The pipeline should write a single doc to "out"
|
|
req: {aggregate: collName, pipeline: [{$match: {_id: 1}}, {$out: "out"}], cursor: {}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.getDB().createCollection("out"));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().out.find().itcount(), 0);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().out.find().itcount(), 1);
|
|
}
|
|
assert.eq(coll.count({_id: 1}), 1);
|
|
coll.getDB().out.drop();
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to update _id to the same value and get a duplicate key error
|
|
req: {aggregate: collName, pipeline: [{$set: {_id: 0}}, {$out: "out"}], cursor: {}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.insert({_id: 2}));
|
|
assert.commandWorked(coll.getDB().createCollection("out"));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey);
|
|
}
|
|
assert.eq(coll.getDB().out.find().itcount(), 0);
|
|
assert.eq(coll.count({_id: 1}), 1);
|
|
coll.getDB().out.drop();
|
|
},
|
|
},
|
|
},
|
|
// TODO SPM-2513 Define test case for analyze once the command is user facing
|
|
analyze: {skip: "internal only"},
|
|
analyzeShardKey: {skip: "does not accept write concern"},
|
|
appendOplogNote: {
|
|
success: {
|
|
// appendOplogNote basic
|
|
req: {appendOplogNote: 1, data: {x: 1}},
|
|
setupFunc: (coll) => {},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
applyOps: {
|
|
noop: {
|
|
// 'applyOps' where the update is a no-op
|
|
req: {applyOps: [{op: "u", ns: fullNs, o: {_id: 1}, o2: {_id: 1}}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.applied, 1);
|
|
assert.eq(res.results[0], true);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({_id: 1}), 1);
|
|
},
|
|
},
|
|
success: {
|
|
// 'applyOps' basic insert
|
|
req: {applyOps: [{op: "i", ns: fullNs, o: {_id: 2, x: 3}}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 3}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.applied, 1);
|
|
assert.eq(res.results[0], true);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
assert.eq(coll.count({x: 3}), 2);
|
|
},
|
|
},
|
|
failure: {
|
|
// 'applyOps' attempt to update immutable field
|
|
req: {applyOps: [{op: "u", ns: fullNs, o: {_id: 2}, o2: {_id: 1}}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.eq(res.applied, 1);
|
|
assert.eq(res.ok, 0);
|
|
assert.eq(res.results[0], false);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({_id: 1}), 1);
|
|
},
|
|
},
|
|
},
|
|
authenticate: {skip: "does not accept write concern"},
|
|
autoCompact: {skip: "does not accept write concern"},
|
|
autoSplitVector: {skip: "does not accept write concern"},
|
|
balancerCollectionStatus: {skip: "does not accept write concern"},
|
|
balancerStart: {skip: "does not accept write concern"},
|
|
balancerStatus: {skip: "does not accept write concern"},
|
|
balancerStop: {skip: "does not accept write concern"},
|
|
buildInfo: {skip: "does not accept write concern"},
|
|
bulkWrite: {
|
|
noop: {
|
|
// The doc to update doesn't exist
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [{update: 0, filter: {_id: 0}, updateMods: {$set: {x: 1}}}],
|
|
nsInfo: [{ns: fullNs}],
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nModified, 0);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic insert in a bulk write
|
|
req: {bulkWrite: 1, ops: [{insert: 0, document: {_id: 2}}], nsInfo: [{ns: fullNs}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 3}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nInserted, 1);
|
|
},
|
|
admin: true,
|
|
},
|
|
failure: {
|
|
// Attempt to insert doc with duplicate _id
|
|
req: {bulkWrite: 1, ops: [{insert: 0, document: {_id: 1}}], nsInfo: [{ns: fullNs}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 3}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.eq(res.nErrors, 1);
|
|
assert(res.cursor && res.cursor.firstBatch && res.cursor.firstBatch.length == 1);
|
|
assert.commandFailedWithCode(res.cursor.firstBatch[0], ErrorCodes.DuplicateKey);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
changePrimary: {
|
|
noop: {
|
|
// The destination shard is already the primary shard for the db
|
|
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[0]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic change primary
|
|
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[1]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard1.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
// Change the primary back
|
|
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: cluster.shard0.shardName}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
checkMetadataConsistency: {skip: "does not accept write concern"},
|
|
checkShardingIndex: {skip: "does not accept write concern"},
|
|
cleanupOrphaned: {skip: "only exist on direct shard connection"},
|
|
cleanupReshardCollection: {skip: "does not accept write concern"},
|
|
cleanupStructuredEncryptionData: {skip: "does not accept write concern"},
|
|
clearJumboFlag: {skip: "does not accept write concern"},
|
|
clearLog: {skip: "does not accept write concern"},
|
|
cloneCollectionAsCapped: {
|
|
success: {
|
|
// Basic cloneColectionAsCapped
|
|
req: {
|
|
cloneCollectionAsCapped: collName,
|
|
toCollection: collName + "2",
|
|
size: 10 * 1024 * 1024,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.eq(
|
|
coll
|
|
.getDB()
|
|
.getCollection(collName + "2")
|
|
.find()
|
|
.itcount(),
|
|
0,
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(
|
|
coll
|
|
.getDB()
|
|
.getCollection(collName + "2")
|
|
.find()
|
|
.itcount(),
|
|
1,
|
|
);
|
|
coll.getDB()
|
|
.getCollection(collName + "2")
|
|
.drop();
|
|
},
|
|
},
|
|
failure: {
|
|
// The destination collection already exists
|
|
req: {
|
|
cloneCollectionAsCapped: collName,
|
|
toCollection: collName + "2",
|
|
size: 10 * 1024 * 1024,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(
|
|
coll
|
|
.getDB()
|
|
.getCollection(collName + "2")
|
|
.insert({_id: 1}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.NamespaceExists);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
},
|
|
},
|
|
clusterAbortTransaction: {skip: "already tested by 'abortTransaction' tests on mongos"},
|
|
clusterAggregate: {skip: "already tested by 'aggregate' tests on mongos"},
|
|
clusterBulkWrite: {skip: "already tested by 'bulkWrite' tests on mongos"},
|
|
clusterCommitTransaction: {skip: "already tested by 'commitTransaction' tests on mongos"},
|
|
clusterCount: {skip: "already tested by 'count' tests on mongos"},
|
|
clusterDelete: {skip: "already tested by 'delete' tests on mongos"},
|
|
clusterFind: {skip: "already tested by 'find' tests on mongos"},
|
|
clusterGetMore: {skip: "already tested by 'getMore' tests on mongos"},
|
|
clusterInsert: {skip: "already tested by 'insert' tests on mongos"},
|
|
clusterUpdate: {skip: "already tested by 'update' tests on mongos"},
|
|
collMod: {
|
|
noop: {
|
|
// Validator already non-existent
|
|
req: {collMod: collName, validator: {}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {}}));
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Add validator
|
|
req: {collMod: collName, validator: {x: {$exists: true}}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 1}));
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, {x: {$exists: true}});
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
failure: {
|
|
// Index to be updated does not exist
|
|
req: {collMod: collName, index: {name: "a_1", hidden: true}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {a: 1}, name: "a_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({dropIndexes: collName, index: "a_1"}),
|
|
);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.IndexNotFound);
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
collStats: {skip: "does not accept write concern"},
|
|
commitReshardCollection: {skip: "does not accept write concern"},
|
|
commitShardRemoval: {skip: "unrelated"},
|
|
commitTransaction: {
|
|
noop: {
|
|
// The transaction is already committed
|
|
req: () => ({
|
|
commitTransaction: 1,
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
lsid: getLSID(),
|
|
}),
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
insert: collName,
|
|
documents: [{_id: 1}],
|
|
lsid: getLSID(),
|
|
stmtIds: [NumberInt(0)],
|
|
txnNumber: getTxnNumber(),
|
|
startTransaction: true,
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({
|
|
commitTransaction: 1,
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
lsid: getLSID(),
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
genNextTxnNumber();
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic commit transaction
|
|
req: () => ({
|
|
commitTransaction: 1,
|
|
txnNumber: getTxnNumber(),
|
|
autocommit: false,
|
|
lsid: getLSID(),
|
|
}),
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
insert: collName,
|
|
documents: [{_id: 1}],
|
|
lsid: getLSID(),
|
|
stmtIds: [NumberInt(0)],
|
|
txnNumber: getTxnNumber(),
|
|
startTransaction: true,
|
|
autocommit: false,
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
genNextTxnNumber();
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
commitTransitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
compact: {skip: "does not accept write concern"},
|
|
compactStructuredEncryptionData: {skip: "does not accept write concern"},
|
|
configureCollectionBalancing: {skip: "does not accept write concern"},
|
|
configureFailPoint: {skip: "internal command"},
|
|
configureQueryAnalyzer: {skip: "does not accept write concern"},
|
|
connPoolStats: {skip: "does not accept write concern"},
|
|
connPoolSync: {skip: "internal command"},
|
|
connectionStatus: {skip: "does not accept write concern"},
|
|
convertToCapped: {
|
|
noop: {
|
|
// Collection is already capped
|
|
req: {convertToCapped: collName, size: 10 * 1024 * 1024},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.runCommand({convertToCapped: collName, size: 10 * 1024 * 1024}));
|
|
assert.eq(coll.stats().capped, true);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.stats().capped, true);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic convertToCapped
|
|
req: {convertToCapped: collName, size: 10 * 1024 * 1024},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.eq(coll.stats().capped, false);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.stats().capped, true);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
failure: {
|
|
// The collection doesn't exist
|
|
req: {convertToCapped: collName, size: 10 * 1024 * 1024},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({drop: collName}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.NamespaceNotFound);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
coordinateCommitTransaction: {skip: "internal command"},
|
|
count: {skip: "does not accept write concern"},
|
|
cpuload: {skip: "does not accept write concern"},
|
|
create: {
|
|
noop: {
|
|
// Coll already exists
|
|
req: {create: collName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({create: collName}));
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic create coll
|
|
req: {create: collName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
// Ensure DB exists, but create a different collection
|
|
assert.commandWorked(coll.getDB().coll2.insert({_id: 1}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to create a view and output to a nonexistent collection
|
|
req: {create: "viewWithOut", viewOn: collName, pipeline: [{$out: "nonexistentColl"}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.getDB().runCommand({drop: "nonexistentColl"}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.OptionNotSupportedOnView);
|
|
}
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert(!coll.getDB().getCollectionNames().includes("nonexistentColl"));
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
createIndexes: {
|
|
// All voting data bearing nodes are not up for this test. So 'createIndexes' command
|
|
// can't succeed with the default index commitQuorum value "votingMembers". So, we run
|
|
// createIndexes using commit quorum "majority".
|
|
noop: {
|
|
// Index already exists
|
|
req: {
|
|
createIndexes: collName,
|
|
indexes: [{key: {a: 1}, name: "a_1"}],
|
|
commitQuorum: "majority",
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {a: 1}, name: "a_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let details = res;
|
|
if ("raw" in details) {
|
|
const raw = details.raw;
|
|
details = raw[Object.keys(raw)[0]];
|
|
}
|
|
assert.eq(details.numIndexesBefore, details.numIndexesAfter);
|
|
assert.eq(details.note, "all indexes already exist");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic create index
|
|
req: {
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({b: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let details = res;
|
|
if ("raw" in details) {
|
|
const raw = details.raw;
|
|
details = raw[Object.keys(raw)[0]];
|
|
}
|
|
assert.eq(details.numIndexesBefore, details.numIndexesAfter - 1);
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to create two indexes with the same name and different keys
|
|
req: {
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({b: 1}));
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: -1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.IndexKeySpecsConflict);
|
|
},
|
|
},
|
|
},
|
|
createRole: {
|
|
targetConfigServer: true,
|
|
success: {
|
|
// Basic create role
|
|
req: {createRole: "foo", privileges: [], roles: []},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
assert.eq(coll.getDB().getRole("foo"), null);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.neq(coll.getDB().getRole("foo"), null);
|
|
}
|
|
coll.getDB().runCommand({dropRole: "foo"});
|
|
},
|
|
},
|
|
failure: {
|
|
// Role already exists
|
|
req: {createRole: "foo", privileges: [], roles: []},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.neq(coll.getDB().getRole("foo"), null);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createUser: "fakeusr",
|
|
pwd: "bar",
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().runCommand({dropUser: "fakeusr"});
|
|
} else {
|
|
assert.commandFailedWithCode(res, 51002);
|
|
}
|
|
assert.neq(coll.getDB().getRole("foo"), null);
|
|
coll.getDB().runCommand({dropRole: "foo"});
|
|
},
|
|
},
|
|
},
|
|
createSearchIndexes: {skip: "does not accept write concern"},
|
|
createUnsplittableCollection: {skip: "internal command"},
|
|
createUser: {
|
|
targetConfigServer: true,
|
|
success: {
|
|
// Basic create user
|
|
req: {createUser: "foo", pwd: "bar", roles: []},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
assert.eq(coll.getDB().getUser("foo"), null);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.neq(coll.getDB().getUser("foo"), null);
|
|
}
|
|
|
|
coll.getDB().runCommand({dropUser: "foo"});
|
|
},
|
|
},
|
|
failure: {
|
|
// User already exists
|
|
req: {createUser: "foo", pwd: "bar", roles: []},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
assert.neq(coll.getDB().getUser("foo"), null);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().runCommand({dropRole: "bar"});
|
|
} else {
|
|
assert.commandFailedWithCode(res, 51003);
|
|
}
|
|
assert.neq(coll.getDB().getUser("foo"), null);
|
|
coll.getDB().runCommand({dropUser: "foo"});
|
|
},
|
|
},
|
|
},
|
|
currentOp: {skip: "does not accept write concern"},
|
|
dataSize: {skip: "does not accept write concern"},
|
|
dbCheck: {skip: "does not accept write concern"},
|
|
dbHash: {skip: "does not accept write concern"},
|
|
dbStats: {skip: "does not accept write concern"},
|
|
delete: {
|
|
noop: {
|
|
// The query will not match any doc
|
|
req: {delete: collName, deletes: [{q: {_id: 1}, limit: 1}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.remove({_id: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.count({a: 1}), 0);
|
|
},
|
|
},
|
|
success: {
|
|
req: {delete: collName, deletes: [{q: {_id: 1}, limit: 1}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 1);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
},
|
|
distinct: {skip: "does not accept write concern"},
|
|
drop: {
|
|
noop: {
|
|
// The collection has already been dropped
|
|
req: {drop: collName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.getDB().runCommand({drop: collName}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic drop collection
|
|
req: {drop: collName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
dropAllRolesFromDatabase: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// No roles exist
|
|
req: {dropAllRolesFromDatabase: 1},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.eq(coll.getDB().getRoles({rolesInfo: 1}).length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
assert.eq(coll.getDB().getRoles({rolesInfo: 1}).length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
}
|
|
},
|
|
},
|
|
success: {
|
|
// Basic dropAllRolesFromDatabase
|
|
req: {dropAllRolesFromDatabase: 1},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getRoles({rolesInfo: 1}).length, 0);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
dropAllUsersFromDatabase: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// No users exist
|
|
req: {dropAllUsersFromDatabase: 1},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.eq(coll.getDB().getUsers().length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
assert.eq(coll.getDB().getUsers().length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().runCommand({dropRole: "bar"});
|
|
}
|
|
},
|
|
},
|
|
success: {
|
|
// Basic dropAllUsersFromDatabase
|
|
req: {dropAllUsersFromDatabase: 1},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getUsers().length, 0);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
dropConnections: {skip: "does not accept write concern"},
|
|
dropDatabase: {
|
|
noop: {
|
|
// Database has already been dropped
|
|
req: {dropDatabase: 1},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorkedIgnoringWriteConcernErrors(coll.getDB().runCommand({dropDatabase: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic drop database
|
|
req: {dropDatabase: 1},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
dropIndexes: {
|
|
noop: {
|
|
// Passing "*" will drop all indexes except the _id index. The only index on this
|
|
// collection is the _id index, so the command will be a no-op.
|
|
req: {dropIndexes: collName, index: "*"},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
// Implicitly create the collection via insert if it doesn't exist
|
|
assert.commandWorked(coll.insert({b: "b"}));
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
optionalArgs.numIndexesBefore = coll.getIndexes().length;
|
|
// Make a non-acknowledged write so that the no-op will have to fail with WCE
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.insert({b: "b"}, {writeConcern: {w: 3, wtimeout: 100}}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic drop index
|
|
req: {
|
|
dropIndexes: collName,
|
|
index: "b_1",
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({b: "b"}));
|
|
|
|
const numIndexesBefore = coll.getIndexes().length;
|
|
optionalArgs.numIndexesBefore = numIndexesBefore;
|
|
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
|
|
assert.eq(coll.getIndexes().length, numIndexesBefore + 1);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let details = res;
|
|
if ("raw" in details) {
|
|
const raw = details.raw;
|
|
details = raw[Object.keys(raw)[0]];
|
|
}
|
|
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
dropRole: {
|
|
targetConfigServer: true,
|
|
success: {
|
|
// Basic dropRole
|
|
req: {dropRole: "foo"},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.eq(coll.getDB().getRoles().length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getRoles().length, 0);
|
|
}
|
|
},
|
|
},
|
|
failure: {
|
|
// Role does not exist
|
|
req: {dropRole: "foo"},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.eq(coll.getDB().getRoles().length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.RoleNotFound);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().runCommand({dropRole: "bar"});
|
|
}
|
|
assert.eq(coll.getDB().getRoles().length, 0);
|
|
},
|
|
},
|
|
},
|
|
dropSearchIndex: {skip: "does not accept write concern"},
|
|
dropUser: {
|
|
targetConfigServer: true,
|
|
success: {
|
|
// Basic dropUser
|
|
req: {dropUser: "foo"},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
assert.eq(coll.getDB().getUsers().length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getUsers().length, 0);
|
|
}
|
|
},
|
|
},
|
|
failure: {
|
|
// User doesn't exist
|
|
req: {dropUser: "foo"},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.eq(coll.getDB().getUsers({filter: {user: "foo"}}).length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.UserNotFound);
|
|
assert.eq(coll.getDB().getUsers({filter: {user: "foo"}}).length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().runCommand({dropRole: "bar"});
|
|
}
|
|
},
|
|
},
|
|
},
|
|
echo: {skip: "does not accept write concern"},
|
|
enableSharding: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Database creation only acknowledged by the config primary node
|
|
req: {enableSharding: dbName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding no
|
|
// longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
|
|
// `enableSharding` throws the WCE as top-level error when majority WC is not
|
|
// available
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().adminCommand({enableSharding: dbName}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
|
|
// The database exists on the config primary node
|
|
assert.eq(
|
|
coll
|
|
.getDB()
|
|
.getSiblingDB("config")
|
|
.databases.find(
|
|
{_id: dbName},
|
|
{}, // project
|
|
{readConcern: {level: 1}, $readPreference: {mode: "primary"}},
|
|
)
|
|
.itcount(),
|
|
1,
|
|
);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic enable sharding
|
|
req: {enableSharding: dbName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 1);
|
|
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
|
|
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 0);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding no
|
|
// longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
endSessions: {skip: "does not accept write concern"},
|
|
eseRotateActiveKEK: {skip: "does not accept write concern"},
|
|
explain: {skip: "does not accept write concern"},
|
|
features: {skip: "does not accept write concern"},
|
|
filemd5: {skip: "does not accept write concern"},
|
|
find: {skip: "does not accept write concern"},
|
|
findAndModify: {
|
|
noop: {
|
|
// The doc to update does not exist
|
|
req: {findAndModify: collName, query: {_id: 1}, update: {b: 2}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.remove({_id: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
assert.eq(coll.count({b: 2}), 0);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic findOneAndReplace
|
|
req: {findAndModify: collName, query: {_id: 1}, update: {_id: 1, b: 2}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to update and cause document validation error
|
|
req: {findAndModify: collName, query: {_id: 1}, update: {_id: 1}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 1}));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {x: {$exists: true}}}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({_id: 1}), 1);
|
|
},
|
|
},
|
|
},
|
|
flushRouterConfig: {skip: "does not accept write concern"},
|
|
forceerror: {skip: "test command"},
|
|
fsync: {skip: "does not accept write concern"},
|
|
fsyncUnlock: {skip: "does not accept write concern"},
|
|
getAuditConfig: {skip: "does not accept write concern"},
|
|
getChangeStreamState: {skip: "removed in v8.3"},
|
|
getClusterParameter: {skip: "does not accept write concern"},
|
|
getCmdLineOpts: {skip: "does not accept write concern"},
|
|
getDatabaseVersion: {skip: "internal command"},
|
|
getDefaultRWConcern: {skip: "does not accept write concern"},
|
|
getDiagnosticData: {skip: "does not accept write concern"},
|
|
getESERotateActiveKEKStatus: {skip: "does not accept write concern"},
|
|
getLog: {skip: "does not accept write concern"},
|
|
getMore: {skip: "does not accept write concern"},
|
|
getParameter: {skip: "does not accept write concern"},
|
|
getQueryableEncryptionCountInfo: {skip: "does not accept write concern"},
|
|
getShardMap: {skip: "internal command"},
|
|
getShardVersion: {skip: "internal command"},
|
|
getTransitionToDedicatedConfigServerStatus: {skip: "unrelated"},
|
|
godinsert: {skip: "for testing only"},
|
|
grantPrivilegesToRole: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Role already has privilege
|
|
req: {
|
|
grantPrivilegesToRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
grantPrivilegesToRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
}),
|
|
);
|
|
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 1);
|
|
assert.eq(role[0].privileges[0].actions, ["find"]);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createUser: "fakeusr",
|
|
pwd: "bar",
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 1);
|
|
assert.eq(role[0].privileges[0].actions, ["find"]);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropUser("fakeusr");
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic grantPrivilegesToRole
|
|
req: {
|
|
grantPrivilegesToRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 1);
|
|
assert.eq(role[0].privileges[0].actions, ["find"]);
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
},
|
|
grantRolesToRole: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Foo already has role bar
|
|
req: {grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
roles: [],
|
|
}),
|
|
);
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]}),
|
|
);
|
|
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 1);
|
|
assert.eq(role.inheritedRoles[0].role, "bar");
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createUser: "fakeusr",
|
|
pwd: "bar",
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 1);
|
|
assert.eq(role.inheritedRoles[0].role, "bar");
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropUser("fakeusr");
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().dropRole("bar");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic grantRolesToRole
|
|
req: {grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
roles: [],
|
|
}),
|
|
);
|
|
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 1);
|
|
assert.eq(role.inheritedRoles[0].role, "bar");
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().dropRole("bar");
|
|
},
|
|
},
|
|
},
|
|
grantRolesToUser: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// User already has role
|
|
req: {grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]}),
|
|
);
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 1);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropRole("bar");
|
|
}
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().runCommand({dropUser: "foo"});
|
|
},
|
|
},
|
|
success: {
|
|
// Basic grantRolesToUser
|
|
req: {grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 1);
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().runCommand({dropUser: "foo"});
|
|
},
|
|
},
|
|
failure: {
|
|
// Role does not exist
|
|
req: {grantRolesToUser: "foo", roles: ["fakeRole"]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.RoleNotFound);
|
|
}
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropRole("bar");
|
|
}
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().runCommand({dropUser: "foo"});
|
|
},
|
|
},
|
|
},
|
|
getTrafficRecordingStatus: {skip: "does not accept write concern"},
|
|
handshake: {skip: "does not accept write concern"},
|
|
hello: {skip: "does not accept write concern"},
|
|
hostInfo: {skip: "does not accept write concern"},
|
|
httpClientRequest: {skip: "does not accept write concern"},
|
|
exportCollection: {skip: "internal command"},
|
|
importCollection: {skip: "internal command"},
|
|
insert: {
|
|
// A no-op insert that returns success is not possible
|
|
success: {
|
|
// Basic insert
|
|
req: {insert: collName, documents: [{_id: 11}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 10}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 1);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
},
|
|
failure: {
|
|
// Insert causes DuplicateKeyError
|
|
req: {insert: collName, documents: [{_id: 10}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 10}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.DuplicateKey);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.count({_id: 10}), 1);
|
|
},
|
|
},
|
|
},
|
|
internalRenameIfOptionsAndIndexesMatch: {skip: "internal command"},
|
|
invalidateUserCache: {skip: "does not accept write concern"},
|
|
isdbgrid: {skip: "does not accept write concern"},
|
|
isMaster: {skip: "does not accept write concern"},
|
|
killAllSessions: {skip: "does not accept write concern"},
|
|
killAllSessionsByPattern: {skip: "does not accept write concern"},
|
|
killCursors: {skip: "does not accept write concern"},
|
|
killOp: {skip: "does not accept write concern"},
|
|
killSessions: {skip: "does not accept write concern"},
|
|
listCollections: {skip: "does not accept write concern"},
|
|
listCommands: {skip: "does not accept write concern"},
|
|
listDatabases: {skip: "does not accept write concern"},
|
|
listDatabasesForAllTenants: {skip: "does not accept write concern"},
|
|
listIndexes: {skip: "does not accept write concern"},
|
|
listSearchIndexes: {skip: "does not accept write concern"},
|
|
listShards: {skip: "does not accept write concern"},
|
|
lockInfo: {skip: "does not accept write concern"},
|
|
logApplicationMessage: {skip: "does not accept write concern"},
|
|
logMessage: {skip: "does not accept write concern"},
|
|
logRotate: {skip: "does not accept write concern"},
|
|
logout: {skip: "does not accept write concern"},
|
|
makeSnapshot: {skip: "does not accept write concern"},
|
|
mapReduce: {skip: "deprecated"},
|
|
mergeAllChunksOnShard: {skip: "does not accept write concern"},
|
|
mergeChunks: {skip: "does not accept write concern"},
|
|
moveChunk: {
|
|
targetConfigServer: true,
|
|
success: {
|
|
// Basic moveChunk
|
|
req: (cluster, coll) => ({
|
|
moveChunk: fullNs,
|
|
find: getShardKeyMinRanges(coll)[0]["min"],
|
|
to: getShardNames(cluster)[0],
|
|
secondaryThrottle: true,
|
|
}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let keyToMove = getShardKeyMinRanges(coll)[0]["min"];
|
|
optionalArgs.originalShard = coll
|
|
.getDB()
|
|
.getSiblingDB("config")
|
|
.chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard;
|
|
let destShard = getShardNames(cluster)[1];
|
|
|
|
assert.commandWorked(coll.getDB().adminCommand({moveChunk: fullNs, find: keyToMove, to: destShard}));
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard,
|
|
destShard,
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once moveChunk no
|
|
// longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let keyMoved = getShardKeyMinRanges(coll)[0]["min"];
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
|
|
getShardNames(cluster)[1],
|
|
);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({moveChunk: fullNs, find: keyMoved, to: optionalArgs.originalShard}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
|
|
optionalArgs.originalShard,
|
|
);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
moveCollection: {skip: "does not accept write concern"},
|
|
movePrimary: {
|
|
noop: {
|
|
// The destination shard is already the primary shard for the db
|
|
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[0]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic movePrimary
|
|
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[1]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
// Change the primary back
|
|
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: cluster.shard0.shardName}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
moveRange: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Chunk already lives on this shard
|
|
req: (cluster, coll) => ({
|
|
moveRange: fullNs,
|
|
min: getShardKeyMinRanges(coll)[0]["min"],
|
|
toShard: getShardNames(cluster)[0],
|
|
}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let keyToMove = getShardKeyMinRanges(coll)[0]["min"];
|
|
optionalArgs.originalShard = coll
|
|
.getDB()
|
|
.getSiblingDB("config")
|
|
.chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard;
|
|
let destShard = getShardNames(cluster)[0];
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({moveRange: fullNs, min: keyToMove, toShard: destShard}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard,
|
|
destShard,
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once moveChunk no
|
|
// longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
let keyMoved = getShardKeyMinRanges(coll)[0]["min"];
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
|
|
getShardNames(cluster)[0],
|
|
);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({moveRange: fullNs, min: keyMoved, toShard: optionalArgs.originalShard}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
|
|
optionalArgs.originalShard,
|
|
);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic moveRange
|
|
req: (cluster, coll) => ({
|
|
moveRange: fullNs,
|
|
min: getShardKeyMinRanges(coll)[0]["min"],
|
|
toShard: getShardNames(cluster)[0],
|
|
secondaryThrottle: true,
|
|
}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let keyToMove = getShardKeyMinRanges(coll)[0]["min"];
|
|
optionalArgs.originalShard = coll
|
|
.getDB()
|
|
.getSiblingDB("config")
|
|
.chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard;
|
|
let destShard = getShardNames(cluster)[1];
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({moveRange: fullNs, min: keyToMove, toShard: destShard}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard,
|
|
destShard,
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once moveChunk no
|
|
// longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
let keyMoved = getShardKeyMinRanges(coll)[0]["min"];
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
|
|
getShardNames(cluster)[1],
|
|
);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({moveRange: fullNs, min: keyMoved, toShard: optionalArgs.originalShard}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
|
|
optionalArgs.originalShard,
|
|
);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
multicast: {skip: "does not accept write concern"},
|
|
netstat: {skip: "internal command"},
|
|
oidcListKeys: {skip: "does not accept write concern"},
|
|
oidcRefreshKeys: {skip: "does not accept write concern"},
|
|
pinHistoryReplicated: {skip: "internal command"},
|
|
ping: {skip: "does not accept write concern"},
|
|
planCacheClear: {skip: "does not accept write concern"},
|
|
planCacheClearFilters: {skip: "does not accept write concern"},
|
|
planCacheListFilters: {skip: "does not accept write concern"},
|
|
planCacheSetFilter: {skip: "does not accept write concern"},
|
|
prepareTransaction: {skip: "internal command"},
|
|
profile: {skip: "does not accept write concern"},
|
|
reIndex: {skip: "does not accept write concern"},
|
|
reapLogicalSessionCacheNow: {skip: "does not accept write concern"},
|
|
refineCollectionShardKey: {
|
|
noop: {
|
|
// Refine to same shard key
|
|
req: (cluster, coll) => ({refineCollectionShardKey: fullNs, key: getShardKey(coll, fullNs)}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({x: 1}));
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Add additional field to shard key
|
|
req: (cluster, coll) => ({
|
|
refineCollectionShardKey: fullNs,
|
|
key: Object.assign({}, getShardKey(coll, fullNs), {newSkField: 1}),
|
|
}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
optionalArgs.origSk = sk;
|
|
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), sk), 0);
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [
|
|
{
|
|
key: Object.assign({}, getShardKey(coll, fullNs), {newSkField: 1}),
|
|
name: "sk_1",
|
|
},
|
|
],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(
|
|
bsonWoCompare(
|
|
getShardKey(coll, fullNs),
|
|
Object.assign({}, getShardKey(coll, fullNs), {newSkField: 1}),
|
|
),
|
|
0,
|
|
);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({refineCollectionShardKey: fullNs, key: optionalArgs.sk}),
|
|
);
|
|
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), optionalArgs.sk), 0);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
refreshLogicalSessionCacheNow: {skip: "does not accept write concern"},
|
|
refreshSessions: {skip: "does not accept write concern"},
|
|
releaseMemory: {skip: "does not accept write concern"},
|
|
removeShard: {skip: "unrelated"},
|
|
removeShardFromZone: {skip: "does not accept write concern"},
|
|
renameCollection: {
|
|
success: {
|
|
// Basic rename
|
|
req: {
|
|
renameCollection: fullNs,
|
|
to: dbName + "." + "coll2",
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
assert.eq(coll.getDB().coll2.find().itcount(), 1);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
coll.getDB().coll2.drop();
|
|
},
|
|
admin: true,
|
|
},
|
|
failure: {
|
|
// target collection exists, and dropTarget not set to true
|
|
req: {
|
|
renameCollection: fullNs,
|
|
to: dbName + "." + "coll2",
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.getDB().coll2.insert({a: 1}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.getDB().coll2.find().itcount(), 1);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.NamespaceExists);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.getDB().coll2.find().itcount(), 1);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
repairShardedCollectionChunksHistory: {skip: "does not accept write concern"},
|
|
replicateSearchIndexCommand: {skip: "internal command for testing only"},
|
|
replSetAbortPrimaryCatchUp: {skip: "does not accept write concern"},
|
|
replSetFreeze: {skip: "does not accept write concern"},
|
|
replSetGetConfig: {skip: "does not accept write concern"},
|
|
replSetGetRBID: {skip: "does not accept write concern"},
|
|
replSetGetStatus: {skip: "does not accept write concern"},
|
|
replSetHeartbeat: {skip: "does not accept write concern"},
|
|
replSetInitiate: {skip: "does not accept write concern"},
|
|
replSetMaintenance: {skip: "does not accept write concern"},
|
|
replSetReconfig: {skip: "does not accept write concern"},
|
|
replSetRequestVotes: {skip: "does not accept write concern"},
|
|
replSetResizeOplog: {skip: "does not accept write concern"},
|
|
replSetStepDown: {skip: "does not accept write concern"},
|
|
replSetStepUp: {skip: "does not accept write concern"},
|
|
replSetSyncFrom: {skip: "does not accept write concern"},
|
|
replSetTest: {skip: "does not accept write concern"},
|
|
replSetTestEgress: {skip: "does not accept write concern"},
|
|
replSetUpdatePosition: {skip: "does not accept write concern"},
|
|
resetPlacementHistory: {skip: "internal command"},
|
|
reshardCollection: {skip: "does not accept write concern"},
|
|
rewriteCollection: {skip: "does not accept write concern"},
|
|
revokePrivilegesFromRole: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Role does not have privilege
|
|
req: {
|
|
revokePrivilegesFromRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["insert"]}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["insert"]}],
|
|
roles: [],
|
|
}),
|
|
);
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
revokePrivilegesFromRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["insert"]}],
|
|
}),
|
|
);
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createUser: "fakeusr",
|
|
pwd: "bar",
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropUser("fakeusr");
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic revokePrivilegesFromRole
|
|
req: {
|
|
revokePrivilegesFromRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
grantPrivilegesToRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
}),
|
|
);
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
|
|
assert.eq(role.length, 1);
|
|
assert.eq(role[0].privileges.length, 0);
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
},
|
|
revokeRolesFromRole: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Role foo does not have role bar
|
|
req: {revokeRolesFromRole: "foo", roles: [{role: "bar", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
roles: [],
|
|
}),
|
|
);
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createUser: "fakeusr",
|
|
pwd: "bar",
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropUser("fakeusr");
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().dropRole("bar");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic revokeRolesFromRole
|
|
req: {revokeRolesFromRole: "foo", roles: [{role: "bar", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
roles: [],
|
|
}),
|
|
);
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]}),
|
|
);
|
|
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 1);
|
|
assert.eq(role.inheritedRoles[0].role, "bar");
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 0);
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().dropRole("bar");
|
|
},
|
|
},
|
|
},
|
|
revokeRolesFromUser: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// User does not have role to revoke
|
|
req: {revokeRolesFromUser: "foo", roles: [{role: "foo", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropRole("bar");
|
|
}
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().runCommand({dropUser: "foo"});
|
|
},
|
|
},
|
|
success: {
|
|
// Basic revokeRolesFromUser
|
|
req: {revokeRolesFromUser: "foo", roles: [{role: "foo", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]}),
|
|
);
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 0);
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().runCommand({dropUser: "foo"});
|
|
},
|
|
},
|
|
},
|
|
rolesInfo: {skip: "does not accept write concern"},
|
|
rotateCertificates: {skip: "does not accept write concern"},
|
|
rotateFTDC: {skip: "does not accept write concern"},
|
|
saslContinue: {skip: "does not accept write concern"},
|
|
saslStart: {skip: "does not accept write concern"},
|
|
sbe: {skip: "internal command"},
|
|
serverStatus: {skip: "does not accept write concern"},
|
|
setAllowMigrations: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Migrations already not allowed
|
|
req: {setAllowMigrations: fullNs, allowMigrations: false},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().adminCommand({setAllowMigrations: fullNs, allowMigrations: false}));
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").collections.findOne({_id: fullNs}).permitMigrations,
|
|
false,
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
|
|
// no longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("config").collections.findOne({_id: fullNs}).permitMigrations,
|
|
false,
|
|
);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic setAllowMigrations
|
|
req: {setAllowMigrations: fullNs, allowMigrations: false},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().adminCommand({setAllowMigrations: fullNs, allowMigrations: true}));
|
|
assert.eq(
|
|
coll
|
|
.getDB()
|
|
.getSiblingDB("config")
|
|
.collections.find({_id: fullNs, permitMigrations: {$exists: false}})
|
|
.itcount(),
|
|
1,
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
|
|
// no longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(
|
|
coll
|
|
.getDB()
|
|
.getSiblingDB("config")
|
|
.collections.find({_id: fullNs, permitMigrations: {$exists: false}})
|
|
.itcount(),
|
|
0,
|
|
);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
setAuditConfig: {skip: "does not accept write concern"},
|
|
setCommittedSnapshot: {skip: "internal command"},
|
|
setDefaultRWConcern: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Default write concern already set to w:1
|
|
req: {setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 0}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 0}}),
|
|
);
|
|
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
|
|
"w": 1,
|
|
"wtimeout": 0,
|
|
});
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
|
|
// no longer override user provided writeConcern
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
|
|
"w": 1,
|
|
"wtimeout": 0,
|
|
});
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
}
|
|
|
|
// Reset to default of majority
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({
|
|
setDefaultRWConcern: 1,
|
|
defaultWriteConcern: {"w": "majority", "wtimeout": 0},
|
|
}),
|
|
);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Default RWConcern has wtimeout of 1234
|
|
req: {setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 0}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll
|
|
.getDB()
|
|
.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 1234}}),
|
|
);
|
|
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
|
|
"w": 1,
|
|
"wtimeout": 1234,
|
|
});
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
|
|
// no longer override user provided writeConcern
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
}
|
|
// Reset to default of majority
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({
|
|
setDefaultRWConcern: 1,
|
|
defaultWriteConcern: {"w": "majority", "wtimeout": 0},
|
|
}),
|
|
);
|
|
},
|
|
admin: true,
|
|
},
|
|
failure: {
|
|
// Default write concern cannot be unset once it is set
|
|
req: {setDefaultRWConcern: 1, defaultWriteConcern: {}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll
|
|
.getDB()
|
|
.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 1234}}),
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
|
|
// no longer override user provided writeConcern
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
|
|
// Unacknowledged write on the CSRS to force a WCE upon command failure
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll
|
|
.getDB()
|
|
.getSiblingDB("config")
|
|
.tmp.insert({x: 1}, {writeConcern: {w: "majority", wtimeout: 1000}}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.IllegalOperation);
|
|
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
|
|
"w": 1,
|
|
"wtimeout": 1234,
|
|
});
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
}
|
|
// Reset to default of majority
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({
|
|
setDefaultRWConcern: 1,
|
|
defaultWriteConcern: {"w": "majority", "wtimeout": 0},
|
|
}),
|
|
);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
setFeatureCompatibilityVersion: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// FCV already 'latest'
|
|
req: {setFeatureCompatibilityVersion: latestFCV, confirm: true},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let fcv = coll
|
|
.getDB()
|
|
.getSiblingDB("admin")
|
|
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version;
|
|
optionalArgs.fcv = fcv;
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({setFeatureCompatibilityVersion: latestFCV, confirm: true}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
|
|
.version,
|
|
latestFCV,
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
|
|
// no longer override user provided writeConcern
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
|
|
.version,
|
|
latestFCV,
|
|
);
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({setFeatureCompatibilityVersion: optionalArgs.fcv, confirm: true}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
|
|
.version,
|
|
optionalArgs.fcv,
|
|
);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
}
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Change FCV from lastLTS to latest
|
|
req: {setFeatureCompatibilityVersion: latestFCV, confirm: true},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let fcv = coll
|
|
.getDB()
|
|
.getSiblingDB("admin")
|
|
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version;
|
|
optionalArgs.fcv = fcv;
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({setFeatureCompatibilityVersion: lastLTSFCV, confirm: true}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
|
|
.version,
|
|
lastLTSFCV,
|
|
);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
|
|
// no longer override user provided writeConcern
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
assert.eq(
|
|
coll
|
|
.getDB()
|
|
.getSiblingDB("admin")
|
|
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version,
|
|
lastLTSFCV,
|
|
);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(
|
|
coll
|
|
.getDB()
|
|
.getSiblingDB("admin")
|
|
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version,
|
|
latestFCV,
|
|
);
|
|
}
|
|
|
|
// Reset FCV
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({setFeatureCompatibilityVersion: optionalArgs.fcv, confirm: true}),
|
|
);
|
|
assert.eq(
|
|
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
|
|
.version,
|
|
optionalArgs.fcv,
|
|
);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
setProfilingFilterGlobally: {skip: "does not accept write concern"},
|
|
setIndexCommitQuorum: {
|
|
noop: {
|
|
// commitQuorum already majority
|
|
req: {setIndexCommitQuorum: collName, indexNames: ["b_1"], commitQuorum: "majority"},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1, b: 1}));
|
|
|
|
let fpConn = coll.getDB();
|
|
if (clusterType == "sharded") {
|
|
// Make sure we set the fp on a shard that will be sent the createIndex request
|
|
let owningShard = cluster.getShard(coll, {_id: 1, b: 1}, false /* includeEmpty */);
|
|
fpConn = owningShard.getDB(dbName);
|
|
}
|
|
|
|
optionalArgs.failpoint = configureFailPoint(fpConn, "hangAfterIndexBuildFirstDrain");
|
|
optionalArgs.thread = new Thread(
|
|
(host, dbName, collName) => {
|
|
const conn = new Mongo(host);
|
|
assert.commandWorked(
|
|
conn.getDB(dbName).runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
},
|
|
coll.getDB().getMongo().host,
|
|
dbName,
|
|
collName,
|
|
);
|
|
|
|
optionalArgs.thread.start();
|
|
optionalArgs.failpoint["wait"]();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (res.raw) {
|
|
Object.keys(res.raw).forEach((key) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrorsOrFailedWithCode(
|
|
res.raw[key],
|
|
ErrorCodes.IndexNotFound,
|
|
);
|
|
});
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
|
|
optionalArgs.failpoint["off"]();
|
|
optionalArgs.thread.join();
|
|
},
|
|
},
|
|
success: {
|
|
// Basic setIndexCommitQuorum
|
|
req: {setIndexCommitQuorum: collName, indexNames: ["b_1"], commitQuorum: 2},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
|
|
let fpConn = coll.getDB();
|
|
if (clusterType == "sharded") {
|
|
// Make sure we set the fp on a shard that will be sent the createIndex request
|
|
let owningShard = cluster.getShard(coll, {_id: 1} /* includeEmpty */);
|
|
fpConn = owningShard.getDB(dbName);
|
|
}
|
|
optionalArgs.failpoint = configureFailPoint(fpConn, "hangAfterIndexBuildFirstDrain");
|
|
optionalArgs.thread = new Thread(
|
|
(host, dbName, collName) => {
|
|
// Use the index builds coordinator for a two-phase index build.
|
|
const conn = new Mongo(host);
|
|
assert.commandWorked(
|
|
conn.getDB(dbName).runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
},
|
|
coll.getDB().getMongo().host,
|
|
dbName,
|
|
collName,
|
|
);
|
|
optionalArgs.thread.start();
|
|
|
|
optionalArgs.failpoint["wait"]();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (res.raw) {
|
|
Object.keys(res.raw).forEach((key) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrorsOrFailedWithCode(
|
|
res.raw[key],
|
|
ErrorCodes.IndexNotFound,
|
|
);
|
|
});
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
optionalArgs.failpoint["off"]();
|
|
optionalArgs.thread.join();
|
|
},
|
|
},
|
|
},
|
|
setParameter: {skip: "does not accept write concern"},
|
|
setShardVersion: {skip: "internal command"},
|
|
setChangeStreamState: {skip: "removed in v8.3"},
|
|
setClusterParameter: {skip: "does not accept write concern"},
|
|
setQuerySettings: {skip: "does not accept write concern"},
|
|
removeQuerySettings: {skip: "does not accept write concern"},
|
|
setUserWriteBlockMode: {skip: "does not accept write concern"},
|
|
shardCollection: {
|
|
noop: {
|
|
// Coll already sharded
|
|
req: {shardCollection: fullNs, key: {x: 1}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().adminCommand({shardCollection: fullNs, key: {x: 1}}));
|
|
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), {x: 1}), 0);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(getShardKey(coll, fullNs), {x: 1});
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic shard coll
|
|
req: {shardCollection: fullNs, key: {x: 1}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
// Ensure DB exists, but create a different collection
|
|
assert.commandWorked(coll.getDB().coll2.insert({_id: 1}));
|
|
|
|
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), {}), 0);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), {x: 1}), 0);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
shardDrainingStatus: {skip: "unrelated"},
|
|
shardingState: {skip: "does not accept write concern"},
|
|
shutdown: {skip: "does not accept write concern"},
|
|
sleep: {skip: "does not accept write concern"},
|
|
split: {skip: "does not accept write concern"},
|
|
splitChunk: {skip: "does not accept write concern"},
|
|
splitVector: {skip: "internal command"},
|
|
stageDebug: {skip: "does not accept write concern"},
|
|
startSession: {skip: "does not accept write concern"},
|
|
startShardDraining: {skip: "unrelated"},
|
|
startRecordingTraffic: {skip: "renamed to startTrafficRecording"},
|
|
stopRecordingTraffic: {skip: "renamed to stopTrafficRecording"},
|
|
stopShardDraining: {skip: "unrelated"},
|
|
startTrafficRecording: {skip: "does not accept write concern"},
|
|
startTransitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
stopTrafficRecording: {skip: "does not accept write concern"},
|
|
stopTransitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
sysprofile: {skip: "internal command"},
|
|
testCommandFeatureFlaggedOnLatestFCV83: {skip: "internal command"},
|
|
testDeprecation: {skip: "test command"},
|
|
testDeprecationInVersion2: {skip: "test command"},
|
|
testInternalTransactions: {skip: "internal command"},
|
|
testRemoval: {skip: "test command"},
|
|
testReshardCloneCollection: {skip: "internal command"},
|
|
testVersions1And2: {skip: "test command"},
|
|
testVersion2: {skip: "test command"},
|
|
timeseriesCatalogBucketParamsChanged: {skip: "internal command"},
|
|
top: {skip: "does not accept write concern"},
|
|
transitionFromDedicatedConfigServer: {skip: "unrelated"},
|
|
transitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
transitionToShardedCluster: {skip: "internal command"},
|
|
unshardCollection: {skip: "does not accept write concern"},
|
|
untrackUnshardedCollection: {skip: "does not accept write concern"},
|
|
update: {
|
|
noop: {
|
|
// The query will not match any doc
|
|
req: {update: collName, updates: [{q: {_id: 1}, u: {b: 2}}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 0, b: 2}));
|
|
assert.commandWorked(coll.remove({_id: 0}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic update
|
|
req: {update: collName, updates: [{q: {_id: 1}, u: {$set: {b: 1}}}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert([{_id: 1}]));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 1);
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to update a doc that would fail the validator
|
|
req: {update: collName, updates: [{q: {_id: 1}, u: {_id: 1, c: 2}}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1, x: 1}));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {x: {$exists: true}}}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0]["code"], ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.count({_id: 1}), 1);
|
|
},
|
|
},
|
|
},
|
|
updateRole: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// updateRole to have privileges it already has
|
|
req: {
|
|
updateRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createRole: "foo",
|
|
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
|
|
roles: [],
|
|
}),
|
|
);
|
|
assert.eq(coll.getDB().getRoles().length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createUser: "fakeusr",
|
|
pwd: "bar",
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
assert.eq(coll.getDB().getRoles().length, 1);
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropUser("fakeusr");
|
|
}
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic updateRole to add inherited role
|
|
req: {updateRole: "foo", roles: ["bar"]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "bar", privileges: [], roles: []}));
|
|
assert.eq(coll.getDB().getRoles().length, 2);
|
|
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let role = coll.getDB().getRole("foo");
|
|
assert.eq(role.inheritedRoles.length, 1);
|
|
assert.eq(role.inheritedRoles[0]["role"], ["bar"]);
|
|
}
|
|
|
|
coll.getDB().dropRole("foo");
|
|
coll.getDB().dropRole("bar");
|
|
},
|
|
},
|
|
failure: {
|
|
// Creating cycle
|
|
req: {updateRole: "foo", roles: ["foo"]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.eq(coll.getDB().getRoles().length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createUser: "fakeusr",
|
|
pwd: "bar",
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.InvalidRoleModification);
|
|
assert.eq(coll.getDB().getRoles().length, 1);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropUser("fakeusr");
|
|
}
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
},
|
|
updateSearchIndex: {skip: "does not accept write concern"},
|
|
updateUser: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// user already has role
|
|
req: {updateUser: "foo", roles: [{role: "foo", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "pwd", roles: ["foo"]}));
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 1);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 1);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropRole("bar");
|
|
}
|
|
|
|
coll.getDB().dropUser("foo");
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic updateUser to cadd role
|
|
req: {updateUser: "foo", roles: [{role: "foo", db: dbName}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "pwd", roles: []}));
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 0);
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
let user = coll.getDB().getUser("foo");
|
|
assert.eq(user.roles.length, 1);
|
|
}
|
|
|
|
coll.getDB().dropUser("foo");
|
|
coll.getDB().dropRole("foo");
|
|
},
|
|
},
|
|
failure: {
|
|
// Role does not exist
|
|
req: {updateUser: "foo", roles: ["fakeRole"]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "pwd", roles: []}));
|
|
|
|
// UMCs enforce wc: majority, so shut down the other node
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
// Run this to advance the system optime on the config server, so that the
|
|
// subsequent failing request will also encounter a WriteConcernTimeout.
|
|
assert.commandFailedWithCode(
|
|
coll.getDB().runCommand({
|
|
createRole: "bar",
|
|
privileges: [],
|
|
roles: [],
|
|
writeConcern: {w: "majority", wtimeout: 100},
|
|
}),
|
|
ErrorCodes.WriteConcernTimeout,
|
|
);
|
|
}
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.RoleNotFound);
|
|
assert.eq(coll.getDB().getUser("foo").roles.length, 0);
|
|
|
|
if (clusterType == "sharded") {
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
coll.getDB().dropRole("bar");
|
|
}
|
|
coll.getDB().dropUser("foo");
|
|
},
|
|
},
|
|
},
|
|
updateZoneKeyRange: {skip: "does not accept write concern"},
|
|
usersInfo: {skip: "does not accept write concern"},
|
|
validate: {skip: "does not accept write concern"},
|
|
validateDBMetadata: {skip: "does not accept write concern"},
|
|
voteAbortIndexBuild: {skip: "internal command"},
|
|
voteCommitImportCollection: {skip: "internal command"},
|
|
voteCommitIndexBuild: {skip: "internal command"},
|
|
waitForFailPoint: {skip: "test command"},
|
|
getShardingReady: {skip: "internal command"},
|
|
whatsmysni: {skip: "does not accept write concern"},
|
|
whatsmyuri: {skip: "internal command"},
|
|
};
|
|
|
|
// All commands applicable on timeseries views in the server.
|
|
|
|
const wcTimeseriesViewsCommandsTests = {
|
|
_addShard: {skip: "internal command"},
|
|
_cloneCollectionOptionsFromPrimaryShard: {skip: "internal command"},
|
|
_clusterQueryWithoutShardKey: {skip: "internal command"},
|
|
_clusterWriteWithoutShardKey: {skip: "internal command"},
|
|
_configsvrAbortReshardCollection: {skip: "internal command"},
|
|
_configsvrAddShard: {skip: "internal command"},
|
|
_configsvrAddShardToZone: {skip: "internal command"},
|
|
_configsvrBalancerCollectionStatus: {skip: "internal command"},
|
|
_configsvrBalancerStart: {skip: "internal command"},
|
|
_configsvrBalancerStatus: {skip: "internal command"},
|
|
_configsvrBalancerStop: {skip: "internal command"},
|
|
_configsvrCheckClusterMetadataConsistency: {skip: "internal command"},
|
|
_configsvrCheckMetadataConsistency: {skip: "internal command"},
|
|
_configsvrCleanupReshardCollection: {skip: "internal command"},
|
|
_configsvrCollMod: {skip: "internal command"},
|
|
_configsvrClearJumboFlag: {skip: "internal command"},
|
|
_configsvrCommitChunksMerge: {skip: "internal command"},
|
|
_configsvrCommitChunkMigration: {skip: "internal command"},
|
|
_configsvrCommitChunkSplit: {skip: "internal command"},
|
|
_configsvrCommitMergeAllChunksOnShard: {skip: "internal command"},
|
|
_configsvrCommitMovePrimary: {skip: "internal command"},
|
|
_configsvrCommitRefineCollectionShardKey: {skip: "internal command"},
|
|
_configsvrCommitReshardCollection: {skip: "internal command"},
|
|
_configsvrCommitShardRemoval: {skip: "internal command"},
|
|
_configsvrConfigureCollectionBalancing: {skip: "internal command"},
|
|
_configsvrCreateDatabase: {skip: "internal command"},
|
|
_configsvrEnsureChunkVersionIsGreaterThan: {skip: "internal command"},
|
|
_configsvrGetHistoricalPlacement: {skip: "internal command"},
|
|
_configsvrMoveRange: {skip: "internal command"},
|
|
_configsvrRemoveChunks: {skip: "internal command"},
|
|
_configsvrRemoveShard: {skip: "internal command"},
|
|
_configsvrRemoveShardCommit: {skip: "internal command"},
|
|
_configsvrRemoveShardFromZone: {skip: "internal command"},
|
|
_configsvrRemoveTags: {skip: "internal command"},
|
|
_configsvrRenameCollection: {skip: "internal command"},
|
|
_configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"},
|
|
_configsvrResetPlacementHistory: {skip: "internal command"},
|
|
_configsvrReshardCollection: {skip: "internal command"},
|
|
_configsvrRunRestore: {skip: "internal command"},
|
|
_configsvrSetAllowMigrations: {skip: "internal command"},
|
|
_configsvrSetClusterParameter: {skip: "internal command"},
|
|
_configsvrSetUserWriteBlockMode: {skip: "internal command"},
|
|
_configsvrShardDrainingStatus: {skip: "internal command"},
|
|
_configsvrStartShardDraining: {skip: "internal command"},
|
|
_configsvrStopShardDraining: {skip: "internal command"},
|
|
_configsvrTransitionFromDedicatedConfigServer: {skip: "internal command"},
|
|
_configsvrTransitionToDedicatedConfigServer: {skip: "internal command"},
|
|
_configsvrUpdateZoneKeyRange: {skip: "internal command"},
|
|
_dropConnectionsToMongot: {skip: "internal command"},
|
|
_dropMirrorMaestroConnections: {skip: "internal command"},
|
|
_flushDatabaseCacheUpdates: {skip: "internal command"},
|
|
_flushDatabaseCacheUpdatesWithWriteConcern: {skip: "internal command"},
|
|
_flushReshardingStateChange: {skip: "internal command"},
|
|
_flushRoutingTableCacheUpdates: {skip: "internal command"},
|
|
_flushRoutingTableCacheUpdatesWithWriteConcern: {skip: "internal command"},
|
|
_getNextSessionMods: {skip: "internal command"},
|
|
_getUserCacheGeneration: {skip: "internal command"},
|
|
_hashBSONElement: {skip: "internal command"},
|
|
_isSelf: {skip: "internal command"},
|
|
_killOperations: {skip: "internal command"},
|
|
_mergeAuthzCollections: {skip: "internal command"},
|
|
_migrateClone: {skip: "internal command"},
|
|
_mirrorMaestroConnPoolStats: {skip: "internal command"},
|
|
_mongotConnPoolStats: {skip: "internal command"},
|
|
_recvChunkAbort: {skip: "internal command"},
|
|
_recvChunkCommit: {skip: "internal command"},
|
|
_recvChunkReleaseCritSec: {skip: "internal command"},
|
|
_recvChunkStart: {skip: "internal command"},
|
|
_recvChunkStatus: {skip: "internal command"},
|
|
_refreshQueryAnalyzerConfiguration: {skip: "internal command"},
|
|
_shardsvrAbortReshardCollection: {skip: "internal command"},
|
|
_shardsvrBeginMigrationBlockingOperation: {skip: "internal command"},
|
|
_shardsvrChangePrimary: {skip: "internal command"},
|
|
_shardsvrCleanupReshardCollection: {skip: "internal command"},
|
|
_shardsvrCloneCatalogData: {skip: "internal command"},
|
|
_shardsvrCommitToShardLocalCatalog: {skip: "internal command"},
|
|
_shardsvrCheckMetadataConsistency: {skip: "internal command"},
|
|
_shardsvrCheckMetadataConsistencyParticipant: {skip: "internal command"},
|
|
_shardsvrCleanupStructuredEncryptionData: {skip: "internal command"},
|
|
_shardsvrCloneAuthoritativeMetadata: {skip: "internal command"},
|
|
_shardsvrCommitCreateDatabaseMetadata: {skip: "internal command"},
|
|
_shardsvrCommitDropDatabaseMetadata: {skip: "internal command"},
|
|
_shardsvrCommitReshardCollection: {skip: "internal command"},
|
|
_shardsvrCompactStructuredEncryptionData: {skip: "internal command"},
|
|
_shardsvrConvertToCapped: {skip: "internal command"},
|
|
_shardsvrCoordinateMultiUpdate: {skip: "internal command"},
|
|
_shardsvrCreateCollection: {skip: "internal command"},
|
|
_shardsvrCreateCollectionParticipant: {skip: "internal command"},
|
|
_shardsvrDropCollection: {skip: "internal command"},
|
|
_shardsvrDropCollectionIfUUIDNotMatchingWithWriteConcern: {skip: "internal command"},
|
|
_shardsvrDropCollectionParticipant: {skip: "internal command"},
|
|
_shardsvrDropIndexes: {skip: "internal command"},
|
|
_shardsvrDropIndexesParticipant: {skip: "internal command"},
|
|
_shardsvrDrainOngoingDDLOperations: {skip: "internal command"},
|
|
_shardsvrDropDatabase: {skip: "internal command"},
|
|
_shardsvrDropDatabaseParticipant: {skip: "internal command"},
|
|
_shardsvrEndMigrationBlockingOperation: {skip: "internal command"},
|
|
_shardsvrFetchCollMetadata: {skip: "internal command"},
|
|
_shardsvrGetStatsForBalancing: {skip: "internal command"},
|
|
_shardsvrCheckCanConnectToConfigServer: {skip: "internal command"},
|
|
_shardsvrJoinDDLCoordinators: {skip: "internal command"},
|
|
_shardsvrJoinMigrations: {skip: "internal command"},
|
|
_shardsvrMergeAllChunksOnShard: {skip: "internal command"},
|
|
_shardsvrMergeChunks: {skip: "internal command"},
|
|
_shardsvrMovePrimary: {skip: "internal command"},
|
|
_shardsvrMovePrimaryEnterCriticalSection: {skip: "internal command"},
|
|
_shardsvrMovePrimaryExitCriticalSection: {skip: "internal command"},
|
|
_shardsvrMoveRange: {skip: "internal command"},
|
|
_shardsvrNotifyShardingEvent: {skip: "internal command"},
|
|
_shardsvrRefineCollectionShardKey: {skip: "internal command"},
|
|
_shardsvrRenameCollection: {skip: "internal command"},
|
|
_shardsvrRenameCollectionParticipant: {skip: "internal command"},
|
|
_shardsvrRenameCollectionParticipantUnblock: {skip: "internal command"},
|
|
_shardsvrRenameIndexMetadata: {skip: "internal command"},
|
|
_shardsvrReshardCollection: {skip: "internal command"},
|
|
_shardsvrReshardingDonorFetchFinalCollectionStats: {skip: "internal command"},
|
|
_shardsvrReshardingDonorStartChangeStreamsMonitor: {skip: "internal command"},
|
|
_shardsvrReshardingOperationTime: {skip: "internal command"},
|
|
_shardsvrReshardRecipientClone: {skip: "internal command"},
|
|
_shardsvrResolveView: {skip: "internal command"},
|
|
_shardsvrRunSearchIndexCommand: {skip: "internal command"},
|
|
_shardsvrSetAllowMigrations: {skip: "internal command"},
|
|
_shardsvrSetClusterParameter: {skip: "internal command"},
|
|
_shardsvrSetUserWriteBlockMode: {skip: "internal command"},
|
|
_shardsvrValidateShardKeyCandidate: {skip: "internal command"},
|
|
_shardsvrCollMod: {skip: "internal command"},
|
|
_shardsvrCollModParticipant: {skip: "internal command"},
|
|
_shardsvrConvertToCappedParticipant: {skip: "internal command"},
|
|
_shardsvrParticipantBlock: {skip: "internal command"},
|
|
_shardsvrUntrackUnsplittableCollection: {skip: "internal command"},
|
|
streams_startStreamProcessor: {skip: "internal command"},
|
|
streams_startStreamSample: {skip: "internal command"},
|
|
streams_stopStreamProcessor: {skip: "internal command"},
|
|
streams_listStreamProcessors: {skip: "internal command"},
|
|
streams_getMoreStreamSample: {skip: "internal command"},
|
|
streams_getStats: {skip: "internal command"},
|
|
streams_testOnlyInsert: {skip: "internal command"},
|
|
streams_getMetrics: {skip: "internal command"},
|
|
streams_updateFeatureFlags: {skip: "internal command"},
|
|
streams_testOnlyGetFeatureFlags: {skip: "internal command"},
|
|
streams_writeCheckpoint: {skip: "internal command"},
|
|
streams_sendEvent: {skip: "internal command"},
|
|
streams_updateConnection: {skip: "internal command"},
|
|
_transferMods: {skip: "internal command"},
|
|
abortMoveCollection: {skip: "does not accept write concern"},
|
|
abortReshardCollection: {skip: "does not accept write concern"},
|
|
abortRewriteCollection: {skip: "does not accept write concern"},
|
|
abortTransaction: {skip: "not supported on timeseries views"},
|
|
abortUnshardCollection: {skip: "does not accept write concern"},
|
|
addShard: {skip: "unrelated"},
|
|
addShardToZone: {skip: "does not accept write concern"},
|
|
aggregate: {
|
|
noop: {
|
|
// The pipeline will not match any docs, so nothing should be written to "out"
|
|
req: {
|
|
aggregate: collName,
|
|
pipeline: [{$match: {"meta.x": 1}}, {$out: "out"}],
|
|
cursor: {},
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
|
|
assert.commandWorked(coll.deleteMany({meta: {x: 1, y: 1}}));
|
|
assert.eq(coll.count({meta: {x: 1, y: 1}}), 0);
|
|
assert.commandWorked(coll.getDB().createCollection("out"));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
}
|
|
assert.eq(coll.getDB().out.find().itcount(), 0);
|
|
coll.getDB().out.drop();
|
|
},
|
|
},
|
|
success: {
|
|
// The pipeline should write a single doc to "out"
|
|
req: {
|
|
aggregate: collName,
|
|
pipeline: [{$match: {"meta.x": 1}}, {$out: "out"}],
|
|
cursor: {},
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
|
|
assert.commandWorked(coll.getDB().createCollection("out"));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().out.find().itcount(), 0);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().out.find().itcount(), 1);
|
|
}
|
|
assert.eq(coll.count({meta: {x: 1, y: 1}}), 1);
|
|
coll.getDB().out.drop();
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to update non meta field
|
|
req: {
|
|
aggregate: collName,
|
|
pipeline: [{$match: {"meta.x": 1}}, {$set: {time: "deadbeef"}}, {$out: "out"}],
|
|
cursor: {},
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorked(coll.insert({meta: 2, time: timeValue}));
|
|
assert.commandWorked(
|
|
coll.getDB().createCollection("out", {timeseries: {timeField: "time", metaField: "meta"}}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().out.find().itcount(), 0);
|
|
coll.getDB().out.drop();
|
|
},
|
|
},
|
|
},
|
|
// TODO SPM-2513 Define test case for analyze once the command is user facing
|
|
analyze: {skip: "internal only"},
|
|
analyzeShardKey: {skip: "does not accept write concern"},
|
|
appendOplogNote: {
|
|
success: {
|
|
// appendOplogNote basic
|
|
req: {appendOplogNote: 1, data: {meta: 1}},
|
|
setupFunc: (coll) => {},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
applyOps: {
|
|
noop: {
|
|
// 'applyOps' where the update is a no-op
|
|
req: {applyOps: [{op: "u", ns: fullNs, o: {meta: 1, _id: 0}, o2: {meta: 1}}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.results[0], true);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.CommandNotSupportedOnView);
|
|
}
|
|
assert.eq(res.applied, 1);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({meta: 1}), 1);
|
|
},
|
|
},
|
|
success: {
|
|
// 'applyOps' basic insert
|
|
req: {applyOps: [{op: "i", ns: fullNs, o: {meta: 2, time: timeValue}}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.results[0], true);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
assert.eq(coll.count({time: timeValue}), 2);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.CommandNotSupportedOnView);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({time: timeValue}), 1);
|
|
}
|
|
assert.eq(res.applied, 1);
|
|
},
|
|
},
|
|
failure: {
|
|
// 'applyOps' attempt to update to bad value
|
|
req: {
|
|
applyOps: [{op: "u", ns: fullNs, o: {time: timeValue, _id: 0}, o2: {time: "deadbeef"}}],
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue, _id: 0}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.eq(res.applied, 1);
|
|
assert.eq(res.ok, 0);
|
|
assert.eq(res.results[0], false);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({meta: 1}), 1);
|
|
},
|
|
},
|
|
},
|
|
authenticate: {skip: "does not accept write concern"},
|
|
autoCompact: {skip: "does not accept write concern"},
|
|
autoSplitVector: {skip: "does not accept write concern"},
|
|
balancerCollectionStatus: {skip: "does not accept write concern"},
|
|
balancerStart: {skip: "does not accept write concern"},
|
|
balancerStatus: {skip: "does not accept write concern"},
|
|
balancerStop: {skip: "does not accept write concern"},
|
|
buildInfo: {skip: "does not accept write concern"},
|
|
bulkWrite: {
|
|
noop: {
|
|
// The doc to update doesn't exist
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{
|
|
update: 0,
|
|
multi: true,
|
|
filter: {"meta.x": 0},
|
|
updateMods: {$set: {"meta.y": 1}},
|
|
},
|
|
],
|
|
nsInfo: [{ns: fullNs}],
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nModified, 0);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic insert in a bulk write
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [{insert: 0, document: {meta: 2, time: timeValue}}],
|
|
nsInfo: [{ns: fullNs}],
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: ISODate()}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nInserted, 1);
|
|
},
|
|
admin: true,
|
|
},
|
|
failure: {
|
|
// Attempt to update doc with bad time value
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": 1},
|
|
multi: true,
|
|
updateMods: {$set: {time: "deadbeef"}},
|
|
},
|
|
],
|
|
nsInfo: [{ns: fullNs}],
|
|
},
|
|
setupFunc: (coll, cluster) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: ISODate()}));
|
|
},
|
|
confirmFunc: (res, coll, cluster) => {
|
|
assert.eq(res.nErrors, 1);
|
|
assert(res.cursor && res.cursor.firstBatch && res.cursor.firstBatch.length == 1);
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.cursor.firstBatch[0].code);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
changePrimary: {
|
|
noop: {
|
|
// The destination shard is already the primary shard for the db
|
|
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[0]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic change primary
|
|
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[1]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard1.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
// Change the primary back
|
|
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: cluster.shard0.shardName}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
checkMetadataConsistency: {skip: "does not accept write concern"},
|
|
checkShardingIndex: {skip: "does not accept write concern"},
|
|
cleanupOrphaned: {skip: "only exist on direct shard connection"},
|
|
cleanupReshardCollection: {skip: "does not accept write concern"},
|
|
cleanupStructuredEncryptionData: {skip: "does not accept write concern"},
|
|
clearJumboFlag: {skip: "does not accept write concern"},
|
|
clearLog: {skip: "does not accept write concern"},
|
|
cloneCollectionAsCapped: {skip: "not supported on timeseries views"},
|
|
clusterAbortTransaction: {skip: "already tested by 'abortTransaction' tests on mongos"},
|
|
clusterAggregate: {skip: "already tested by 'aggregate' tests on mongos"},
|
|
clusterBulkWrite: {skip: "already tested by 'bulkWrite' tests on mongos"},
|
|
clusterCommitTransaction: {skip: "already tested by 'commitTransaction' tests on mongos"},
|
|
clusterCount: {skip: "already tested by 'count' tests on mongos"},
|
|
clusterDelete: {skip: "already tested by 'delete' tests on mongos"},
|
|
clusterFind: {skip: "already tested by 'find' tests on mongos"},
|
|
clusterGetMore: {skip: "already tested by 'getMore' tests on mongos"},
|
|
clusterInsert: {skip: "already tested by 'insert' tests on mongos"},
|
|
clusterUpdate: {skip: "already tested by 'update' tests on mongos"},
|
|
collMod: {
|
|
noop: {
|
|
// Set expireAfterSeconds off for already non expiring
|
|
req: {collMod: collName, expireAfterSeconds: "off"},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Add validator
|
|
req: {collMod: collName, index: {keyPattern: {a: 1}, hidden: true}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {a: 1}, name: "a_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getIndexes().length, 2);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
failure: {
|
|
// Index to be updated does not exist
|
|
req: {collMod: collName, index: {name: "a_1", hidden: true}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {a: 1}, name: "a_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({dropIndexes: collName, index: "a_1"}),
|
|
);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.IndexNotFound);
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
collStats: {skip: "does not accept write concern"},
|
|
commitReshardCollection: {skip: "does not accept write concern"},
|
|
commitShardRemoval: {skip: "unrelated"},
|
|
commitTransaction: {skip: "not supported on timeseries views"},
|
|
commitTransitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
compact: {skip: "does not accept write concern"},
|
|
compactStructuredEncryptionData: {skip: "does not accept write concern"},
|
|
configureCollectionBalancing: {skip: "does not accept write concern"},
|
|
configureFailPoint: {skip: "internal command"},
|
|
configureQueryAnalyzer: {skip: "does not accept write concern"},
|
|
connPoolStats: {skip: "does not accept write concern"},
|
|
connPoolSync: {skip: "internal command"},
|
|
connectionStatus: {skip: "does not accept write concern"},
|
|
convertToCapped: {skip: "not supported on timeseries views"},
|
|
coordinateCommitTransaction: {skip: "internal command"},
|
|
count: {skip: "does not accept write concern"},
|
|
cpuload: {skip: "does not accept write concern"},
|
|
create: {
|
|
noop: {
|
|
// Coll already exists
|
|
req: {create: collName, timeseries: {timeField: "time", metaField: "meta"}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
coll.insert({meta: 1, time: ISODate()});
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic create coll
|
|
req: {create: collName, timeseries: {timeField: "time", metaField: "meta"}},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert(coll.drop());
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to create a view and output to a nonexistent collection
|
|
req: {create: "viewWithOut", viewOn: collName, pipeline: [{$out: "nonexistentColl"}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.commandWorked(coll.getDB().runCommand({drop: "nonexistentColl"}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
if (clusterType == "sharded") {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.OptionNotSupportedOnView);
|
|
}
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert(!coll.getDB().getCollectionNames().includes("nonexistentColl"));
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
createIndexes: {
|
|
noop: {
|
|
// Index already exists
|
|
req: {
|
|
createIndexes: collName,
|
|
indexes: [{key: {a: 1}, name: "a_1"}],
|
|
commitQuorum: "majority",
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {a: 1}, name: "a_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let details = res;
|
|
if ("raw" in details) {
|
|
const raw = details.raw;
|
|
details = raw[Object.keys(raw)[0]];
|
|
}
|
|
assert.eq(details.numIndexesBefore, details.numIndexesAfter);
|
|
assert.eq(details.note, "all indexes already exist");
|
|
},
|
|
},
|
|
success: {
|
|
// Basic create index
|
|
req: {
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
let details = res;
|
|
if ("raw" in details) {
|
|
const raw = details.raw;
|
|
details = raw[Object.keys(raw)[0]];
|
|
}
|
|
assert.eq(details.numIndexesBefore, details.numIndexesAfter - 1);
|
|
},
|
|
},
|
|
failure: {
|
|
// Attempt to create two indexes with the same name and different keys
|
|
req: {
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: -1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.IndexKeySpecsConflict);
|
|
},
|
|
},
|
|
},
|
|
createRole: wcCommandsTests["createRole"],
|
|
createSearchIndexes: {skip: "does not accept write concern"},
|
|
createUnsplittableCollection: {skip: "internal command"},
|
|
createUser: wcCommandsTests["createUser"],
|
|
currentOp: {skip: "does not accept write concern"},
|
|
dataSize: {skip: "does not accept write concern"},
|
|
dbCheck: {skip: "does not accept write concern"},
|
|
dbHash: {skip: "does not accept write concern"},
|
|
dbStats: {skip: "does not accept write concern"},
|
|
delete: {
|
|
noop: {
|
|
// The query will not match any doc
|
|
req: {delete: collName, deletes: [{q: {"meta.x": 1}, limit: 0}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
|
|
assert.commandWorked(coll.remove({meta: {x: 1, y: 1}, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.count({meta: {x: 1, y: 1}}), 0);
|
|
},
|
|
},
|
|
success: {
|
|
req: {delete: collName, deletes: [{q: {"meta.x": 1}, limit: 0}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 1);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
},
|
|
distinct: {skip: "does not accept write concern"},
|
|
drop: {
|
|
noop: {
|
|
// The collection has already been dropped
|
|
req: {drop: collName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorked(coll.getDB().runCommand({drop: collName}));
|
|
assert.eq(coll.find().itcount(), 0);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic drop collection
|
|
req: {drop: collName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
dropAllRolesFromDatabase: wcCommandsTests["dropAllRolesFromDatabase"],
|
|
dropAllUsersFromDatabase: wcCommandsTests["dropAllUsersFromDatabase"],
|
|
dropConnections: {skip: "does not accept write concern"},
|
|
dropDatabase: {
|
|
noop: {
|
|
// Database has already been dropped
|
|
req: {dropDatabase: 1},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorkedIgnoringWriteConcernErrors(coll.getDB().runCommand({dropDatabase: 1}));
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic drop database
|
|
req: {dropDatabase: 1},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
dropIndexes: {
|
|
noop: {
|
|
// Passing "*" will drop all indexes except the index created for shard key in pre
|
|
// setup.
|
|
// The only index on this collection is that one so the command will be a no-op in
|
|
// sharded cluster. In replica sets, there is a default index created on the timeseries
|
|
// fields - this will be dropped in setup.
|
|
req: {
|
|
dropIndexes: collName,
|
|
index: "*",
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: "a", time: timeValue}));
|
|
assert.commandWorked(coll.getDB().runCommand({dropIndexes: collName, index: "*"}));
|
|
optionalArgs.numIndexesBefore = coll.getIndexes().length;
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
// Make a non-acknowledged write so that the no-op will have to fail with WCE
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.insert({meta: "a", time: timeValue}, {writeConcern: {w: "majority", wtimeout: 100}}),
|
|
);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic drop index
|
|
req: {
|
|
dropIndexes: collName,
|
|
index: "b_1",
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: "b", time: timeValue}));
|
|
|
|
const numIndexesBefore = coll.getIndexes().length;
|
|
optionalArgs.numIndexesBefore = numIndexesBefore;
|
|
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [{key: {b: 1}, name: "b_1"}],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
|
|
assert.eq(coll.getIndexes().length, numIndexesBefore + 1);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
},
|
|
},
|
|
dropRole: wcCommandsTests["dropRole"],
|
|
dropSearchIndex: {skip: "does not accept write concern"},
|
|
dropUser: wcCommandsTests["dropUser"],
|
|
echo: {skip: "does not accept write concern"},
|
|
enableSharding: {
|
|
targetConfigServer: true,
|
|
noop: {
|
|
// Sharding already enabled
|
|
req: {enableSharding: dbName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
|
|
assert.commandWorked(coll.getDB().adminCommand({enableSharding: dbName}));
|
|
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 1);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding
|
|
// no longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 1);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic enable sharding
|
|
req: {enableSharding: dbName},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
|
|
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 0);
|
|
|
|
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding
|
|
// no longer override user provided writeConcern
|
|
secondariesRunning[0].getDB("admin").fsyncLock();
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
|
|
secondariesRunning[0].getDB("admin").fsyncUnlock();
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
endSessions: {skip: "does not accept write concern"},
|
|
eseRotateActiveKEK: {skip: "does not accept write concern"},
|
|
explain: {skip: "does not accept write concern"},
|
|
features: {skip: "does not accept write concern"},
|
|
filemd5: {skip: "does not accept write concern"},
|
|
find: {skip: "does not accept write concern"},
|
|
findAndModify: {
|
|
// noop and success cases are not applicable because findAndModify does single
|
|
// updates and non-multi updates are not allowed on timeseries views
|
|
failure: {
|
|
req: {findAndModify: collName, query: {"meta.x": 1}, update: {time: "deadbeef"}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll, cluster) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.includes([ErrorCodes.InvalidOptions, ErrorCodes.BadValue], res.code);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({meta: {x: 1, y: 1}}), 1);
|
|
},
|
|
},
|
|
},
|
|
flushRouterConfig: {skip: "does not accept write concern"},
|
|
forceerror: {skip: "test command"},
|
|
fsync: {skip: "does not accept write concern"},
|
|
fsyncUnlock: {skip: "does not accept write concern"},
|
|
getAuditConfig: {skip: "does not accept write concern"},
|
|
getChangeStreamState: {skip: "removed in v8.3"},
|
|
getClusterParameter: {skip: "does not accept write concern"},
|
|
getCmdLineOpts: {skip: "does not accept write concern"},
|
|
getDatabaseVersion: {skip: "internal command"},
|
|
getDefaultRWConcern: {skip: "does not accept write concern"},
|
|
getDiagnosticData: {skip: "does not accept write concern"},
|
|
getESERotateActiveKEKStatus: {skip: "does not accept write concern"},
|
|
getLog: {skip: "does not accept write concern"},
|
|
getMore: {skip: "does not accept write concern"},
|
|
getParameter: {skip: "does not accept write concern"},
|
|
getQueryableEncryptionCountInfo: {skip: "does not accept write concern"},
|
|
getShardMap: {skip: "internal command"},
|
|
getShardVersion: {skip: "internal command"},
|
|
getTrafficRecordingStatus: {skip: "does not accept write concern"},
|
|
getTransitionToDedicatedConfigServerStatus: {skip: "unrelated"},
|
|
godinsert: {skip: "for testing only"},
|
|
grantPrivilegesToRole: wcCommandsTests["grantPrivilegesToRole"],
|
|
grantRolesToRole: wcCommandsTests["grantRolesToRole"],
|
|
grantRolesToUser: wcCommandsTests["grantRolesToUser"],
|
|
handshake: {skip: "does not accept write concern"},
|
|
hello: {skip: "does not accept write concern"},
|
|
hostInfo: {skip: "does not accept write concern"},
|
|
httpClientRequest: {skip: "does not accept write concern"},
|
|
exportCollection: {skip: "internal command"},
|
|
importCollection: {skip: "internal command"},
|
|
insert: {
|
|
// A no-op insert that returns success is not possible
|
|
success: {
|
|
// Basic insert
|
|
req: {insert: collName, documents: [{meta: 11, time: timeValue}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 11, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 1);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
},
|
|
},
|
|
internalRenameIfOptionsAndIndexesMatch: {skip: "internal command"},
|
|
invalidateUserCache: {skip: "does not accept write concern"},
|
|
isdbgrid: {skip: "does not accept write concern"},
|
|
isMaster: {skip: "does not accept write concern"},
|
|
killAllSessions: {skip: "does not accept write concern"},
|
|
killAllSessionsByPattern: {skip: "does not accept write concern"},
|
|
killCursors: {skip: "does not accept write concern"},
|
|
killOp: {skip: "does not accept write concern"},
|
|
killSessions: {skip: "does not accept write concern"},
|
|
listCollections: {skip: "does not accept write concern"},
|
|
listCommands: {skip: "does not accept write concern"},
|
|
listDatabases: {skip: "does not accept write concern"},
|
|
listDatabasesForAllTenants: {skip: "does not accept write concern"},
|
|
listIndexes: {skip: "does not accept write concern"},
|
|
listSearchIndexes: {skip: "does not accept write concern"},
|
|
listShards: {skip: "does not accept write concern"},
|
|
lockInfo: {skip: "does not accept write concern"},
|
|
logApplicationMessage: {skip: "does not accept write concern"},
|
|
logMessage: {skip: "does not accept write concern"},
|
|
logRotate: {skip: "does not accept write concern"},
|
|
logout: {skip: "does not accept write concern"},
|
|
makeSnapshot: {skip: "does not accept write concern"},
|
|
mapReduce: {skip: "deprecated"},
|
|
mergeAllChunksOnShard: {skip: "does not accept write concern"},
|
|
mergeChunks: {skip: "does not accept write concern"},
|
|
moveChunk: {skip: "not applicable on timeseries views"},
|
|
moveCollection: {skip: "does not accept write concern"},
|
|
movePrimary: {
|
|
noop: {
|
|
// The destination shard is already the primary shard for the db
|
|
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[0]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Basic movePrimary
|
|
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[1]}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
// Change the primary back
|
|
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: cluster.shard0.shardName}));
|
|
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
moveRange: {skip: "not applicable on timeseries views"},
|
|
multicast: {skip: "does not accept write concern"},
|
|
netstat: {skip: "internal command"},
|
|
oidcListKeys: {skip: "does not accept write concern"},
|
|
oidcRefreshKeys: {skip: "does not accept write concern"},
|
|
pinHistoryReplicated: {skip: "internal command"},
|
|
ping: {skip: "does not accept write concern"},
|
|
planCacheClear: {skip: "does not accept write concern"},
|
|
planCacheClearFilters: {skip: "does not accept write concern"},
|
|
planCacheListFilters: {skip: "does not accept write concern"},
|
|
planCacheSetFilter: {skip: "does not accept write concern"},
|
|
prepareTransaction: {skip: "internal command"},
|
|
profile: {skip: "does not accept write concern"},
|
|
reIndex: {skip: "does not accept write concern"},
|
|
reapLogicalSessionCacheNow: {skip: "does not accept write concern"},
|
|
refineCollectionShardKey: {
|
|
noop: {
|
|
// Refine to same shard key
|
|
req: (cluster, coll) => ({refineCollectionShardKey: fullNs, key: getShardKey(coll, fullNs)}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
// Add additional field to shard key
|
|
req: (cluster, coll) => ({
|
|
refineCollectionShardKey: fullNs,
|
|
key: Object.assign({}, getShardKey(coll, fullNs), {"meta.a": 1}),
|
|
}),
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
optionalArgs.origSk = sk;
|
|
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), sk), 0);
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().runCommand({
|
|
createIndexes: collName,
|
|
indexes: [
|
|
{
|
|
key: Object.assign({}, getShardKey(coll, fullNs), {"meta.a": 1}),
|
|
name: "sk_1",
|
|
},
|
|
],
|
|
commitQuorum: "majority",
|
|
}),
|
|
);
|
|
|
|
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(
|
|
bsonWoCompare(
|
|
getShardKey(coll, fullNs),
|
|
Object.assign({}, getShardKey(coll, fullNs), {"meta.a": 1}),
|
|
),
|
|
0,
|
|
);
|
|
|
|
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
|
|
|
|
assert.commandWorked(
|
|
coll.getDB().adminCommand({refineCollectionShardKey: fullNs, key: optionalArgs.sk}),
|
|
);
|
|
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), optionalArgs.sk), 0);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
refreshLogicalSessionCacheNow: {skip: "does not accept write concern"},
|
|
refreshSessions: {skip: "does not accept write concern"},
|
|
releaseMemory: {skip: "does not accept write concern"},
|
|
removeShard: {skip: "unrelated"},
|
|
removeShardFromZone: {skip: "does not accept write concern"},
|
|
renameCollection: {skip: "not supported on timeseries views"},
|
|
repairShardedCollectionChunksHistory: {skip: "does not accept write concern"},
|
|
replicateSearchIndexCommand: {skip: "internal command for testing only"},
|
|
replSetAbortPrimaryCatchUp: {skip: "does not accept write concern"},
|
|
replSetFreeze: {skip: "does not accept write concern"},
|
|
replSetGetConfig: {skip: "does not accept write concern"},
|
|
replSetGetRBID: {skip: "does not accept write concern"},
|
|
replSetGetStatus: {skip: "does not accept write concern"},
|
|
replSetHeartbeat: {skip: "does not accept write concern"},
|
|
replSetInitiate: {skip: "does not accept write concern"},
|
|
replSetMaintenance: {skip: "does not accept write concern"},
|
|
replSetReconfig: {skip: "does not accept write concern"},
|
|
replSetRequestVotes: {skip: "does not accept write concern"},
|
|
replSetResizeOplog: {skip: "does not accept write concern"},
|
|
replSetStepDown: {skip: "does not accept write concern"},
|
|
replSetStepUp: {skip: "does not accept write concern"},
|
|
replSetSyncFrom: {skip: "does not accept write concern"},
|
|
replSetTest: {skip: "does not accept write concern"},
|
|
replSetTestEgress: {skip: "does not accept write concern"},
|
|
replSetUpdatePosition: {skip: "does not accept write concern"},
|
|
resetPlacementHistory: {skip: "internal command"},
|
|
reshardCollection: {skip: "does not accept write concern"},
|
|
revokePrivilegesFromRole: wcCommandsTests["revokePrivilegesFromRole"],
|
|
revokeRolesFromRole: wcCommandsTests["revokeRolesFromRole"],
|
|
revokeRolesFromUser: wcCommandsTests["revokeRolesFromUser"],
|
|
rewriteCollection: {skip: "does not accept write concern"},
|
|
rolesInfo: {skip: "does not accept write concern"},
|
|
rotateCertificates: {skip: "does not accept write concern"},
|
|
rotateFTDC: {skip: "does not accept write concern"},
|
|
saslContinue: {skip: "does not accept write concern"},
|
|
saslStart: {skip: "does not accept write concern"},
|
|
sbe: {skip: "internal command"},
|
|
serverStatus: {skip: "does not accept write concern"},
|
|
setAllowMigrations: wcCommandsTests["setAllowMigrations"],
|
|
setAuditConfig: {skip: "does not accept write concern"},
|
|
setCommittedSnapshot: {skip: "internal command"},
|
|
setDefaultRWConcern: wcCommandsTests["setDefaultRWConcern"],
|
|
setFeatureCompatibilityVersion: wcCommandsTests["setFeatureCompatibilityVersion"],
|
|
setProfilingFilterGlobally: {skip: "does not accept write concern"},
|
|
setIndexCommitQuorum: {skip: "not supported on timeseries views"},
|
|
setParameter: {skip: "does not accept write concern"},
|
|
setShardVersion: {skip: "internal command"},
|
|
setChangeStreamState: {skip: "removed in v8.3"},
|
|
setClusterParameter: {skip: "does not accept write concern"},
|
|
setQuerySettings: {skip: "does not accept write concern"},
|
|
removeQuerySettings: {skip: "does not accept write concern"},
|
|
setUserWriteBlockMode: {skip: "does not accept write concern"},
|
|
shardCollection: wcCommandsTests["shardCollection"],
|
|
shardDrainingStatus: {skip: "unrelated"},
|
|
shardingState: {skip: "does not accept write concern"},
|
|
shutdown: {skip: "does not accept write concern"},
|
|
sleep: {skip: "does not accept write concern"},
|
|
split: {skip: "does not accept write concern"},
|
|
splitChunk: {skip: "does not accept write concern"},
|
|
splitVector: {skip: "internal command"},
|
|
stageDebug: {skip: "does not accept write concern"},
|
|
startSession: {skip: "does not accept write concern"},
|
|
startShardDraining: {skip: "unrelated"},
|
|
stopShardDraining: {skip: "unrelated"},
|
|
startTrafficRecording: {skip: "does not accept write concern"},
|
|
startTransitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
stopTrafficRecording: {skip: "does not accept write concern"},
|
|
stopTransitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
sysprofile: {skip: "internal command"},
|
|
testCommandFeatureFlaggedOnLatestFCV83: {skip: "internal command"},
|
|
testDeprecation: {skip: "test command"},
|
|
testDeprecationInVersion2: {skip: "test command"},
|
|
testInternalTransactions: {skip: "internal command"},
|
|
testRemoval: {skip: "test command"},
|
|
testReshardCloneCollection: {skip: "internal command"},
|
|
testVersions1And2: {skip: "test command"},
|
|
testVersion2: {skip: "test command"},
|
|
timeseriesCatalogBucketParamsChanged: {skip: "internal command"},
|
|
top: {skip: "does not accept write concern"},
|
|
transitionFromDedicatedConfigServer: {skip: "unrelated"},
|
|
transitionToDedicatedConfigServer: {skip: "unrelated"},
|
|
transitionToShardedCluster: {skip: "internal command"},
|
|
unshardCollection: {skip: "does not accept write concern"},
|
|
untrackUnshardedCollection: {skip: "does not accept write concern"},
|
|
update: {
|
|
noop: {
|
|
// The query will not match any doc
|
|
req: {
|
|
update: collName,
|
|
updates: [{q: {"meta.x": 0}, u: {$set: {"meta.y": 1}}, multi: true}],
|
|
},
|
|
setupFunc: (coll) => {},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
success: {
|
|
// Basic update
|
|
req: {
|
|
update: collName,
|
|
updates: [{q: {"meta.x": 1}, u: {$set: {"meta.y": 2}}, multi: true}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert([{meta: {x: 1, y: 1}, time: timeValue}]));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find({"meta.y": 2}).toArray().length, 1);
|
|
},
|
|
},
|
|
failure: {
|
|
req: {
|
|
update: collName,
|
|
updates: [{q: {"meta.x": 1}, u: {$set: {"meta.y": 2, time: "deadbeef"}}, multi: true}],
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.includes([ErrorCodes.InvalidOptions, ErrorCodes.BadValue], res.writeErrors[0]["code"]);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.count({"meta.y": 1}), 1);
|
|
},
|
|
},
|
|
},
|
|
updateRole: wcCommandsTests["updateRole"],
|
|
updateSearchIndex: {skip: "does not accept write concern"},
|
|
updateUser: wcCommandsTests["updateRole"],
|
|
updateZoneKeyRange: {skip: "does not accept write concern"},
|
|
usersInfo: {skip: "does not accept write concern"},
|
|
validate: {skip: "does not accept write concern"},
|
|
validateDBMetadata: {skip: "does not accept write concern"},
|
|
voteAbortIndexBuild: {skip: "internal command"},
|
|
voteCommitImportCollection: {skip: "internal command"},
|
|
voteCommitIndexBuild: {skip: "internal command"},
|
|
waitForFailPoint: {skip: "test command"},
|
|
getShardingReady: {skip: "internal command"},
|
|
whatsmysni: {skip: "does not accept write concern"},
|
|
whatsmyuri: {skip: "internal command"},
|
|
};
|
|
|
|
// A list of additional CRUD ops which exercise different write paths, and do error handling
|
|
// differently than the basic write path exercised in wcCommandsTestsT.
|
|
const additionalCRUDOpsTimeseriesViews = {
|
|
"deleteMany": {
|
|
noop: {
|
|
req: {delete: collName, deletes: [{q: {"meta.x": {$lt: 0}}, limit: 0}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
|
|
assert.commandWorked(coll.insert({meta: {x: 2}, time: timeValue}));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
},
|
|
success: {
|
|
req: {delete: collName, deletes: [{q: {"meta.x": {$gt: -5}}, limit: 0}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
|
|
assert.commandWorked(coll.insert({meta: {x: 2}, time: timeValue}));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 2);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
},
|
|
"insertMany": {
|
|
success: {
|
|
req: {
|
|
insert: collName,
|
|
documents: [
|
|
{meta: -2, time: timeValue},
|
|
{meta: 2, time: timeValue},
|
|
],
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 2);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
},
|
|
failure: {
|
|
req: {insert: collName, documents: [{meta: -2, time: timeValue}, {meta: 2}]},
|
|
setupFunc: (coll) => {},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.includes([5743702, ErrorCodes.BadValue], res.writeErrors[0].code);
|
|
assert.eq(res.n, 1);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
},
|
|
},
|
|
"updateMany": {
|
|
noop: {
|
|
req: {
|
|
update: collName,
|
|
updates: [{q: {"meta.x": {$gt: -21}}, u: {$set: {"meta.y": 1}}, multi: true}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.commandWorked(coll.remove({"meta.x": {$gt: -21}}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.find({"meta.y": 1}).toArray().length, 0);
|
|
},
|
|
},
|
|
success: {
|
|
req: {
|
|
update: collName,
|
|
updates: [{q: {"meta.x": {$gt: -21}}, u: {$set: {"meta.y": 1}}, multi: true}],
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(res.n, 3);
|
|
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
|
|
},
|
|
},
|
|
failure: {
|
|
req: {
|
|
update: collName,
|
|
updates: [{q: {"meta.x": {$gt: -5}}, u: {$set: {time: "deadbeef"}}, multi: true}],
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1}, time: ISODate()}));
|
|
assert.commandWorked(coll.insert({meta: {x: 2}, time: ISODate()}));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[0].code);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(res.nModified, 0);
|
|
},
|
|
},
|
|
},
|
|
"findOneAndRemove": {
|
|
noop: {
|
|
req: {findAndModify: collName, query: {"meta.x": 1}, remove: true},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
|
|
assert.commandWorked(coll.remove({meta: {x: 1}, time: timeValue}));
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
}
|
|
assert.eq(res.value, null);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
success: {
|
|
req: {findAndModify: collName, query: {"meta.x": 1}, remove: true},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.value.meta.x, 1);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"findOneAndUpdate": {
|
|
// Modifier updates
|
|
// No noop and success cases as we cannot perform a non-multi update on a
|
|
// time-series
|
|
// collection. findAndModify does not support multi updates.
|
|
failure: {
|
|
req: {
|
|
findAndModify: collName,
|
|
query: {"meta.x": 1},
|
|
update: {time: "deadbeef"},
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.code);
|
|
},
|
|
},
|
|
},
|
|
"unorderedBatch": {
|
|
noop: {
|
|
// The two writes would execute the same update, so one will be a no-op.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {"meta.x": 21}, u: {$set: {"meta.y": 21}}, multi: true},
|
|
{q: {"meta.x": {$gte: 20}}, u: {$set: {"meta.y": 21}}, multi: true},
|
|
],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22, y: 21}, time: ISODate()},
|
|
{meta: {x: -20, y: 21}, time: ISODate()},
|
|
{meta: {x: 20, y: 21}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find({"meta.y": 21}).toArray().length, 4);
|
|
},
|
|
},
|
|
success: {
|
|
// All updates should succeed, but all should return WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {"meta.x": -20}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
{q: {"meta.x": 20}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
{q: {"meta.x": 21}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22, y: 21}, time: ISODate()},
|
|
{meta: {x: -20, y: 21}, time: ISODate()},
|
|
{meta: {x: 20, y: 21}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 3);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
|
|
} else {
|
|
// The two phase write path returns WriteConcernTimeout in the write
|
|
// errors array.
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 3);
|
|
res.writeErrors.forEach((err) => {
|
|
assert.eq(err.code, ErrorCodes.WriteConcernTimeout);
|
|
});
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
|
|
}
|
|
},
|
|
},
|
|
failure: {
|
|
// The second update should fail. This is an unordered batch, so the
|
|
// other two updates should succeed, but still return WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {"meta.x": -20}, u: {$set: {"meta.y": 3}}, multi: true},
|
|
{q: {"meta.x": 20}, u: {$set: {time: "deadbeef"}}, multi: true},
|
|
{q: {"meta.x": 21}, u: {$set: {"meta.y": 3}}, multi: true},
|
|
],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[0].code);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
assert.eq(coll.find({"meta.y": {$exists: true}}).toArray().length, 2);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 3);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[1].code);
|
|
assert.eq(res.writeErrors[2].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.find({"meta.y": {$exists: true}}).toArray().length, 0);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"orderedBatch": {
|
|
noop: {
|
|
// The last update is a no-op.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {"meta.x": {$gte: -21}}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
{q: {"meta.x": 21}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(coll.find({"meta.y": 1}).itcount(), 3);
|
|
},
|
|
},
|
|
success: {
|
|
// All updates should succeed, but all should return WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {"meta.x": -20}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
{q: {"meta.x": 20}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
{q: {"meta.x": 21}, u: {$set: {"meta.y": 1}}, multi: true},
|
|
],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
|
|
} else {
|
|
// We stop execution after the first write because it fails with a WCE
|
|
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({"meta.y": 1}).toArray().length, 0);
|
|
}
|
|
},
|
|
},
|
|
failure: {
|
|
// The second update should fail. This is an ordered batch, so the
|
|
// last update should not be executed. The first and second should still return
|
|
// WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {"meta.x": -20}, u: {$set: {"meta.y": 3}}, multi: true},
|
|
{q: {"meta.x": 20}, u: {$set: {time: "deadbeef"}}, multi: true},
|
|
{q: {"meta.x": 21}, u: {$set: {"meta.y": 3}}, multi: true},
|
|
],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
{meta: {x: 21}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[0].code);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
assert.eq(coll.find({"meta.y": {$exists: true}}).toArray().length, 1);
|
|
} else {
|
|
// We stop execution after the first write because it fails with a WCE
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({"meta.y": 1}).toArray().length, 0);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"bulkWriteUnordered": {
|
|
// The two writes would execute the same delete, so one will be a no-op.
|
|
noop: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{delete: 0, filter: {"meta.x": {$gte: -20}}, multi: true},
|
|
{delete: 0, filter: {"meta.x": -20}, multi: true},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 1);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.nErrors, 0);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 1);
|
|
}
|
|
assert.eq(res.cursor.firstBatch[1].n, 0);
|
|
|
|
assert.eq(res.nDeleted, 1);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{insert: 0, document: {meta: 22, time: timeValue}},
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": -20},
|
|
updateMods: {$set: {"meta.y": 2}},
|
|
multi: true,
|
|
},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find({"meta.y": 2}).itcount(), 1);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({"meta.y": 2}).itcount(), 0);
|
|
}
|
|
assert.eq(res.nInserted, 1);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
admin: true,
|
|
},
|
|
failure: {
|
|
// The second update should fail, but this is an unordered batch so the other 2
|
|
// updates should succeed.
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": {$gte: -20}},
|
|
updateMods: {$set: {"meta.y": 5}},
|
|
multi: true,
|
|
},
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": -22},
|
|
updateMods: {$set: {time: "deadbeef"}},
|
|
multi: true,
|
|
},
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": -22},
|
|
updateMods: {$set: {"meta.y": 4}},
|
|
multi: true,
|
|
},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.cursor.firstBatch.length, 3);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.cursor.firstBatch[1].code);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[2].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[2].n, 1);
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(coll.find({"meta.y": 4}).itcount(), 1);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[2].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[2].n, 0);
|
|
assert.eq(res.cursor.firstBatch[2].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 2);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find({"meta.y": 4}).itcount(), 1);
|
|
}
|
|
|
|
assert.eq(coll.find().itcount(), 3);
|
|
assert.eq(coll.find({"meta.y": 5}).itcount(), 2);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
"bulkWriteOrdered": {
|
|
// The two deletes would remove the same doc, so one will be a no-op.
|
|
noop: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{delete: 0, filter: {"meta.x": {$gte: -20}}, multi: true},
|
|
{delete: 1, filter: {"meta.x": -20}},
|
|
{insert: 0, document: {meta: 25, time: timeValue}},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch.length, 3);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 1);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[1].n, 0);
|
|
|
|
assert.eq(res.cursor.firstBatch[2].ok, 1);
|
|
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
} else {
|
|
// The write without shard key will execute a transaction and fail the
|
|
// write with WriteConcernTimeout, so the insert will not execute.
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 1);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].n, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
}
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": {$gte: -10}},
|
|
updateMods: {$set: {"meta.y": 1}},
|
|
multi: true,
|
|
},
|
|
{
|
|
update: 1,
|
|
filter: {"meta.x": -20},
|
|
updateMods: {$set: {"meta.y": 2}},
|
|
multi: true,
|
|
},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find({"meta.y": 2}).itcount(), 1);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find({"meta.y": 2}).itcount(), 0);
|
|
}
|
|
|
|
assert.eq(coll.find().itcount(), 3);
|
|
assert.eq(coll.find({"meta.y": 1}).itcount(), 1);
|
|
},
|
|
admin: true,
|
|
},
|
|
// The second update will fail, and this is an ordered batch so the final update
|
|
// should
|
|
// not execute.
|
|
failure: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": {$gte: -20}},
|
|
updateMods: {$set: {"meta.y": 5}},
|
|
multi: true,
|
|
},
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": -22},
|
|
updateMods: {$set: {time: "deadbeef"}},
|
|
multi: true,
|
|
},
|
|
{
|
|
update: 0,
|
|
filter: {"meta.x": -22},
|
|
updateMods: {$set: {"meta.y": 4}},
|
|
multi: true,
|
|
},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{meta: {x: -22}, time: ISODate()},
|
|
{meta: {x: -20}, time: ISODate()},
|
|
{meta: {x: 20}, time: ISODate()},
|
|
]),
|
|
);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.cursor.firstBatch[1].code);
|
|
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
assert.eq(coll.find({"meta.y": 5}).itcount(), 2);
|
|
assert.eq(coll.find({"meta.y": 4}).itcount(), 0);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
};
|
|
|
|
// A list of additional CRUD ops which exercise different write paths, and do error handling
|
|
// differently than the basic write path exercised in the test cases above.
|
|
let additionalCRUDOps = {
|
|
"deleteMany": {
|
|
noop: {
|
|
req: {delete: collName, deletes: [{q: {a: {$lt: 0}}, limit: 0}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.insert({a: 2}));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
},
|
|
success: {
|
|
req: {delete: collName, deletes: [{q: {a: {$gt: -5}}, limit: 0}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.insert({a: -1}));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 2);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
},
|
|
"insertMany": {
|
|
success: {
|
|
req: {insert: collName, documents: [{a: -2}, {a: 2}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({_id: 1}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 2);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
},
|
|
failure: {
|
|
req: {insert: collName, documents: [{a: -2}, {a: 2}], ordered: false},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {x: {$exists: true}}}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 2);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(res.writeErrors[1].code, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
},
|
|
},
|
|
"updateMany": {
|
|
noop: {
|
|
req: {update: collName, updates: [{q: {a: {$gt: -21}}, u: {$set: {b: 1}}, multi: true}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
|
|
assert.commandWorked(coll.remove({a: {$gt: -21}}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 0);
|
|
},
|
|
},
|
|
success: {
|
|
req: {update: collName, updates: [{q: {a: {$gt: -21}}, u: {$set: {b: 1}}, multi: true}]},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(res.n, 3);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 3);
|
|
},
|
|
},
|
|
failure: {
|
|
req: {update: collName, updates: [{q: {a: {$gt: -5}}, u: {$set: {b: 1}}, multi: true}]},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.insert({a: 2}));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(res.n, 0);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.count({b: 1}), 0);
|
|
},
|
|
},
|
|
},
|
|
"findOneAndRemove": {
|
|
noop: {
|
|
req: {findAndModify: collName, query: {a: 1}, remove: true},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.remove({a: 1}));
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
}
|
|
assert.eq(res.value, null);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
},
|
|
success: {
|
|
req: {findAndModify: collName, query: {a: 1}, remove: true},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.value.a, 1);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"findOneAndUpdate": {
|
|
// Modifier updates
|
|
noop: {
|
|
req: {findAndModify: collName, query: {a: 1}, update: {$set: {c: 2}}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorkedIgnoringWriteConcernErrors(
|
|
coll.getDB().runCommand({findAndModify: collName, query: {a: 1}, update: {$set: {c: 2}}}),
|
|
);
|
|
assert.eq(coll.count({a: 1, c: 2}), 1);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.lastErrorObject.updatedExisting, true);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
}
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({a: 1, c: 2}), 1);
|
|
},
|
|
},
|
|
success: {
|
|
req: {findAndModify: collName, query: {a: 1}, update: {$set: {c: 2}}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.lastErrorObject.updatedExisting, true);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.eq(coll.count({a: 1, c: 2}), 1);
|
|
} else {
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.count({a: 1, c: 2}), 0);
|
|
}
|
|
},
|
|
},
|
|
failure: {
|
|
req: {findAndModify: collName, query: {a: 1}, update: {$set: {value: 3}}},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert({a: 1, value: 1}));
|
|
assert.eq(coll.find().itcount(), 1);
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {value: {$gt: 4}}}));
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandFailedWithCode(res, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(coll.find({a: 1, value: 1}).itcount(), 1);
|
|
},
|
|
},
|
|
},
|
|
"unorderedBatch": {
|
|
noop: {
|
|
// The two writes would execute the same update, so one will be a no-op.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {a: 21}, u: {$set: {b: 1}}},
|
|
{q: {a: {$gte: -20}}, u: {$set: {b: 1}}, multi: true},
|
|
],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22, b: 1}, {a: -20, b: 1}, {a: 20, b: 1}, {a: 21}]));
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 4);
|
|
},
|
|
},
|
|
success: {
|
|
// All updates should succeed, but all should return WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {a: -20}, u: {$set: {b: 1}}},
|
|
{q: {a: 20}, u: {$set: {b: 1}}},
|
|
{q: {a: 21}, u: {$set: {b: 1}}},
|
|
],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.n, 3);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 3);
|
|
} else {
|
|
// The two phase write path returns WriteConcernTimeout in the write
|
|
// errors array.
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 3);
|
|
res.writeErrors.forEach((err) => {
|
|
assert.eq(err.code, ErrorCodes.WriteConcernTimeout);
|
|
});
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 0);
|
|
}
|
|
},
|
|
},
|
|
failure: {
|
|
// The second update should fail the validator. This is an unordered batch, so the
|
|
// other two updates should succeed, but still return WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {a: -20}, u: {$set: {b: 3}}},
|
|
{q: {a: 20}, u: {$set: {b: 1}}},
|
|
{q: {a: 21}, u: {$set: {b: 3}}},
|
|
],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
assert.eq(coll.find({b: {$exists: true}}).toArray().length, 2);
|
|
} else {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 3);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.writeErrors[1].code, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(res.writeErrors[2].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(coll.find({b: {$exists: true}}).toArray().length, 0);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"orderedBatch": {
|
|
noop: {
|
|
// The last update is a no-op.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {a: {$gte: -21}}, u: {$set: {b: 1}}, multi: true},
|
|
{q: {a: 21}, u: {$set: {b: 1}}},
|
|
],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22, b: 1}, {a: -20, b: 1}, {a: 20, b: 1}, {a: 21}]));
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find({b: 1}).itcount(), 4);
|
|
},
|
|
},
|
|
success: {
|
|
// All updates should succeed, but all should return WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {a: -20}, u: {$set: {b: 1}}},
|
|
{q: {a: 20}, u: {$set: {b: 1}}},
|
|
{q: {a: 21}, u: {$set: {b: 1}}},
|
|
],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 3);
|
|
} else {
|
|
// We stop execution after the first write because it fails with a WCE
|
|
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 0);
|
|
}
|
|
},
|
|
},
|
|
failure: {
|
|
// The second update should fail the validator. This is an ordered batch, so the
|
|
// last update should not be executed. The first and second should still return
|
|
// WCEs.
|
|
req: {
|
|
update: collName,
|
|
updates: [
|
|
{q: {a: -20}, u: {$set: {b: 3}}},
|
|
{q: {a: 20}, u: {$set: {b: 1}}},
|
|
{q: {a: 21}, u: {$set: {b: 3}}},
|
|
],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
assert.eq(coll.find({b: {$exists: true}}).toArray().length, 1);
|
|
} else {
|
|
// We stop execution after the first write because it fails with a WCE
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert(res.writeErrors && res.writeErrors.length == 1);
|
|
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({b: 1}).toArray().length, 0);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"bulkWriteUnordered": {
|
|
// The two writes would execute the same delete, so one will be a no-op.
|
|
noop: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{delete: 0, filter: {a: {$gte: -20}}, multi: true},
|
|
{delete: 0, filter: {a: -20}},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}]));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 1);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.nErrors, 0);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 1);
|
|
}
|
|
assert.eq(res.cursor.firstBatch[1].n, 0);
|
|
|
|
assert.eq(res.nDeleted, 1);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{insert: 0, document: {a: 22}},
|
|
{update: 0, filter: {a: -20}, updateMods: {$set: {b: 2}}},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find({b: 2}).itcount(), 1);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 0);
|
|
assert.eq(coll.find({b: 2}).itcount(), 0);
|
|
}
|
|
assert.eq(res.nInserted, 1);
|
|
assert.eq(coll.find().itcount(), 4);
|
|
},
|
|
admin: true,
|
|
},
|
|
failure: {
|
|
// The second update should fail, but this is an unordered batch so the other 2
|
|
// updates should succeed.
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{update: 0, filter: {a: {$gte: -20}}, updateMods: {$set: {b: 5}}, multi: true},
|
|
{update: 0, filter: {a: -20}, updateMods: {$set: {b: 2}}},
|
|
{update: 0, filter: {a: -22}, updateMods: {$set: {b: 4}}},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
|
|
ordered: false,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.cursor.firstBatch.length, 3);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.DocumentValidationFailure);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[2].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[2].n, 1);
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 3);
|
|
assert.eq(coll.find({b: 4}).itcount(), 1);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[2].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[2].n, 0);
|
|
assert.eq(res.cursor.firstBatch[2].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 2);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find({b: 4}).itcount(), 0);
|
|
}
|
|
|
|
assert.eq(coll.find().itcount(), 3);
|
|
assert.eq(coll.find({b: 5}).itcount(), 2);
|
|
assert.eq(coll.find({b: 2}).itcount(), 0);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
"bulkWriteOrdered": {
|
|
// The two deletes would remove the same doc, so one will be a no-op.
|
|
noop: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{delete: 0, filter: {a: {$gte: -20}}, multi: true},
|
|
{delete: 1, filter: {a: -20}},
|
|
{insert: 0, document: {a: 25}},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}]));
|
|
assert.eq(coll.find().itcount(), 2);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch.length, 3);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 1);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[1].n, 0);
|
|
|
|
assert.eq(res.cursor.firstBatch[2].ok, 1);
|
|
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(coll.find().itcount(), 2);
|
|
} else {
|
|
// The write without shard key will execute a transaction and fail the
|
|
// write with WriteConcernTimeout, so the insert will not execute.
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 1);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].n, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(coll.find().itcount(), 1);
|
|
}
|
|
},
|
|
admin: true,
|
|
},
|
|
success: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{update: 0, filter: {a: {$gte: -10}}, updateMods: {$set: {b: 1}}, multi: true},
|
|
{update: 1, filter: {a: -20}, updateMods: {$set: {b: 2}}},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
|
|
let sk = getShardKey(coll, fullNs);
|
|
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
|
|
if (clusterType != "sharded" || !writeWithoutSkOrId) {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 1);
|
|
assert.eq(res.nErrors, 0);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find({b: 2}).itcount(), 1);
|
|
} else {
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 1);
|
|
assert.eq(coll.find({b: 2}).itcount(), 0);
|
|
}
|
|
|
|
assert.eq(coll.find().itcount(), 3);
|
|
assert.eq(coll.find({b: 1}).itcount(), 1);
|
|
},
|
|
admin: true,
|
|
},
|
|
// The second update will fail, and this is an ordered batch so the final update
|
|
// should
|
|
// not execute.
|
|
failure: {
|
|
req: {
|
|
bulkWrite: 1,
|
|
ops: [
|
|
{update: 0, filter: {a: {$gte: -20}}, updateMods: {$set: {b: 5}}, multi: true},
|
|
{update: 0, filter: {a: -20}, updateMods: {$set: {b: 2}}},
|
|
{update: 0, filter: {a: -22}, updateMods: {$set: {b: 4}}},
|
|
],
|
|
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
|
|
ordered: true,
|
|
},
|
|
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
|
|
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
|
|
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
|
|
assert.eq(coll.find().itcount(), 3);
|
|
},
|
|
confirmFunc: (res, coll, cluster, clusterType) => {
|
|
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
|
|
assert.eq(res.cursor.firstBatch.length, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[0].ok, 1);
|
|
assert.eq(res.cursor.firstBatch[0].n, 2);
|
|
|
|
assert.eq(res.cursor.firstBatch[1].ok, 0);
|
|
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.DocumentValidationFailure);
|
|
|
|
assert.eq(res.nErrors, 1);
|
|
assert.eq(res.nModified, 2);
|
|
assert.eq(coll.find().itcount(), 3);
|
|
assert.eq(coll.find({b: 5}).itcount(), 2);
|
|
assert.eq(coll.find({b: 4}).itcount(), 0);
|
|
},
|
|
admin: true,
|
|
},
|
|
},
|
|
};
|
|
|
|
export function stopSecondaries(cluster, clusterType) {
|
|
if (clusterType == "rs") {
|
|
assert.eq(cluster.nodeList().length, 3);
|
|
|
|
// Stop one of the secondaries so that w:3 will fail
|
|
const secondary = cluster.getSecondaries()[0];
|
|
cluster.stop(secondary);
|
|
return [secondary];
|
|
} else {
|
|
assert.eq(clusterType, "sharded");
|
|
|
|
let secondariesRunning = [];
|
|
const shards = cluster.getAllShards();
|
|
shards.forEach((rs) => {
|
|
assert.eq(rs.nodeList().length, 3);
|
|
|
|
// Stop one of the secondaries so that w:3 will fail. Some commands override user
|
|
// write concern to be w: majority. For those specific test cases, we'll need to
|
|
// shut down the other secondary to ensure waiting for write concern will still
|
|
// fail, so we track the other secondary.
|
|
const secondaries = rs.getSecondaries();
|
|
rs.stop(secondaries[0]);
|
|
secondariesRunning.push(secondaries[1]);
|
|
});
|
|
return secondariesRunning;
|
|
}
|
|
}
|
|
|
|
export function restartSecondaries(cluster, clusterType) {
|
|
if (clusterType == "rs") {
|
|
// Restart the secondary
|
|
cluster.restart(cluster.getSecondaries()[0]);
|
|
cluster.waitForPrimary();
|
|
cluster.awaitSecondaryNodes();
|
|
} else {
|
|
// Restart the secondaries
|
|
const shards = cluster.getAllShards();
|
|
shards.forEach((rs) => {
|
|
const secondaries = rs.getSecondaries();
|
|
rs.restart(secondaries[0]);
|
|
rs.waitForPrimary();
|
|
rs.awaitSecondaryNodes();
|
|
});
|
|
}
|
|
}
|
|
|
|
function awaitReplication(cluster, clusterType) {
|
|
if (clusterType == "rs") {
|
|
cluster.awaitReplication();
|
|
} else {
|
|
assert.eq(clusterType, "sharded");
|
|
const shards = cluster.getAllShards();
|
|
shards.forEach((rs) => {
|
|
rs.awaitReplication();
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Assert that the result contains a write concern error. Typically we expect WCEs to be
|
|
* reported in the WriteConcernErrors field, but some commands nest the WCE and other actually
|
|
* return the error as a top-level command error.
|
|
*/
|
|
export function assertHasWCE(res, cmd) {
|
|
try {
|
|
assertWriteConcernError(res);
|
|
} catch (e) {
|
|
try {
|
|
// Some commands return WCEs in a nested "raw" field
|
|
if (res.raw) {
|
|
let hasWCE = false;
|
|
Object.keys(res.raw).forEach((key) => {
|
|
if (res.raw[key].writeConcernError) {
|
|
assertWriteConcernError(res.raw[key]);
|
|
hasWCE = true;
|
|
}
|
|
});
|
|
assert(hasWCE);
|
|
} else {
|
|
// Some commands fail the command with WriteConcernTimeout as a top-level error,
|
|
// and do not include a separate WCE field.
|
|
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
|
|
}
|
|
} catch (err) {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
function runCommandTest(testCase, conn, coll, cluster, clusterType, preSetup, secondariesRunning, forceUseMajorityWC) {
|
|
const dbName = coll.getDB().getName();
|
|
|
|
// Drop collection.
|
|
coll.drop();
|
|
assert.eq(0, coll.find().itcount(), "test collection not empty");
|
|
|
|
jsTestLog("Testing command: " + tojson(testCase.req));
|
|
|
|
// Create environment for command to run in.
|
|
if (preSetup) {
|
|
preSetup(conn, cluster, dbName, collName);
|
|
}
|
|
|
|
let optionalArgs = {};
|
|
testCase.setupFunc(coll, cluster, clusterType, secondariesRunning, optionalArgs);
|
|
|
|
const request =
|
|
typeof testCase.req === "function" ? testCase.req(cluster, coll) : Object.assign({}, testCase.req, {});
|
|
|
|
// Provide a small wtimeout that we expect to time out.
|
|
if (forceUseMajorityWC) {
|
|
request.writeConcern = {w: "majority", wtimeout: 1000};
|
|
} else {
|
|
request.writeConcern = {w: 3, wtimeout: 1000};
|
|
}
|
|
|
|
// We run the command on a different connection. If the the command were run on the
|
|
// same connection, then the client last op for the noop write would be set by the setup
|
|
// operation. By using a fresh connection the client last op begins as null.
|
|
// This test explicitly tests that write concern for noop writes works when the
|
|
// client last op has not already been set by a duplicate operation.
|
|
const freshConn = new Mongo(conn.host);
|
|
|
|
// We check the error code of 'res' in the 'confirmFunc'.
|
|
const res = testCase.admin ? freshConn.adminCommand(request) : freshConn.getDB(dbName).runCommand(request);
|
|
|
|
try {
|
|
// Tests that the command receives a write concern error.
|
|
assertHasWCE(res, testCase.req);
|
|
|
|
// Validate post-conditions of the commands.
|
|
jsTestLog(tojson(getCommandName(request)) + " command returned " + tojson(res));
|
|
testCase.confirmFunc(res, coll, cluster, clusterType, secondariesRunning, optionalArgs);
|
|
} catch (e) {
|
|
// Make sure that we print out the response.
|
|
printjson(res);
|
|
throw e;
|
|
}
|
|
|
|
// Kill the implicit session to abort any idle transactions to "force" reap.
|
|
assert.commandWorkedOrFailedWithCode(
|
|
freshConn.adminCommand({killSessions: [freshConn.getDB(dbName).getSession().getSessionId()]}),
|
|
ErrorCodes.HostUnreachable,
|
|
);
|
|
}
|
|
|
|
// TODO SERVER-97736 Modify `shouldSkipTestCase` to ensure these commands are not skipped once
|
|
// they no longer hang until the majority of the shards involved in DDL are available and return
|
|
// WCE on timing out.
|
|
const shardedDDLCommandsRequiringMajorityCommit = [
|
|
"changePrimary",
|
|
"collMod",
|
|
"convertToCapped",
|
|
"drop",
|
|
"dropDatabase",
|
|
"dropIndexes",
|
|
"movePrimary",
|
|
"refineCollectionShardKey",
|
|
"renameCollection",
|
|
"setAllowMigrations",
|
|
"shardCollection",
|
|
];
|
|
|
|
function shouldSkipTestCase(clusterType, command, testCase, shardedCollection, writeWithoutSk, coll) {
|
|
if (
|
|
!shardedCollection &&
|
|
(command == "moveChunk" ||
|
|
command == "moveRange" ||
|
|
command == "refineCollectionShardKey" ||
|
|
command == "setAllowMigrations" ||
|
|
command == "updateDocSk")
|
|
) {
|
|
jsTestLog(
|
|
"Skipping " + command + " because requires sharded collection, and the current collection is not sharded.",
|
|
);
|
|
return true;
|
|
}
|
|
|
|
if (shardedCollection && command == "updateDocSk" && bsonWoCompare(getShardKey(coll, fullNs), {"_id": 1}) == 0) {
|
|
jsTestLog(
|
|
"Skipping updating a document's shard key because the shard key is {_id: 1}, and the _id field is immutable.",
|
|
);
|
|
return true;
|
|
}
|
|
|
|
if (shardedCollection && command == "shardCollection") {
|
|
jsTestLog(
|
|
"Skipping " + command + " because requires an unsharded collection, and the current collection is sharded.",
|
|
);
|
|
return true;
|
|
}
|
|
|
|
// TODO SERVER-112609 Re-enable create command for sharded collections once no-op operations
|
|
// honor a 'majority' write concern.
|
|
if (shardedCollection && command == "create") {
|
|
jsTestLog(
|
|
"Skipping " +
|
|
command +
|
|
" because the preSetup function creates a sharded collection and all test cases become no-op operations.",
|
|
);
|
|
return true;
|
|
}
|
|
|
|
if (testCase == "noop") {
|
|
// TODO SERVER-100309 adapt/enable setFeatureCompatibilityVersion no-op case once the
|
|
// upgrade procedure will not proactively shard the sessions collection.
|
|
if (
|
|
clusterType == "sharded" &&
|
|
(shardedDDLCommandsRequiringMajorityCommit.includes(command) ||
|
|
command == "setFeatureCompatibilityVersion" ||
|
|
// TODO SERVER-112609 Re-enable create command no-op test in sharded clusters once
|
|
// no-op operations honor a 'majority' write concern.
|
|
command == "create")
|
|
) {
|
|
jsTestLog("Skipping " + command + " test for no-op case.");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (testCase == "success") {
|
|
if (clusterType == "sharded" && shardedDDLCommandsRequiringMajorityCommit.includes(command)) {
|
|
jsTestLog("Skipping " + command + " test for success case.");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (testCase == "failure") {
|
|
if (clusterType == "sharded" && shardedDDLCommandsRequiringMajorityCommit.includes(command)) {
|
|
jsTestLog("Skipping " + command + " test for failure case.");
|
|
return true;
|
|
}
|
|
|
|
if (clusterType == "rs" && command == "setDefaultRWConcern") {
|
|
jsTestLog("Skipping " + command + " test for failure case.");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// These commands only accept w:1 or w:majority.
|
|
let umcRequireMajority = [
|
|
"createRole",
|
|
"createUser",
|
|
"dropAllRolesFromDatabase",
|
|
"dropAllUsersFromDatabase",
|
|
"dropRole",
|
|
"dropUser",
|
|
"grantPrivilegesToRole",
|
|
"grantRolesToRole",
|
|
"grantRolesToUser",
|
|
"revokePrivilegesFromRole",
|
|
"revokeRolesFromRole",
|
|
"revokeRolesFromUser",
|
|
"updateRole",
|
|
"updateUser",
|
|
];
|
|
|
|
function executeWriteConcernBehaviorTests(
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
commandsToRun,
|
|
masterCommandsList,
|
|
secondariesRunning,
|
|
shardedCollection,
|
|
writeWithoutSk,
|
|
) {
|
|
commandsToRun.forEach((command) => {
|
|
let cmd = masterCommandsList[command];
|
|
|
|
if (!cmd.skip && !cmd.noop && !cmd.success && !cmd.failure) {
|
|
throw "Must implement test case for command " + command + ", or explain why it should be skipped.";
|
|
}
|
|
|
|
// Some commands only allow w:1 or w:majority in a sharded cluster, so we must choose
|
|
// majority
|
|
let forceUseMajorityWC = clusterType == "sharded" && umcRequireMajority.includes(command);
|
|
|
|
if (cmd.noop) {
|
|
if (!shouldSkipTestCase(clusterType, command, "noop", shardedCollection, writeWithoutSk, coll))
|
|
runCommandTest(
|
|
cmd.noop,
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
secondariesRunning,
|
|
forceUseMajorityWC,
|
|
);
|
|
}
|
|
|
|
if (cmd.success) {
|
|
if (!shouldSkipTestCase(clusterType, command, "success", shardedCollection, writeWithoutSk, coll))
|
|
runCommandTest(
|
|
cmd.success,
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
secondariesRunning,
|
|
forceUseMajorityWC,
|
|
);
|
|
}
|
|
|
|
if (cmd.failure) {
|
|
if (!shouldSkipTestCase(clusterType, command, "failure", shardedCollection, writeWithoutSk, coll))
|
|
runCommandTest(
|
|
cmd.failure,
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
secondariesRunning,
|
|
forceUseMajorityWC,
|
|
writeWithoutSk,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
export function checkWriteConcernBehaviorForAllCommands(
|
|
conn,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
shardedCollection,
|
|
limitToTimeseriesViews = false,
|
|
) {
|
|
jsTestLog("Checking write concern behavior for all commands");
|
|
const commandsToTest = limitToTimeseriesViews ? wcTimeseriesViewsCommandsTests : wcCommandsTests;
|
|
const commandsList = AllCommandsTest.checkCommandCoverage(conn, commandsToTest);
|
|
|
|
let coll = conn.getDB(dbName).getCollection(collName);
|
|
|
|
if (clusterType == "rs") {
|
|
jsTestLog("Running tests against replica set");
|
|
|
|
stopSecondaries(cluster, clusterType);
|
|
|
|
executeWriteConcernBehaviorTests(conn, coll, cluster, clusterType, preSetup, commandsList, commandsToTest);
|
|
|
|
restartSecondaries(cluster, clusterType);
|
|
|
|
awaitReplication(cluster, clusterType);
|
|
return;
|
|
}
|
|
|
|
assert.eq(clusterType, "sharded");
|
|
|
|
// In a sharded cluster, some commands are coordinated through the configsvr, and others
|
|
// through the shard(s). For commands that target the configsvr, we'll prevent replication
|
|
// on the configsvr. Otherwise, we'll prevent replication on the shard(s). To speed up the
|
|
// execution of this test, we'll run all commands that target the configsvr first and then
|
|
// those that shards target shards so that we don't need to stop and restart nodes for each
|
|
// test case.
|
|
let cmdsTargetConfigServer = [];
|
|
let cmdsTargetShards = [];
|
|
commandsList.forEach((command) => {
|
|
let cmd = commandsToTest[command];
|
|
if (cmd.targetConfigServer) {
|
|
cmdsTargetConfigServer.push(command);
|
|
} else {
|
|
cmdsTargetShards.push(command);
|
|
}
|
|
});
|
|
|
|
if (FeatureFlagUtil.isPresentAndEnabled(cluster.configRS.getPrimary(), "CreateDatabaseDDLCoordinator")) {
|
|
shardedDDLCommandsRequiringMajorityCommit.push("enableSharding");
|
|
}
|
|
|
|
// Run test cases for commands that target the configsvr
|
|
(() => {
|
|
jsTestLog("Running commands that target the configsvr");
|
|
assert.eq(cluster.configRS.nodeList().length, 3);
|
|
|
|
// Stop one of the secondaries so that w:3 will fail. Some commands override user write
|
|
// concern to be w: majority. For those specific test cases, we'll need to shut down the
|
|
// other secondary to ensure wiating for write concern will still fail.
|
|
const csrsSecondaries = cluster.configRS.getSecondaries();
|
|
cluster.configRS.stop(csrsSecondaries[0]);
|
|
|
|
executeWriteConcernBehaviorTests(
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
cmdsTargetConfigServer,
|
|
commandsToTest,
|
|
[csrsSecondaries[1]],
|
|
shardedCollection,
|
|
);
|
|
|
|
cluster.configRS.restart(csrsSecondaries[0]);
|
|
})();
|
|
|
|
// Run test cases for commands the target shard(s)
|
|
(() => {
|
|
jsTestLog("Running commands that target shards");
|
|
|
|
let secondariesRunning = stopSecondaries(cluster, clusterType);
|
|
|
|
executeWriteConcernBehaviorTests(
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
cmdsTargetShards,
|
|
commandsToTest,
|
|
secondariesRunning,
|
|
shardedCollection,
|
|
);
|
|
|
|
restartSecondaries(cluster, clusterType);
|
|
})();
|
|
|
|
awaitReplication(cluster, clusterType);
|
|
}
|
|
|
|
export function checkWriteConcernBehaviorAdditionalCRUDOps(
|
|
conn,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
shardedCollection,
|
|
writeWithoutSk,
|
|
limitToTimeseriesViews = false,
|
|
) {
|
|
jsTestLog("Checking write concern behavior for additional CRUD commands");
|
|
|
|
let coll = conn.getDB(dbName).getCollection(collName);
|
|
|
|
const commandsToTest = limitToTimeseriesViews ? additionalCRUDOpsTimeseriesViews : additionalCRUDOps;
|
|
|
|
stopSecondaries(cluster, clusterType);
|
|
|
|
executeWriteConcernBehaviorTests(
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
Object.keys(commandsToTest),
|
|
commandsToTest,
|
|
[] /* secondariesRunning */,
|
|
shardedCollection,
|
|
);
|
|
|
|
restartSecondaries(cluster, clusterType);
|
|
|
|
awaitReplication(cluster, clusterType);
|
|
}
|
|
|
|
export function checkWriteConcernBehaviorUpdatingDocShardKey(
|
|
conn,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
shardedCollection,
|
|
writeWithoutSk,
|
|
limitToTimeseriesViews = false,
|
|
) {
|
|
jsTestLog("Checking write concern behavior for updating a document's shard key");
|
|
|
|
let coll = conn.getDB(dbName).getCollection(collName);
|
|
|
|
stopSecondaries(cluster, clusterType);
|
|
|
|
// Note we don't need to check the transaction cases, because we will not wait for write concern
|
|
// on the individual transaction statements, but rather only on the commit. We test commit
|
|
// already separately above. We test the retryable write case because the server internally
|
|
// executes commit in this case, and builds a response object from the commit response.
|
|
let testCases = {
|
|
"updateDocSk": {
|
|
noop: {
|
|
req: () => ({
|
|
update: collName,
|
|
updates: [{q: {a: 1}, u: {$set: {[Object.keys(getShardKey(coll, fullNs))[0]]: -1}}}],
|
|
lsid: getLSID(),
|
|
txnNumber: getTxnNumber(),
|
|
}),
|
|
setupFunc: (coll) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
if (bsonWoCompare(sk, {a: 1}) == 0) {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
assert.commandWorked(coll.remove({a: 1}));
|
|
} else {
|
|
// Make sure doc has "a" to query on and the shard key
|
|
assert.commandWorked(coll.insert({a: 1, [Object.keys(sk)[0]]: 2}));
|
|
assert.commandWorked(coll.remove({[Object.keys(sk)[0]]: 2}));
|
|
}
|
|
|
|
assert.eq(coll.find().itcount(), 0);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find().itcount(), 0);
|
|
genNextTxnNumber();
|
|
},
|
|
},
|
|
success: {
|
|
req: () => ({
|
|
update: collName,
|
|
updates: [{q: {a: 1}, u: {$set: {[Object.keys(getShardKey(coll, fullNs))[0]]: -1}}}],
|
|
lsid: getLSID(),
|
|
txnNumber: getTxnNumber(),
|
|
}),
|
|
setupFunc: (coll) => {
|
|
let sk = getShardKey(coll, fullNs);
|
|
if (bsonWoCompare(sk, {a: 1}) == 0) {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
} else {
|
|
// Make sure doc has "a" to query on and the shard key
|
|
assert.commandWorked(coll.insert({a: 1, [Object.keys(sk)[0]]: 2}));
|
|
}
|
|
|
|
assert.eq(coll.find().itcount(), 1);
|
|
},
|
|
confirmFunc: (res, coll) => {
|
|
assert.commandWorkedIgnoringWriteConcernErrors(res);
|
|
assert.eq(coll.find({[Object.keys(getShardKey(coll, fullNs))[0]]: -1}).itcount(), 1);
|
|
genNextTxnNumber();
|
|
},
|
|
},
|
|
// We don't test a failure case, because if the insert or delete (executed internally by
|
|
// the server) fails for any reason, mongod will not wait for write concern at all
|
|
// (again, because it only waits on commit or abort, and in this case we won't try to
|
|
// commit). So, this becomes a similar case as the commit case in the tests above -
|
|
// there isn't a sequence of events that would mutate the data that would cause the
|
|
// commit to fail in such a way that the WCE is important.
|
|
},
|
|
};
|
|
|
|
executeWriteConcernBehaviorTests(
|
|
conn,
|
|
coll,
|
|
cluster,
|
|
clusterType,
|
|
preSetup,
|
|
Object.keys(testCases),
|
|
testCases,
|
|
[] /* secondariesRunning */,
|
|
shardedCollection,
|
|
writeWithoutSk,
|
|
limitToTimeseriesViews,
|
|
);
|
|
|
|
restartSecondaries(cluster, clusterType);
|
|
|
|
awaitReplication(cluster, clusterType);
|
|
}
|
|
|
|
export function precmdShardKey(shardKey, conn, cluster, dbName, collName) {
|
|
let db = conn.getDB(dbName);
|
|
let nss = dbName + "." + collName;
|
|
|
|
assert.commandWorked(db.adminCommand({enableSharding: dbName, primary: cluster.shard0.shardName}));
|
|
assert.commandWorked(db.adminCommand({shardCollection: nss, key: {[shardKey]: 1}}));
|
|
assert.commandWorked(db.adminCommand({split: nss, middle: {[shardKey]: 0}}));
|
|
assert.commandWorked(
|
|
db.adminCommand({
|
|
moveChunk: nss,
|
|
find: {[shardKey]: -1},
|
|
to: cluster.shard0.shardName,
|
|
_waitForDelete: true,
|
|
}),
|
|
);
|
|
assert.commandWorked(
|
|
db.adminCommand({
|
|
moveChunk: nss,
|
|
find: {[shardKey]: 1},
|
|
to: cluster.shard1.shardName,
|
|
_waitForDelete: true,
|
|
}),
|
|
);
|
|
}
|