diff options
author | Jason Zhang <jason.zhang@mongodb.com> | 2021-11-29 16:25:30 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-11-29 17:21:29 +0000 |
commit | 8e983ae3261ea43ea3493fa082683f1aa718e998 (patch) | |
tree | 1c2a852845c42574ee284507fd2dbce346aeecc3 | |
parent | 95526e8d8efb0a453aec2c15c831fa98de5e4366 (diff) | |
download | mongo-8e983ae3261ea43ea3493fa082683f1aa718e998.tar.gz |
SERVER-53335 Queries, updates, and deletes with non-"simple" collations may miss documents when using hashed sharding
(cherry picked from commit c7dfb6bd1e3cbb6862c8189d2300943b02e5c494)
-rw-r--r-- | etc/backports_required_for_multiversion_tests.yml | 2 | ||||
-rw-r--r-- | jstests/libs/check_orphans_are_deleted_helpers.js | 12 | ||||
-rw-r--r-- | jstests/sharding/collation_lookup.js | 3 | ||||
-rw-r--r-- | jstests/sharding/collation_shard_targeting_hashed_shard_key.js | 416 | ||||
-rw-r--r-- | jstests/sharding/collation_targeting.js | 3 | ||||
-rw-r--r-- | jstests/sharding/collation_targeting_inherited.js | 3 | ||||
-rw-r--r-- | src/mongo/s/chunk_manager.cpp | 17 | ||||
-rw-r--r-- | src/mongo/s/chunk_manager.h | 4 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_find_and_modify_cmd.cpp | 7 |
9 files changed, 450 insertions, 17 deletions
diff --git a/etc/backports_required_for_multiversion_tests.yml b/etc/backports_required_for_multiversion_tests.yml index f4a30d5ff8e..7ab910681a8 100644 --- a/etc/backports_required_for_multiversion_tests.yml +++ b/etc/backports_required_for_multiversion_tests.yml @@ -179,6 +179,8 @@ all: test_file: jstests/replsets/initial_sync_replicate_drop_mid_secondary_batch.js - ticket: SERVER-54909 test_file: jstests/replsets/replSetGetStatus_member_wall_times.js + - ticket: SERVER-53335 + test_file: jstests/sharding/collation_shard_targeting_hashed_shard_key.js suites: diff --git a/jstests/libs/check_orphans_are_deleted_helpers.js b/jstests/libs/check_orphans_are_deleted_helpers.js index 12b8b918fa6..dcc7adfbe48 100644 --- a/jstests/libs/check_orphans_are_deleted_helpers.js +++ b/jstests/libs/check_orphans_are_deleted_helpers.js @@ -70,11 +70,15 @@ var CheckOrphansAreDeletedHelpers = (function() { .chunks.find({ns: ns, shard: {$ne: shardId}}) .forEach(chunkDoc => { // Use $min/$max so this will also work with hashed and compound shard keys. - const orphans = coll.find({}) - .hint(collDoc.key) - .min(chunkDoc.min) - .max(chunkDoc.max) + const orphans = new DBCommandCursor( + coll.getDB(), assert.commandWorked(coll.runCommand("find", { + collation: {locale: "simple"}, + hint: collDoc.key, + min: chunkDoc.min, + max: chunkDoc.max + }))) .toArray(); + assert.eq(0, orphans.length, 'found orphans @ ' + shardId + ' within chunk: ' + tojson(chunkDoc) + diff --git a/jstests/sharding/collation_lookup.js b/jstests/sharding/collation_lookup.js index dcb693265e0..ae3a15d59e6 100644 --- a/jstests/sharding/collation_lookup.js +++ b/jstests/sharding/collation_lookup.js @@ -13,9 +13,6 @@ load("jstests/aggregation/extras/utils.js"); // for arrayEq load("jstests/noPassthrough/libs/server_parameter_helpers.js"); // For setParameterOnAllHosts. load("jstests/libs/discover_topology.js"); // For findDataBearingNodes. -// Shard key index has collation, which is not compatible with $min/$max -TestData.skipCheckOrphans = true; - function runTests(withDefaultCollationColl, withoutDefaultCollationColl, collation) { // Test that the $lookup stage respects the inherited collation. let res = withDefaultCollationColl diff --git a/jstests/sharding/collation_shard_targeting_hashed_shard_key.js b/jstests/sharding/collation_shard_targeting_hashed_shard_key.js new file mode 100644 index 00000000000..649d0b0c309 --- /dev/null +++ b/jstests/sharding/collation_shard_targeting_hashed_shard_key.js @@ -0,0 +1,416 @@ + +/** + * Test shard targeting for queries on a collection with a non-simple collation and a hashed shard + * key. + * @tags: [ + * requires_find_command + * ] + */ +(function() { +const st = new ShardingTest({mongos: 1, config: 1, shards: 2, rs: {nodes: 1}}); + +function shardCollectionWithSplitsAndMoves( + ns, shardKeyPattern, collation, splitPoints, chunksToMove) { + const collection = st.s.getCollection(ns); + const db = collection.getDB(); + + assert.commandWorked(db.runCommand({create: collection.getName(), collation: collation})); + + st.ensurePrimaryShard(db.getName(), st.shard0.shardName); + assert.commandWorked(st.s.adminCommand({enableSharding: db.getName()})); + + assert.commandWorked(st.s.adminCommand({ + shardCollection: collection.getFullName(), + key: shardKeyPattern, + collation: {locale: "simple"} + })); + + for (let splitPoint of splitPoints) { + assert.commandWorked( + st.s.adminCommand({split: collection.getFullName(), middle: splitPoint})); + } + + for (let {query, shard} of chunksToMove) { + assert.commandWorked(st.s.adminCommand({ + moveChunk: collection.getFullName(), + find: query, + to: shard, + })); + } + + return collection; +} + +function findQueryWithCollation(collection, query, collation) { + let cursor = collection.find(query); + if (collation) { + cursor = cursor.collation(collation); + } + return cursor.toArray(); +} + +{ + jsTestLog( + "Test find command in an _id:hashed sharded collection with simple default collation."); + + const collection = shardCollectionWithSplitsAndMoves( + "test.id_hashed_sharding_with_simple_collation", + {_id: "hashed"}, + {locale: "simple"}, + [{_id: convertShardKeyToHashed("A")}, {_id: convertShardKeyToHashed("a")}], + [ + {query: {_id: "A"}, shard: st.shard0.shardName}, + {query: {_id: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: "A"}]; + assert.commandWorked(collection.insert(docs)); + + // Check default collation, simple collation, non-simple collation. + assert.eq([], + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {_id: "a"}, undefined)); + assert.eq([], + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {_id: "a"}, {locale: "simple"})); + assert.eq( + docs, + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {_id: "a"}, {locale: "en", strength: 2})); +} + +{ + jsTestLog( + "Test find command in an _id:hashed sharded collection with non-simple default collation."); + + const collection = shardCollectionWithSplitsAndMoves( + "test.id_hashed_sharding_with_default_collation", + {_id: "hashed"}, + {locale: "en", strength: 2}, + [{_id: convertShardKeyToHashed("A")}, {_id: convertShardKeyToHashed("a")}], + [ + {query: {_id: "A"}, shard: st.shard0.shardName}, + {query: {_id: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: "A"}]; + assert.commandWorked(collection.insert(docs)); + + // Check default collation, simple collation, non-simple collation. + assert.eq(docs, + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {_id: "a"}, undefined)); + assert.eq([], + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {_id: "a"}, {locale: "simple"})); + assert.eq( + docs, + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {_id: "a"}, {locale: "en", strength: 2})); +} + +{ + jsTestLog("Test an _id:1 sharded collection with non-simple default collation."); + + const collection = st.s.getCollection("test.id_range_sharding_with_default_collation"); + const db = collection.getDB(); + assert.commandWorked( + db.runCommand({create: collection.getName(), collation: {locale: "en", strength: 2}})); + + st.ensurePrimaryShard(db.getName(), st.shard0.shardName); + assert.commandWorked(st.s.adminCommand({enableSharding: db.getName()})); + + const res = assert.commandFailedWithCode(st.s.adminCommand({ + shardCollection: collection.getFullName(), + key: {_id: 1}, + collation: {locale: "simple"} + }), + ErrorCodes.BadValue); + assert(/The _id index must have the same collation as the collection/.test(res.errmsg), + `expected shardCollection command to fail due to required collation for _id index: ${ + tojson(res)}`); +} + +{ + jsTestLog("Test find command in a hashed sharded collection with simple default collation."); + const collection = shardCollectionWithSplitsAndMoves( + "test.non_id_hashed_sharding_with_simple_collation", + {notUnderscoreId: "hashed"}, + {locale: "simple"}, + [ + {notUnderscoreId: convertShardKeyToHashed("A")}, + {notUnderscoreId: convertShardKeyToHashed("a")} + ], + [ + {query: {notUnderscoreId: "A"}, shard: st.shard0.shardName}, + {query: {notUnderscoreId: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: 0, notUnderscoreId: "A"}]; + assert.commandWorked(collection.insert(docs)); + + // Check default collation, simple collation, non-simple collation. + assert.eq([], + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {notUnderscoreId: "a"}, undefined)); + assert.eq([], + findQueryWithCollation(st.s.getCollection(collection.getFullName()), + {notUnderscoreId: "a"}, + {locale: "simple"})); + assert.eq(docs, + findQueryWithCollation(st.s.getCollection(collection.getFullName()), + {notUnderscoreId: "a"}, + {locale: "en", strength: 2})); +} + +{ + jsTestLog( + "Test find command in a hashed sharded collection with non-simple default collation."); + + const collection = shardCollectionWithSplitsAndMoves( + "test.non_id_hashed_sharding_with_non_simple_collation", + {notUnderscoreId: "hashed"}, + {locale: "en", strength: 2}, + [ + {notUnderscoreId: convertShardKeyToHashed("A")}, + {notUnderscoreId: convertShardKeyToHashed("a")} + ], + [ + {query: {notUnderscoreId: "A"}, shard: st.shard0.shardName}, + {query: {notUnderscoreId: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: 0, notUnderscoreId: "A"}]; + assert.commandWorked(collection.insert(docs)); + + // Check default collation, simple collation, non-simple collation. + assert.eq(docs, + findQueryWithCollation( + st.s.getCollection(collection.getFullName()), {notUnderscoreId: "a"}, undefined)); + assert.eq([], + findQueryWithCollation(st.s.getCollection(collection.getFullName()), + {notUnderscoreId: "a"}, + {locale: "simple"})); + assert.eq(docs, + findQueryWithCollation(st.s.getCollection(collection.getFullName()), + {notUnderscoreId: "a"}, + {locale: "en", strength: 2})); +} + +{ + jsTestLog( + "Test findAndModify command in an _id:hashed sharded collection with simple default collation."); + + const collection = shardCollectionWithSplitsAndMoves( + "test.id_hashed_sharding_find_and_modify_simple_collation", + {_id: "hashed"}, + {locale: "simple"}, + [{_id: convertShardKeyToHashed("A")}, {_id: convertShardKeyToHashed("a")}], + [ + {query: {_id: "A"}, shard: st.shard0.shardName}, + {query: {_id: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: "A", count: 0}]; + assert.commandWorked(collection.insert(docs)); + + const mongosCollection = st.s.getCollection(collection.getFullName()); + + // Check findAndModify results with the default, simple, and non-simple collation. Currently, + // due to findAndModify's assumption that _id is uniquely targetable, we do not do a scatter + // gather to check every shard for a match. findAndModify's current behavior will target the + // first shard in which the max key of a chunk is greater than the query's shard key. In this + // case, because we're using hashed sharding, hash('a') is less than hash('A'), which means when + // we query for {_id: "a"} we will target the shard containing the chunk for "a", likewise if we + // query for {_id: "A"} we will only target the shard containing the chunk for "A". + assert.lt(convertShardKeyToHashed("a"), convertShardKeyToHashed("A")); + assert.eq(null, + mongosCollection.findAndModify({query: {_id: "a"}, update: {$inc: {count: 1}}})); + assert.eq(null, + mongosCollection.findAndModify( + {query: {_id: "a"}, update: {$inc: {count: 1}}, collation: {locale: "simple"}})); + assert.eq(null, mongosCollection.findAndModify({ + query: {_id: "a"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); + assert.eq({_id: "A", count: 0}, + mongosCollection.findAndModify({query: {_id: "A"}, update: {$inc: {count: 1}}})); + assert.eq({_id: "A", count: 1}, + mongosCollection.findAndModify( + {query: {_id: "A"}, update: {$inc: {count: 1}}, collation: {locale: "simple"}})); + assert.eq({_id: "A", count: 2}, mongosCollection.findAndModify({ + query: {_id: "A"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); +} + +{ + jsTestLog( + "Test findAndModify command in an _id:hashed sharded collection with non-simple default collation."); + + const collection = shardCollectionWithSplitsAndMoves( + "test.id_hashed_sharding_find_and_modify_with_non_simple_collation", + {_id: "hashed"}, + {locale: "en", strength: 2}, + [{_id: convertShardKeyToHashed("A")}, {_id: convertShardKeyToHashed("a")}], + [ + {query: {_id: "A"}, shard: st.shard0.shardName}, + {query: {_id: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: "A", count: 0}]; + assert.commandWorked(collection.insert(docs)); + + const mongosCollection = st.s.getCollection(collection.getFullName()); + + // Check findAndModify results with the default, simple, and non-simple collation. Currently, + // due to findAndModify's assumption that _id is uniquely targetable, we do not do a scatter + // gather to check every shard for a match. findAndModify's current behavior will target the + // first shard in which the max key of a chunk is greater than the query's shard key. In this + // case, because we're using hashed sharding, hash('a') is less than hash('A'), which means when + // we query for {_id: "a"} we will target the shard containing the chunk for "a", likewise if we + // query for {_id: "A"} we will only target the shard containing the chunk for "A". + assert.lt(convertShardKeyToHashed("a"), convertShardKeyToHashed("A")); + assert.eq(null, + mongosCollection.findAndModify({query: {_id: "a"}, update: {$inc: {count: 1}}})); + assert.eq(null, + mongosCollection.findAndModify( + {query: {_id: "a"}, update: {$inc: {count: 1}}, collation: {locale: "simple"}})); + assert.eq(null, mongosCollection.findAndModify({ + query: {_id: "a"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); + assert.eq({_id: "A", count: 0}, + mongosCollection.findAndModify({query: {_id: "A"}, update: {$inc: {count: 1}}})); + assert.eq({_id: "A", count: 1}, + mongosCollection.findAndModify( + {query: {_id: "A"}, update: {$inc: {count: 1}}, collation: {locale: "simple"}})); + assert.eq({_id: "A", count: 2}, mongosCollection.findAndModify({ + query: {_id: "A"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); +} + +{ + jsTestLog( + "Test findAndModify command in a hashed sharded collection with simple default collation."); + const collection = shardCollectionWithSplitsAndMoves( + "test.non_id_hashed_sharding_find_and_modify_with_simple_collation", + {notUnderscoreId: "hashed"}, + {locale: "simple"}, + [ + {notUnderscoreId: convertShardKeyToHashed("A")}, + {notUnderscoreId: convertShardKeyToHashed("a")} + ], + [ + {query: {notUnderscoreId: "A"}, shard: st.shard0.shardName}, + {query: {notUnderscoreId: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: 0, notUnderscoreId: "A", count: 0}]; + assert.commandWorked(collection.insert(docs)); + + const mongosCollection = st.s.getCollection(collection.getFullName()); + + // Check findAndModify results with the default, simple, and non-simple collation. Currently, + // due to findAndModify's assumption that _id is uniquely targetable, we do not do a scatter + // gather to check every shard for a match. findAndModify's current behavior will target the + // first shard in which the max key of a chunk is greater than the query's shard key. In this + // case, because we're using hashed sharding, hash('a') is less than hash('A'), which means when + // we query for {notUnderscoeId: "a"} we will target the shard containing the chunk for "a", + // likewise if we query for {notUnderscoreId: "A"} we will only target the shard containing the + // chunk for "A". + assert.lt(convertShardKeyToHashed("a"), convertShardKeyToHashed("A")); + assert.eq(null, + mongosCollection.findAndModify( + {query: {notUnderscoreId: "a"}, update: {$inc: {count: 1}}})); + assert.eq(null, mongosCollection.findAndModify({ + query: {notUnderscoreId: "a"}, + update: {$inc: {count: 1}}, + collation: {locale: "simple"} + })); + assert.eq(null, mongosCollection.findAndModify({ + query: {notUnderscoreId: "a"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); + assert.eq({_id: 0, notUnderscoreId: "A", count: 0}, + mongosCollection.findAndModify( + {query: {notUnderscoreId: "A"}, update: {$inc: {count: 1}}})); + assert.eq({_id: 0, notUnderscoreId: "A", count: 1}, mongosCollection.findAndModify({ + query: {notUnderscoreId: "A"}, + update: {$inc: {count: 1}}, + collation: {locale: "simple"} + })); + assert.eq({_id: 0, notUnderscoreId: "A", count: 2}, mongosCollection.findAndModify({ + query: {notUnderscoreId: "A"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); +} + +{ + jsTestLog( + "Test findAndModify command in a hashed sharded collection with non-simple default collation."); + + const collection = shardCollectionWithSplitsAndMoves( + "test.non_id_hashed_sharding_find_and_modify_with_non_simple_collation", + {notUnderscoreId: "hashed"}, + {locale: "en", strength: 2}, + [ + {notUnderscoreId: convertShardKeyToHashed("A")}, + {notUnderscoreId: convertShardKeyToHashed("a")} + ], + [ + {query: {notUnderscoreId: "A"}, shard: st.shard0.shardName}, + {query: {notUnderscoreId: "a"}, shard: st.shard1.shardName} + ]); + + const docs = [{_id: 0, notUnderscoreId: "A", count: 0}]; + assert.commandWorked(collection.insert(docs)); + + const mongosCollection = st.s.getCollection(collection.getFullName()); + + // Check findAndModify results with the default, simple, and non-simple collation. Currently, + // due to findAndModify's assumption that _id is uniquely targetable, we do not do a scatter + // gather to check every shard for a match. findAndModify's current behavior will target the + // first shard in which the max key of a chunk is greater than the query's shard key. In this + // case, because we're using hashed sharding, hash('a') is less than hash('A'), which means when + // we query for {notUnderscoreId: "a"} we will target the shard containing the chunk for "a", + // likewise if we query for {notUnderscoreId: "A"} we will only target the shard containing the + // chunk for "A". + assert.lt(convertShardKeyToHashed("a"), convertShardKeyToHashed("A")); + assert.eq(null, + mongosCollection.findAndModify( + {query: {notUnderscoreId: "a"}, update: {$inc: {count: 1}}})); + assert.eq(null, mongosCollection.findAndModify({ + query: {notUnderscoreId: "a"}, + update: {$inc: {count: 1}}, + collation: {locale: "simple"} + })); + assert.eq(null, mongosCollection.findAndModify({ + query: {notUnderscoreId: "a"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); + assert.eq({_id: 0, notUnderscoreId: "A", count: 0}, + mongosCollection.findAndModify( + {query: {notUnderscoreId: "A"}, update: {$inc: {count: 1}}})); + assert.eq({_id: 0, notUnderscoreId: "A", count: 1}, mongosCollection.findAndModify({ + query: {notUnderscoreId: "A"}, + update: {$inc: {count: 1}}, + collation: {locale: "simple"} + })); + assert.eq({_id: 0, notUnderscoreId: "A", count: 2}, mongosCollection.findAndModify({ + query: {notUnderscoreId: "A"}, + update: {$inc: {count: 1}}, + collation: {locale: "en", strength: 2} + })); +} + +st.stop(); +})();
\ No newline at end of file diff --git a/jstests/sharding/collation_targeting.js b/jstests/sharding/collation_targeting.js index 7b60eafc68d..5485a9cc428 100644 --- a/jstests/sharding/collation_targeting.js +++ b/jstests/sharding/collation_targeting.js @@ -3,9 +3,6 @@ (function() { "use strict"; -// Shard key index has collation, which is not compatible with $min/$max -TestData.skipCheckOrphans = true; - const caseInsensitive = { locale: "en_US", strength: 2 diff --git a/jstests/sharding/collation_targeting_inherited.js b/jstests/sharding/collation_targeting_inherited.js index c86198adbcb..ab3a94ef6d5 100644 --- a/jstests/sharding/collation_targeting_inherited.js +++ b/jstests/sharding/collation_targeting_inherited.js @@ -3,9 +3,6 @@ (function() { "use strict"; -// Shard key index has collation, which is not compatible with $min/$max -TestData.skipCheckOrphans = true; - const caseInsensitive = { locale: "en_US", strength: 2 diff --git a/src/mongo/s/chunk_manager.cpp b/src/mongo/s/chunk_manager.cpp index 2f45a4d7922..85539279c74 100644 --- a/src/mongo/s/chunk_manager.cpp +++ b/src/mongo/s/chunk_manager.cpp @@ -117,15 +117,28 @@ void RoutingTableHistory::setAllShardsRefreshed() { } } -Chunk ChunkManager::findIntersectingChunk(const BSONObj& shardKey, const BSONObj& collation) const { +Chunk ChunkManager::findIntersectingChunk(const BSONObj& shardKey, + const BSONObj& collation, + bool bypassIsFieldHashedCheck) const { const bool hasSimpleCollation = (collation.isEmpty() && !_rt->getDefaultCollator()) || SimpleBSONObjComparator::kInstance.evaluate(collation == CollationSpec::kSimpleSpec); if (!hasSimpleCollation) { for (BSONElement elt : shardKey) { + // We must assume that if the field is specified as "hashed" in the shard key pattern, + // then the hash value could have come from a collatable type. + const bool isFieldHashed = + (_rt->getShardKeyPattern().isHashedPattern() && + _rt->getShardKeyPattern().getHashedField().fieldNameStringData() == + elt.fieldNameStringData()); + + // If we want to skip the check in the special case where the _id field is hashed and + // used as the shard key, set bypassIsFieldHashedCheck. This assumes that a request with + // a query that contains an _id field can target a specific shard. uassert(ErrorCodes::ShardKeyNotFound, str::stream() << "Cannot target single shard due to collation of key " << elt.fieldNameStringData() << " for namespace " << getns(), - !CollationIndexKey::isCollatableType(elt.type())); + !CollationIndexKey::isCollatableType(elt.type()) && + (!isFieldHashed || bypassIsFieldHashedCheck)); } } diff --git a/src/mongo/s/chunk_manager.h b/src/mongo/s/chunk_manager.h index 6b4f97766ca..2c327966b07 100644 --- a/src/mongo/s/chunk_manager.h +++ b/src/mongo/s/chunk_manager.h @@ -374,7 +374,9 @@ public: * Throws a DBException with the ShardKeyNotFound code if unable to target a single shard due to * collation or due to the key not matching the shard key pattern. */ - Chunk findIntersectingChunk(const BSONObj& shardKey, const BSONObj& collation) const; + Chunk findIntersectingChunk(const BSONObj& shardKey, + const BSONObj& collation, + bool bypassIsFieldHashedCheck = false) const; /** * Same as findIntersectingChunk, but assumes the simple collation. diff --git a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp index 060cff95828..25c91604062 100644 --- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp +++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp @@ -277,7 +277,12 @@ public: const BSONObj query = cmdObjForShard.getObjectField("query"); const BSONObj collation = getCollation(cmdObjForShard); const BSONObj shardKey = getShardKey(opCtx, *chunkMgr, nss, query); - auto chunk = chunkMgr->findIntersectingChunk(shardKey, collation); + + // For now, set bypassIsFieldHashedCheck to be true in order to skip the + // isFieldHashedCheck in the special case where _id is hashed and used as the shard key. + // This means that we always assume that a findAndModify request using _id is targetable + // to a single shard. + auto chunk = chunkMgr->findIntersectingChunk(shardKey, collation, true); _runCommand(opCtx, chunk.getShardId(), |