summaryrefslogtreecommitdiff
path: root/jstests/sharding/merge_requires_unique_index.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/sharding/merge_requires_unique_index.js')
-rw-r--r--jstests/sharding/merge_requires_unique_index.js493
1 files changed, 246 insertions, 247 deletions
diff --git a/jstests/sharding/merge_requires_unique_index.js b/jstests/sharding/merge_requires_unique_index.js
index 868b40c0e7e..cf052888a2d 100644
--- a/jstests/sharding/merge_requires_unique_index.js
+++ b/jstests/sharding/merge_requires_unique_index.js
@@ -3,204 +3,235 @@
// collator-compatible index in the index catalog. This is meant to test sharding-related
// configurations that are not covered by the aggregation passthrough suites.
(function() {
- "use strict";
+"use strict";
- load("jstests/aggregation/extras/utils.js"); // For assertErrorCode.
- load("jstests/aggregation/extras/merge_helpers.js"); // For withEachMergeMode,
- // assertMergeFailsWithoutUniqueIndex,
- // assertMergeSucceedsWithExpectedUniqueIndex.
+load("jstests/aggregation/extras/utils.js"); // For assertErrorCode.
+load("jstests/aggregation/extras/merge_helpers.js"); // For withEachMergeMode,
+ // assertMergeFailsWithoutUniqueIndex,
+// assertMergeSucceedsWithExpectedUniqueIndex.
- const st = new ShardingTest({shards: 2, rs: {nodes: 1}, config: 1});
+const st = new ShardingTest({shards: 2, rs: {nodes: 1}, config: 1});
- const mongosDB = st.s0.getDB("merge_requires_unique_index");
- const foreignDB = st.s0.getDB("merge_requires_unique_index_foreign");
- const sourceColl = mongosDB.source;
- let targetColl = mongosDB.target;
- sourceColl.drop();
+const mongosDB = st.s0.getDB("merge_requires_unique_index");
+const foreignDB = st.s0.getDB("merge_requires_unique_index_foreign");
+const sourceColl = mongosDB.source;
+let targetColl = mongosDB.target;
+sourceColl.drop();
- // Enable sharding on the test DB and ensure that shard0 is the primary.
- assert.commandWorked(mongosDB.adminCommand({enableSharding: mongosDB.getName()}));
- st.ensurePrimaryShard(mongosDB.getName(), st.rs0.getURL());
+// Enable sharding on the test DB and ensure that shard0 is the primary.
+assert.commandWorked(mongosDB.adminCommand({enableSharding: mongosDB.getName()}));
+st.ensurePrimaryShard(mongosDB.getName(), st.rs0.getURL());
- // Enable sharding on the foreign DB, except ensure that shard1 is the primary shard.
- assert.commandWorked(foreignDB.adminCommand({enableSharding: foreignDB.getName()}));
- st.ensurePrimaryShard(foreignDB.getName(), st.rs1.getURL());
+// Enable sharding on the foreign DB, except ensure that shard1 is the primary shard.
+assert.commandWorked(foreignDB.adminCommand({enableSharding: foreignDB.getName()}));
+st.ensurePrimaryShard(foreignDB.getName(), st.rs1.getURL());
- // Increase the log verbosity for sharding, in the hope of getting a clearer picture of the
- // cluster writer as part of BF-11106. This should be removed once BF-11106 is fixed.
- st.shard0.getDB("admin").setLogLevel(4, 'sharding');
- st.shard1.getDB("admin").setLogLevel(4, 'sharding');
+// Increase the log verbosity for sharding, in the hope of getting a clearer picture of the
+// cluster writer as part of BF-11106. This should be removed once BF-11106 is fixed.
+st.shard0.getDB("admin").setLogLevel(4, 'sharding');
+st.shard1.getDB("admin").setLogLevel(4, 'sharding');
- function resetTargetColl(shardKey, split) {
- targetColl.drop();
- // Shard the target collection, and set the unique flag to ensure that there's a unique
- // index on the shard key.
- assert.commandWorked(mongosDB.adminCommand(
- {shardCollection: targetColl.getFullName(), key: shardKey, unique: true}));
- assert.commandWorked(
- mongosDB.adminCommand({split: targetColl.getFullName(), middle: split}));
- assert.commandWorked(mongosDB.adminCommand(
- {moveChunk: targetColl.getFullName(), find: split, to: st.rs1.getURL()}));
- }
+function resetTargetColl(shardKey, split) {
+ targetColl.drop();
+ // Shard the target collection, and set the unique flag to ensure that there's a unique
+ // index on the shard key.
+ assert.commandWorked(mongosDB.adminCommand(
+ {shardCollection: targetColl.getFullName(), key: shardKey, unique: true}));
+ assert.commandWorked(mongosDB.adminCommand({split: targetColl.getFullName(), middle: split}));
+ assert.commandWorked(mongosDB.adminCommand(
+ {moveChunk: targetColl.getFullName(), find: split, to: st.rs1.getURL()}));
+}
- function runOnFieldsTests(targetShardKey, targetSplit) {
- jsTestLog("Running unique key tests for target shard key " + tojson(targetShardKey));
- resetTargetColl(targetShardKey, targetSplit);
+function runOnFieldsTests(targetShardKey, targetSplit) {
+ jsTestLog("Running unique key tests for target shard key " + tojson(targetShardKey));
+ resetTargetColl(targetShardKey, targetSplit);
- // Not specifying "on" fields should always pass.
- assertMergeSucceedsWithExpectedUniqueIndex({source: sourceColl, target: targetColl});
+ // Not specifying "on" fields should always pass.
+ assertMergeSucceedsWithExpectedUniqueIndex({source: sourceColl, target: targetColl});
- // Since the target collection is sharded with a unique shard key, specifying "on" fields
- // that is equal to the shard key should be valid.
- assertMergeSucceedsWithExpectedUniqueIndex(
- {source: sourceColl, target: targetColl, onFields: Object.keys(targetShardKey)});
+ // Since the target collection is sharded with a unique shard key, specifying "on" fields
+ // that is equal to the shard key should be valid.
+ assertMergeSucceedsWithExpectedUniqueIndex(
+ {source: sourceColl, target: targetColl, onFields: Object.keys(targetShardKey)});
- // Create a compound "on" fields consisting of the shard key and one additional field.
- let prefixPipeline = [{$addFields: {newField: 1}}];
- const indexSpec = Object.merge(targetShardKey, {newField: 1});
+ // Create a compound "on" fields consisting of the shard key and one additional field.
+ let prefixPipeline = [{$addFields: {newField: 1}}];
+ const indexSpec = Object.merge(targetShardKey, {newField: 1});
- // Expect the $merge to fail since we haven't created a unique index on the compound
- // "on" fields.
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- prevStages: prefixPipeline
- });
+ // Expect the $merge to fail since we haven't created a unique index on the compound
+ // "on" fields.
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ prevStages: prefixPipeline
+ });
- // Create the unique index and verify that the "on" fields is now valid.
- assert.commandWorked(targetColl.createIndex(indexSpec, {unique: true}));
- assertMergeSucceedsWithExpectedUniqueIndex({
- source: sourceColl,
- target: targetColl,
- onFields: Object.keys(indexSpec),
- prevStages: prefixPipeline
- });
+ // Create the unique index and verify that the "on" fields is now valid.
+ assert.commandWorked(targetColl.createIndex(indexSpec, {unique: true}));
+ assertMergeSucceedsWithExpectedUniqueIndex({
+ source: sourceColl,
+ target: targetColl,
+ onFields: Object.keys(indexSpec),
+ prevStages: prefixPipeline
+ });
- // Create a non-unique index and make sure that doesn't work.
- assert.commandWorked(targetColl.dropIndex(indexSpec));
- assert.commandWorked(targetColl.createIndex(indexSpec));
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- prevStages: prefixPipeline
- });
+ // Create a non-unique index and make sure that doesn't work.
+ assert.commandWorked(targetColl.dropIndex(indexSpec));
+ assert.commandWorked(targetColl.createIndex(indexSpec));
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ prevStages: prefixPipeline
+ });
- // Test that a unique, partial index on the "on" fields cannot be used to satisfy the
- // requirement.
- resetTargetColl(targetShardKey, targetSplit);
- assert.commandWorked(targetColl.createIndex(
- indexSpec, {unique: true, partialFilterExpression: {a: {$gte: 2}}}));
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- prevStages: prefixPipeline
- });
+ // Test that a unique, partial index on the "on" fields cannot be used to satisfy the
+ // requirement.
+ resetTargetColl(targetShardKey, targetSplit);
+ assert.commandWorked(
+ targetColl.createIndex(indexSpec, {unique: true, partialFilterExpression: {a: {$gte: 2}}}));
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ prevStages: prefixPipeline
+ });
- // Test that a unique index on the "on" fields cannot be used to satisfy the requirement if
- // it has a different collation.
- resetTargetColl(targetShardKey, targetSplit);
- assert.commandWorked(
- targetColl.createIndex(indexSpec, {unique: true, collation: {locale: "en_US"}}));
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- prevStages: prefixPipeline
- });
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- options: {collation: {locale: "en"}},
- prevStages: prefixPipeline
- });
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- options: {collation: {locale: "simple"}},
- prevStages: prefixPipeline
- });
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- options: {collation: {locale: "en_US", strength: 1}},
- prevStages: prefixPipeline
- });
- assertMergeSucceedsWithExpectedUniqueIndex({
- source: sourceColl,
- target: targetColl,
- onFields: Object.keys(indexSpec),
- options: {collation: {locale: "en_US"}},
- prevStages: prefixPipeline
- });
+ // Test that a unique index on the "on" fields cannot be used to satisfy the requirement if
+ // it has a different collation.
+ resetTargetColl(targetShardKey, targetSplit);
+ assert.commandWorked(
+ targetColl.createIndex(indexSpec, {unique: true, collation: {locale: "en_US"}}));
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ prevStages: prefixPipeline
+ });
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ options: {collation: {locale: "en"}},
+ prevStages: prefixPipeline
+ });
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ options: {collation: {locale: "simple"}},
+ prevStages: prefixPipeline
+ });
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ options: {collation: {locale: "en_US", strength: 1}},
+ prevStages: prefixPipeline
+ });
+ assertMergeSucceedsWithExpectedUniqueIndex({
+ source: sourceColl,
+ target: targetColl,
+ onFields: Object.keys(indexSpec),
+ options: {collation: {locale: "en_US"}},
+ prevStages: prefixPipeline
+ });
- // Test that a unique index with dotted field names can be used.
- resetTargetColl(targetShardKey, targetSplit);
- const dottedPathIndexSpec = Object.merge(targetShardKey, {"newField.subField": 1});
- assert.commandWorked(targetColl.createIndex(dottedPathIndexSpec, {unique: true}));
+ // Test that a unique index with dotted field names can be used.
+ resetTargetColl(targetShardKey, targetSplit);
+ const dottedPathIndexSpec = Object.merge(targetShardKey, {"newField.subField": 1});
+ assert.commandWorked(targetColl.createIndex(dottedPathIndexSpec, {unique: true}));
- // No longer a supporting index on the original compound "on" fields.
- assertMergeFailsWithoutUniqueIndex({
- source: sourceColl,
- onFields: Object.keys(indexSpec),
- target: targetColl,
- prevStages: prefixPipeline
- });
+ // No longer a supporting index on the original compound "on" fields.
+ assertMergeFailsWithoutUniqueIndex({
+ source: sourceColl,
+ onFields: Object.keys(indexSpec),
+ target: targetColl,
+ prevStages: prefixPipeline
+ });
- // Test that an embedded object matching the "on" fields is valid.
- prefixPipeline = [{$addFields: {"newField.subField": 5}}];
- assertMergeSucceedsWithExpectedUniqueIndex({
- source: sourceColl,
- target: targetColl,
- onFields: Object.keys(dottedPathIndexSpec),
- prevStages: prefixPipeline
- });
+ // Test that an embedded object matching the "on" fields is valid.
+ prefixPipeline = [{$addFields: {"newField.subField": 5}}];
+ assertMergeSucceedsWithExpectedUniqueIndex({
+ source: sourceColl,
+ target: targetColl,
+ onFields: Object.keys(dottedPathIndexSpec),
+ prevStages: prefixPipeline
+ });
- // Test that we cannot use arrays with a dotted path within a $merge.
- resetTargetColl(targetShardKey, targetSplit);
- assert.commandWorked(targetColl.createIndex(dottedPathIndexSpec, {unique: true}));
- withEachMergeMode(
- ({whenMatchedMode, whenNotMatchedMode}) => {
- assertErrorCode(
- sourceColl,
- [
- {
- $replaceRoot:
- {newRoot: {$mergeObjects: ["$$ROOT", {newField: [{subField: 1}]}]}}
- },
- {
- $merge: {
- into: {
- db: targetColl.getDB().getName(),
- coll: targetColl.getName(),
+ // Test that we cannot use arrays with a dotted path within a $merge.
+ resetTargetColl(targetShardKey, targetSplit);
+ assert.commandWorked(targetColl.createIndex(dottedPathIndexSpec, {unique: true}));
+ withEachMergeMode(({whenMatchedMode, whenNotMatchedMode}) => {
+ assertErrorCode(sourceColl,
+ [
+ {
+ $replaceRoot: {
+ newRoot:
+ {$mergeObjects: ["$$ROOT", {newField: [{subField: 1}]}]}
+ }
},
- whenMatched: whenMatchedMode,
- whenNotMatched: whenNotMatchedMode,
- on: Object.keys(dottedPathIndexSpec)
- }
- }
- ],
- 51132);
- });
+ {
+ $merge: {
+ into: {
+ db: targetColl.getDB().getName(),
+ coll: targetColl.getName(),
+ },
+ whenMatched: whenMatchedMode,
+ whenNotMatched: whenNotMatchedMode,
+ on: Object.keys(dottedPathIndexSpec)
+ }
+ }
+ ],
+ 51132);
+ });
- // Test that a unique index that is multikey can still be used.
- resetTargetColl(targetShardKey, targetSplit);
- assert.commandWorked(targetColl.createIndex(dottedPathIndexSpec, {unique: true}));
- assert.commandWorked(targetColl.insert(
- Object.merge(targetShardKey, {newField: [{subField: "hi"}, {subField: "hello"}]})));
- assert.commandWorked(sourceColl.update(
- {}, {$set: {newField: {subField: "hi"}, proofOfUpdate: "PROOF"}}, {multi: true}));
+ // Test that a unique index that is multikey can still be used.
+ resetTargetColl(targetShardKey, targetSplit);
+ assert.commandWorked(targetColl.createIndex(dottedPathIndexSpec, {unique: true}));
+ assert.commandWorked(targetColl.insert(
+ Object.merge(targetShardKey, {newField: [{subField: "hi"}, {subField: "hello"}]})));
+ assert.commandWorked(sourceColl.update(
+ {}, {$set: {newField: {subField: "hi"}, proofOfUpdate: "PROOF"}}, {multi: true}));
- // If whenMatched is "replace" and whenNotMatched is "insert", expect the command to
- // fail if the "on" fields does not contain _id, since a replacement-style update will fail
- // if attempting to modify _id.
- if (dottedPathIndexSpec.hasOwnProperty("_id")) {
- assert.doesNotThrow(() => sourceColl.aggregate([{
+ // If whenMatched is "replace" and whenNotMatched is "insert", expect the command to
+ // fail if the "on" fields does not contain _id, since a replacement-style update will fail
+ // if attempting to modify _id.
+ if (dottedPathIndexSpec.hasOwnProperty("_id")) {
+ assert.doesNotThrow(() => sourceColl.aggregate([{
+ $merge: {
+ into: {
+ db: targetColl.getDB().getName(),
+ coll: targetColl.getName(),
+ },
+ whenMatched: "replace",
+ whenNotMatched: "insert",
+ on: Object.keys(dottedPathIndexSpec)
+ }
+ }]));
+ assert.docEq(targetColl.findOne({"newField.subField": "hi", proofOfUpdate: "PROOF"},
+ {"newField.subField": 1, proofOfUpdate: 1, _id: 0}),
+ {newField: {subField: "hi"}, proofOfUpdate: "PROOF"});
+ } else {
+ assertErrCodeAndErrMsgContains(sourceColl,
+ [{
+ $merge: {
+ into: {
+ db: targetColl.getDB().getName(),
+ coll: targetColl.getName(),
+ },
+ whenMatched: "replace",
+ whenNotMatched: "insert",
+ on: Object.keys(dottedPathIndexSpec)
+ }
+ }],
+ ErrorCodes.ImmutableField,
+ "did you attempt to modify the _id or the shard key?");
+
+ assert.doesNotThrow(() => sourceColl.aggregate([
+ {$project: {_id: 0}},
+ {
$merge: {
into: {
db: targetColl.getDB().getName(),
@@ -210,85 +241,53 @@
whenNotMatched: "insert",
on: Object.keys(dottedPathIndexSpec)
}
- }]));
- assert.docEq(targetColl.findOne({"newField.subField": "hi", proofOfUpdate: "PROOF"},
- {"newField.subField": 1, proofOfUpdate: 1, _id: 0}),
- {newField: {subField: "hi"}, proofOfUpdate: "PROOF"});
- } else {
- assertErrCodeAndErrMsgContains(sourceColl,
- [{
- $merge: {
- into: {
- db: targetColl.getDB().getName(),
- coll: targetColl.getName(),
- },
- whenMatched: "replace",
- whenNotMatched: "insert",
- on: Object.keys(dottedPathIndexSpec)
- }
- }],
- ErrorCodes.ImmutableField,
- "did you attempt to modify the _id or the shard key?");
-
- assert.doesNotThrow(() => sourceColl.aggregate([
- {$project: {_id: 0}},
- {
- $merge: {
- into: {
- db: targetColl.getDB().getName(),
- coll: targetColl.getName(),
- },
- whenMatched: "replace",
- whenNotMatched: "insert",
- on: Object.keys(dottedPathIndexSpec)
- }
- }
- ]));
- assert.docEq(targetColl.findOne({"newField.subField": "hi", proofOfUpdate: "PROOF"},
- {"newField.subField": 1, proofOfUpdate: 1, _id: 0}),
- {newField: {subField: "hi"}, proofOfUpdate: "PROOF"});
- }
+ }
+ ]));
+ assert.docEq(targetColl.findOne({"newField.subField": "hi", proofOfUpdate: "PROOF"},
+ {"newField.subField": 1, proofOfUpdate: 1, _id: 0}),
+ {newField: {subField: "hi"}, proofOfUpdate: "PROOF"});
}
+}
- function testAgainstDB(targetDB) {
- targetColl = targetDB["target"];
- targetColl.drop();
+function testAgainstDB(targetDB) {
+ targetColl = targetDB["target"];
+ targetColl.drop();
- //
- // Test unsharded source and sharded target collections.
- //
- let targetShardKey = {_id: 1, a: 1, b: 1};
- let splitPoint = {_id: 0, a: 0, b: 0};
- sourceColl.drop();
- assert.commandWorked(sourceColl.insert([{a: 0, b: 0}, {a: 1, b: 1}]));
- runOnFieldsTests(targetShardKey, splitPoint);
+ //
+ // Test unsharded source and sharded target collections.
+ //
+ let targetShardKey = {_id: 1, a: 1, b: 1};
+ let splitPoint = {_id: 0, a: 0, b: 0};
+ sourceColl.drop();
+ assert.commandWorked(sourceColl.insert([{a: 0, b: 0}, {a: 1, b: 1}]));
+ runOnFieldsTests(targetShardKey, splitPoint);
- // Test with a shard key that does *not* include _id.
- targetShardKey = {a: 1, b: 1};
- splitPoint = {a: 0, b: 0};
- runOnFieldsTests(targetShardKey, splitPoint);
+ // Test with a shard key that does *not* include _id.
+ targetShardKey = {a: 1, b: 1};
+ splitPoint = {a: 0, b: 0};
+ runOnFieldsTests(targetShardKey, splitPoint);
- //
- // Test both source and target collections as sharded.
- //
- targetShardKey = {_id: 1, a: 1, b: 1};
- splitPoint = {_id: 0, a: 0, b: 0};
- sourceColl.drop();
- st.shardColl(sourceColl.getName(), {a: 1}, {a: 0}, {a: 1}, mongosDB.getName());
- assert.commandWorked(sourceColl.insert([{a: 0, b: 0}, {a: 1, b: 1}]));
- runOnFieldsTests(targetShardKey, splitPoint);
+ //
+ // Test both source and target collections as sharded.
+ //
+ targetShardKey = {_id: 1, a: 1, b: 1};
+ splitPoint = {_id: 0, a: 0, b: 0};
+ sourceColl.drop();
+ st.shardColl(sourceColl.getName(), {a: 1}, {a: 0}, {a: 1}, mongosDB.getName());
+ assert.commandWorked(sourceColl.insert([{a: 0, b: 0}, {a: 1, b: 1}]));
+ runOnFieldsTests(targetShardKey, splitPoint);
- // Re-run the test with a shard key that does *not* include _id.
- targetShardKey = {a: 1, b: 1};
- splitPoint = {a: 0, b: 0};
- runOnFieldsTests(targetShardKey, splitPoint);
- }
+ // Re-run the test with a shard key that does *not* include _id.
+ targetShardKey = {a: 1, b: 1};
+ splitPoint = {a: 0, b: 0};
+ runOnFieldsTests(targetShardKey, splitPoint);
+}
- // First test $merge to the same database as the source.
- testAgainstDB(mongosDB);
+// First test $merge to the same database as the source.
+testAgainstDB(mongosDB);
- // Then test against a foreign database, with the same expected behavior.
- testAgainstDB(foreignDB);
+// Then test against a foreign database, with the same expected behavior.
+testAgainstDB(foreignDB);
- st.stop();
+st.stop();
})();