diff options
Diffstat (limited to 'jstests/sharding/multi_writes_with_shard_version_ignored_dont_bubble_up_critical_section.js')
-rw-r--r-- | jstests/sharding/multi_writes_with_shard_version_ignored_dont_bubble_up_critical_section.js | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/jstests/sharding/multi_writes_with_shard_version_ignored_dont_bubble_up_critical_section.js b/jstests/sharding/multi_writes_with_shard_version_ignored_dont_bubble_up_critical_section.js new file mode 100644 index 00000000000..2f055ec08a0 --- /dev/null +++ b/jstests/sharding/multi_writes_with_shard_version_ignored_dont_bubble_up_critical_section.js @@ -0,0 +1,154 @@ +/* + * Tests that multi-writes where the router attaches 'shardVersion: IGNORED' (i.e. if they need to + * target several shards AND are not part of a txn) do not bubble up StaleConfig errors due to + * ongoing critical sections. Instead, the shard yields and waits for the critical section to finish + * and then continues the write plan. + * + * @tags: [ + * requires_fcv_61, + * ] + */ + +(function() { +"use strict"; + +load('jstests/libs/parallel_shell_helpers.js'); +load("jstests/libs/fail_point_util.js"); + +// Configure 'internalQueryExecYieldIterations' on both shards such that operations will yield on +// each 10th PlanExecuter iteration. +var st = new ShardingTest({ + shards: 2, + rs: {setParameter: {internalQueryExecYieldIterations: 10}}, + other: {enableBalancer: false} +}); + +const dbName = "test"; +const collName = "foo"; +const ns = dbName + "." + collName; +const numDocs = 100; +let coll = st.s.getCollection(ns); + +assert.commandWorked( + st.s.adminCommand({enableSharding: dbName, primaryShard: st.shard0.shardName})); + +function setupTest() { + coll.drop(); + assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {x: 1}})); + + // Create three chunks: + // - [MinKey, 0) initially on shard0 and has no documents. This chunk will be migrated during + // the test execution. + // - [0, numDocs) on shard 0. Contains 'numDocs' documents. + // - [numDocs, MaxKey) shard 1. Contains no documents. + assert.commandWorked(st.s.adminCommand({split: ns, middle: {x: 0}})); + assert.commandWorked(st.s.adminCommand({split: ns, middle: {x: numDocs}})); + assert.commandWorked(st.s.adminCommand( + {moveChunk: ns, find: {x: numDocs}, to: st.shard1.shardName, waitForDelete: true})); + + jsTest.log("Inserting initial data."); + const bulkOp = coll.initializeOrderedBulkOp(); + for (let i = 0; i < numDocs; ++i) { + bulkOp.insert({x: i, c: 0}); + } + assert.commandWorked(bulkOp.execute()); + jsTest.log("Inserted initial data."); +} + +function runMigration() { + const awaitResult = startParallelShell( + funWithArgs(function(ns, toShard) { + jsTest.log("Starting migration."); + assert.commandWorked(db.adminCommand({moveChunk: ns, find: {x: -1}, to: toShard})); + jsTest.log("Completed migration."); + }, ns, st.shard1.shardName), st.s.port); + + return awaitResult; +} + +function updateOperationFn(shardColl, numInitialDocsOnShard0) { + load('jstests/sharding/libs/shard_versioning_util.js'); // For kIgnoredShardVersion + + jsTest.log("Begin multi-update."); + + // Send a multi-update with 'shardVersion: IGNORED' directly to the shard, as if we were a + // router. + const result = assert.commandWorked(shardColl.runCommand({ + update: shardColl.getName(), + updates: [{q: {}, u: {$inc: {c: 1}}, multi: true}], + shardVersion: ShardVersioningUtil.kIgnoredShardVersion + })); + + jsTest.log("End multi-update. Result: " + tojson(result)); + + // Check that all documents got updates. Despite the weak guarantees of {multi: true} writes + // concurrent with migrations, this has to be the case in this test because the migrated chunk + // does not contain any document. + assert.eq(numInitialDocsOnShard0, shardColl.find({c: 1}).itcount()); +} + +function deleteOperationFn(shardColl, numInitialDocsOnShard0) { + load('jstests/sharding/libs/shard_versioning_util.js'); // For kIgnoredShardVersion + + jsTest.log("Begin multi-delete"); + + // Send a multi-delete with 'shardVersion: IGNORED' directly to the shard, as if we were a + // router. + const result = assert.commandWorked(shardColl.runCommand({ + delete: shardColl.getName(), + deletes: [{q: {}, limit: 0}], + shardVersion: ShardVersioningUtil.kIgnoredShardVersion + })); + + jsTest.log("End multi-delete. Result: " + tojson(result)); + + // Check that all documents got deleted. Despite the weak guarantees of {multi: true} writes + // concurrent with migrations, this has to be the case in this test because the migrated chunk + // does not contain any document. + assert.eq(0, shardColl.find().itcount()); +} + +function runTest(writeOpFn) { + setupTest(); + + let fp1 = configureFailPoint( + st.rs0.getPrimary(), 'setYieldAllLocksHang', {namespace: coll.getFullName()}); + + const awaitWriteResult = startParallelShell( + funWithArgs(function(writeOpFn, dbName, collName, numDocs) { + const shardColl = db.getSiblingDB(dbName)[collName]; + writeOpFn(shardColl, numDocs); + }, writeOpFn, coll.getDB().getName(), coll.getName(), numDocs), st.rs0.getPrimary().port); + + // Wait for the write op to yield. + fp1.wait(); + jsTest.log("Multi-write yielded"); + + // Start chunk migration and wait for it to enter the critical section. + let failpointHangMigrationWhileInCriticalSection = + configureFailPoint(st.rs0.getPrimary(), 'moveChunkHangAtStep5'); + const awaitMigration = runMigration(); + failpointHangMigrationWhileInCriticalSection.wait(); + + // Let the multi-write resume from the yield. + jsTest.log("Resuming yielded multi-write"); + fp1.off(); + + // Let the multi-write run for a bit after the resuming from yield. It will encounter the + // critical section. + sleep(1000); + + // Let the migration continue and release the critical section. + jsTest.log("Letting migration exit its critical section and complete"); + failpointHangMigrationWhileInCriticalSection.off(); + awaitMigration(); + + // Wait for the write op to finish. It should succeed. + awaitWriteResult(); +} + +runTest(updateOperationFn); +runTest(deleteOperationFn); + +st.stop(); +})(); |