summaryrefslogtreecommitdiff
path: root/jstests
diff options
context:
space:
mode:
Diffstat (limited to 'jstests')
-rw-r--r--jstests/concurrency/fsm_workloads/write_without_shard_key_with_resharding.js138
1 files changed, 138 insertions, 0 deletions
diff --git a/jstests/concurrency/fsm_workloads/write_without_shard_key_with_resharding.js b/jstests/concurrency/fsm_workloads/write_without_shard_key_with_resharding.js
new file mode 100644
index 00000000000..4b684f837cf
--- /dev/null
+++ b/jstests/concurrency/fsm_workloads/write_without_shard_key_with_resharding.js
@@ -0,0 +1,138 @@
+'use strict';
+
+/**
+ * Runs updateOne, deleteOne, and findAndModify without shard key against a sharded cluster while
+ * the collection reshards concurrently.
+ *
+ * @tags: [
+ * featureFlagUpdateOneWithoutShardKey,
+ * requires_fcv_70,
+ * requires_sharding,
+ * uses_transactions,
+ * ]
+ */
+
+load('jstests/concurrency/fsm_libs/extend_workload.js');
+load('jstests/concurrency/fsm_workloads/write_without_shard_key_base.js');
+load("jstests/libs/feature_flag_util.js");
+
+var $config = extendWorkload($config, function($config, $super) {
+ $config.startState = "init";
+
+ // reshardingMinimumOperationDurationMillis is set to 30 seconds when there are stepdowns.
+ // So in order to limit the overall time for the test, we limit the number of resharding
+ // operations to maxReshardingExecutions.
+ const maxReshardingExecutions = TestData.runningWithShardStepdowns ? 4 : $config.iterations;
+ const customShardKeyFieldName = "customShardKey";
+
+ $config.data.shardKeys = [];
+ $config.data.currentShardKeyIndex = -1;
+ $config.data.reshardingCount = 0;
+
+ $config.states.init = function init(db, collName, connCache) {
+ $super.states.init.apply(this, arguments);
+ this.shardKeys.push({[this.defaultShardKeyField]: 1});
+ this.shardKeys.push({[customShardKeyFieldName]: 1});
+ this.currentShardKeyIndex = 0;
+ };
+
+ $config.data.generateRandomDocument = function generateRandomDocument(tid, partition) {
+ const doc = $super.data.generateRandomDocument.apply(this, arguments);
+ assert.neq(partition, null);
+ doc[customShardKeyFieldName] = this.generateRandomInt(partition.lower, partition.upper - 1);
+ return doc;
+ };
+
+ /**
+ * Returns a random boolean.
+ */
+ $config.data.generateRandomBool = function generateRandomBool() {
+ return Math.random() > 0.5;
+ };
+
+ $config.data.shouldSkipWriteResponseValidation = function shouldSkipWriteResponseValidation(
+ res) {
+ let shouldSkip = $super.data.shouldSkipWriteResponseValidation.apply(this, arguments);
+
+ // This workload does in-place resharding so a retry that is sent
+ // reshardingMinimumOperationDurationMillis after resharding completes is expected to fail
+ // with IncompleteTransactionHistory.
+ if (!shouldSkip && (res.code == ErrorCodes.IncompleteTransactionHistory)) {
+ return res.errmsg.includes("Incomplete history detected for transaction");
+ }
+
+ return shouldSkip;
+ };
+
+ $config.states.reshardCollection = function reshardCollection(db, collName, connCache) {
+ const collection = db.getCollection(collName);
+ const ns = collection.getFullName();
+ jsTestLog("Running reshardCollection state on: " + tojson(ns));
+
+ if (this.tid === 0 && (this.reshardingCount <= maxReshardingExecutions)) {
+ const newShardKeyIndex = (this.currentShardKeyIndex + 1) % this.shardKeys.length;
+ const newShardKey = this.shardKeys[newShardKeyIndex];
+ const reshardCollectionCmdObj = {
+ reshardCollection: ns,
+ key: newShardKey,
+ };
+
+ print(`Started resharding collection ${ns}: ${tojson({newShardKey})}`);
+ if (TestData.runningWithShardStepdowns) {
+ assert.soon(function() {
+ var res = db.adminCommand(reshardCollectionCmdObj);
+ if (res.ok) {
+ return true;
+ }
+ assert(res.hasOwnProperty("code"));
+
+ if (!FeatureFlagUtil.isEnabled(db, "PointInTimeCatalogLookups")) {
+ // Expected error prior to the PointInTimeCatalogLookups project.
+ if (res.code === ErrorCodes.SnapshotUnavailable) {
+ return true;
+ }
+ }
+
+ // Race to retry.
+ if (res.code === ErrorCodes.ReshardCollectionInProgress) {
+ return false;
+ }
+ // Unexpected error.
+ doassert(`Failed with unexpected ${tojson(res)}`);
+ }, "Reshard command failed", 10 * 1000);
+ } else {
+ assert.commandWorked(db.adminCommand(reshardCollectionCmdObj));
+ }
+ print(`Finished resharding collection ${ns}: ${tojson({newShardKey})}`);
+
+ // If resharding fails with SnapshotUnavailable, then this will be incorrect. But
+ // its fine since reshardCollection will succeed if the new shard key matches the
+ // existing one.
+ this.currentShardKeyIndex = newShardKeyIndex;
+ this.reshardingCount += 1;
+
+ db.printShardingStatus();
+
+ connCache.mongos.forEach(mongos => {
+ if (this.generateRandomBool()) {
+ // Without explicitly refreshing mongoses, retries of retryable write statements
+ // would always be routed to the donor shards. Non-deterministically refreshing
+ // enables us to have test coverage for retrying against both the donor and
+ // recipient shards.
+ assert.commandWorked(mongos.adminCommand({flushRouterConfig: 1}));
+ }
+ });
+ }
+ };
+
+ $config.transitions = {
+ init: {reshardCollection: 0.3, updateOne: 0.3, deleteOne: 0.2, findAndModify: 0.2},
+ updateOne: {reshardCollection: 0.3, updateOne: 0.3, deleteOne: 0.2, findAndModify: 0.2},
+ deleteOne: {reshardCollection: 0.3, updateOne: 0.3, deleteOne: 0.2, findAndModify: 0.2},
+ findAndModify: {reshardCollection: 0.3, updateOne: 0.3, deleteOne: 0.2, findAndModify: 0.2},
+ reshardCollection:
+ {reshardCollection: 0.3, updateOne: 0.3, deleteOne: 0.2, findAndModify: 0.2}
+ };
+
+ return $config;
+});