summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheahuychou Mao <mao.cheahuychou@gmail.com>2023-03-28 16:06:55 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-03-28 19:51:51 +0000
commit3e44409d23b9924ceca60bb2429382fbcc8fc0c3 (patch)
tree61cfcf4fc51e174817ae33653826463b2add0c58
parentb5953fbf51dc9b626e33c8fc62eded65f99c6b49 (diff)
downloadmongo-3e44409d23b9924ceca60bb2429382fbcc8fc0c3.tar.gz
SERVER-75312 Make sure analyzeShardKey command can handle queries with $expr equality
-rw-r--r--jstests/sharding/analyze_shard_key/read_and_write_distribution.js2
-rw-r--r--src/mongo/db/s/analyze_shard_key_read_write_distribution.cpp204
-rw-r--r--src/mongo/db/s/analyze_shard_key_read_write_distribution.h36
-rw-r--r--src/mongo/db/s/analyze_shard_key_read_write_distribution_test.cpp148
-rw-r--r--src/mongo/s/chunk_manager_query_test.cpp102
-rw-r--r--src/mongo/s/cluster_commands_helpers.cpp16
-rw-r--r--src/mongo/s/collection_routing_info_targeter.cpp7
-rw-r--r--src/mongo/s/commands/cluster_query_without_shard_key_cmd.cpp9
-rw-r--r--src/mongo/s/shard_key_pattern_query_util.cpp47
-rw-r--r--src/mongo/s/shard_key_pattern_query_util.h25
10 files changed, 293 insertions, 303 deletions
diff --git a/jstests/sharding/analyze_shard_key/read_and_write_distribution.js b/jstests/sharding/analyze_shard_key/read_and_write_distribution.js
index 1de3a289960..1087ee97004 100644
--- a/jstests/sharding/analyze_shard_key/read_and_write_distribution.js
+++ b/jstests/sharding/analyze_shard_key/read_and_write_distribution.js
@@ -257,7 +257,7 @@ function makeTestCase(collName, {shardKeyField, isHashed, minVal, maxVal}) {
update: collName,
updates: [
{q: {[shardKeyField]: getNextVal()}, u: {$set: {z: 0}}},
- {q: {[shardKeyField]: getNextVal()}, u: {$set: {z: 0}}}
+ {q: {$expr: {$eq: ["$" + shardKeyField, getNextVal()]}}, u: [{$set: {z: 0}}]}
]
});
writeDistribution.sampleSize.update += 2;
diff --git a/src/mongo/db/s/analyze_shard_key_read_write_distribution.cpp b/src/mongo/db/s/analyze_shard_key_read_write_distribution.cpp
index 55db53ef6ce..70bbeef8aab 100644
--- a/src/mongo/db/s/analyze_shard_key_read_write_distribution.cpp
+++ b/src/mongo/db/s/analyze_shard_key_read_write_distribution.cpp
@@ -48,40 +48,6 @@
namespace mongo {
namespace analyze_shard_key {
-namespace {
-
-/**
- * Returns true if the query that specifies the given collation against the collection with the
- * given default collator has simple collation.
- */
-bool hasSimpleCollation(const CollatorInterface* defaultCollator, const BSONObj& collation) {
- if (collation.isEmpty()) {
- return !defaultCollator;
- }
- return SimpleBSONObjComparator::kInstance.evaluate(collation == CollationSpec::kSimpleSpec);
-}
-
-/**
- * Returns true if the given shard key contains any collatable fields (ones that can be affected in
- * comparison or sort order by collation).
- */
-bool shardKeyHasCollatableType(const ShardKeyPattern& shardKeyPattern, const BSONObj& shardKey) {
- for (const BSONElement& elt : shardKey) {
- if (CollationIndexKey::isCollatableType(elt.type())) {
- return true;
- }
- if (shardKeyPattern.isHashedPattern() &&
- shardKeyPattern.getHashedField().fieldNameStringData() == elt.fieldNameStringData()) {
- // If the field is specified as "hashed" in the shard key pattern, then the hash value
- // could have come from a collatable type.
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
template <typename DistributionMetricsType, typename SampleSizeType>
DistributionMetricsType
DistributionMetricsCalculator<DistributionMetricsType, SampleSizeType>::_getMetrics() const {
@@ -106,23 +72,13 @@ DistributionMetricsCalculator<DistributionMetricsType, SampleSizeType>::_getMetr
}
template <typename DistributionMetricsType, typename SampleSizeType>
-BSONObj
-DistributionMetricsCalculator<DistributionMetricsType, SampleSizeType>::_incrementMetricsForQuery(
+QueryTargetingInfo
+DistributionMetricsCalculator<DistributionMetricsType, SampleSizeType>::_getTargetingInfoForQuery(
OperationContext* opCtx,
- const BSONObj& primaryFilter,
+ const BSONObj& filter,
const BSONObj& collation,
- const BSONObj& secondaryFilter,
- const boost::optional<LegacyRuntimeConstants>& runtimeConstants,
- const boost::optional<BSONObj>& letParameters) {
- auto filter = primaryFilter;
- auto shardKey = uassertStatusOK(extractShardKeyFromBasicQuery(
- opCtx, _targeter.getNS(), _getShardKeyPattern(), primaryFilter));
- if (shardKey.isEmpty() && !secondaryFilter.isEmpty()) {
- shardKey = _getShardKeyPattern().extractShardKeyFromDoc(secondaryFilter);
- filter = shardKey;
- }
-
- // Increment metrics about range targeting.
+ const boost::optional<BSONObj>& letParameters,
+ const boost::optional<LegacyRuntimeConstants>& runtimeConstants) {
auto&& cif = [&]() {
if (collation.isEmpty()) {
return std::unique_ptr<CollatorInterface>{};
@@ -131,48 +87,51 @@ DistributionMetricsCalculator<DistributionMetricsType, SampleSizeType>::_increme
CollatorFactoryInterface::get(opCtx->getServiceContext())->makeFromBSON(collation));
}();
auto expCtx = make_intrusive<ExpressionContext>(
- opCtx, std::move(cif), _getChunkManager().getNss(), runtimeConstants, letParameters);
+ opCtx,
+ std::move(cif),
+ _getChunkManager().getNss(),
+ runtimeConstants.value_or(Variables::generateRuntimeConstants(opCtx)),
+ letParameters);
std::set<ShardId> shardIds; // This is not used.
- std::set<ChunkRange> chunkRanges;
- bool targetMinkeyToMaxKey = false;
- getShardIdsForQuery(expCtx,
- filter,
- collation,
- _getChunkManager(),
- &shardIds,
- &chunkRanges,
- &targetMinkeyToMaxKey);
- _incrementNumByRanges(chunkRanges);
+ QueryTargetingInfo info;
+ getShardIdsForQuery(expCtx, filter, collation, _getChunkManager(), &shardIds, &info);
+
+ return info;
+}
- // Increment metrics about sharding targeting.
- if (!shardKey.isEmpty()) {
- // This query filters by shard key equality. If the query has a simple collation or the
- // shard key doesn't contain a collatable field, then there is only one matching shard key
- // value so the query is guaranteed to target only one shard. Otherwise, the number of
- // shards that it targets depend on how the matching shard key values are distributed among
- // shards. Given this, pessimistically classify it as targeting to multiple shards.
- invariant(!targetMinkeyToMaxKey);
- if (hasSimpleCollation(_getDefaultCollator(), collation) ||
- !shardKeyHasCollatableType(_getShardKeyPattern(), shardKey)) {
+template <typename DistributionMetricsType, typename SampleSizeType>
+void DistributionMetricsCalculator<DistributionMetricsType, SampleSizeType>::
+ _incrementMetricsForQuery(const QueryTargetingInfo& info) {
+ // Increment metrics about range targeting.
+ _incrementNumByRanges(info.chunkRanges);
+
+ // Increment metrics about shard targeting.
+ switch (info.desc) {
+ case QueryTargetingInfo::Description::kSingleKey: {
_incrementNumSingleShard();
- invariant(chunkRanges.size() == 1U);
- } else {
+ tassert(7531200,
+ "Found a point query that targets multiple chunks",
+ info.chunkRanges.size() == 1U);
+ break;
+ }
+ case QueryTargetingInfo::Description::kMultipleKeys: {
+ // This query targets a subset of the shard key space. Therefore, the number of shards
+ // that it targets depends on how the matching shard key ranges are distributed among
+ // shards. Given this, pessimistically classify it as targeting to multiple shards.
_incrementNumMultiShard();
+ break;
+ }
+ case QueryTargetingInfo::Description::kMinKeyToMaxKey: {
+ // This query targets the entire shard key space. Therefore, it always targets all
+ // shards and chunks.
+ _incrementNumScatterGather();
+ invariant((int)info.chunkRanges.size() == _getChunkManager().numChunks());
+ break;
}
- } else if (targetMinkeyToMaxKey) {
- // This query targets the entire shard key space. Therefore, it always targets all
- // shards and chunks.
- _incrementNumScatterGather();
- invariant((int)chunkRanges.size() == _getChunkManager().numChunks());
- } else {
- // This query targets a subset of the shard key space. Therefore, the number of shards
- // that it targets depends on how the matching shard key ranges are distributed among
- // shards. Given this, pessimistically classify it as targeting to multiple shards.
- _incrementNumMultiShard();
+ default:
+ MONGO_UNREACHABLE;
}
-
- return shardKey;
}
ReadSampleSize ReadDistributionMetricsCalculator::_getSampleSize() const {
@@ -210,7 +169,8 @@ void ReadDistributionMetricsCalculator::addQuery(OperationContext* opCtx,
auto cmd = SampledReadCommand::parse(IDLParserContext("ReadDistributionMetricsCalculator"),
doc.getCmd());
- _incrementMetricsForQuery(opCtx, cmd.getFilter(), cmd.getCollation());
+ auto info = _getTargetingInfoForQuery(opCtx, cmd.getFilter(), cmd.getCollation());
+ _incrementMetricsForQuery(info);
}
WriteSampleSize WriteDistributionMetricsCalculator::_getSampleSize() const {
@@ -269,16 +229,18 @@ void WriteDistributionMetricsCalculator::_addUpdateQuery(
OperationContext* opCtx, const write_ops::UpdateCommandRequest& cmd) {
for (const auto& updateOp : cmd.getUpdates()) {
_numUpdate++;
- auto primaryFilter = updateOp.getQ();
auto collation = write_ops::collationOf(updateOp);
- // If this is a non-upsert replacement update, the replacement document can be used as a
- // filter.
- auto secondaryFilter = [&] {
+ auto info = _getTargetingInfoForQuery(
+ opCtx, updateOp.getQ(), collation, cmd.getLet(), cmd.getLegacyRuntimeConstants());
+
+ if (info.desc != QueryTargetingInfo::Description::kSingleKey) {
+ // If this is a non-upsert replacement update, the replacement document can be used as
+ // the filter.
auto isReplacementUpdate = !updateOp.getUpsert() &&
updateOp.getU().type() == write_ops::UpdateModification::Type::kReplacement;
auto isExactIdQuery = [&] {
return CollectionRoutingInfoTargeter::isExactIdQuery(
- opCtx, cmd.getNamespace(), primaryFilter, collation, _getChunkManager());
+ opCtx, cmd.getNamespace(), updateOp.getQ(), collation, _getChunkManager());
};
// Currently, targeting by replacement document is only done when an updateOne without
@@ -287,17 +249,13 @@ void WriteDistributionMetricsCalculator::_addUpdateQuery(
(!feature_flags::gFeatureFlagUpdateOneWithoutShardKey.isEnabled(
serverGlobalParams.featureCompatibility) ||
isExactIdQuery())) {
- return updateOp.getU().getUpdateReplacement();
+ auto filter = _getShardKeyPattern().extractShardKeyFromDoc(
+ updateOp.getU().getUpdateReplacement());
+ info = _getTargetingInfoForQuery(
+ opCtx, filter, collation, cmd.getLet(), cmd.getLegacyRuntimeConstants());
}
- return BSONObj();
- }();
- _incrementMetricsForQuery(opCtx,
- primaryFilter,
- secondaryFilter,
- collation,
- updateOp.getMulti(),
- cmd.getLegacyRuntimeConstants(),
- cmd.getLet());
+ }
+ _incrementMetricsForQuery(info, updateOp.getMulti());
}
}
@@ -305,44 +263,30 @@ void WriteDistributionMetricsCalculator::_addDeleteQuery(
OperationContext* opCtx, const write_ops::DeleteCommandRequest& cmd) {
for (const auto& deleteOp : cmd.getDeletes()) {
_numDelete++;
- auto primaryFilter = deleteOp.getQ();
- auto secondaryFilter = BSONObj();
- _incrementMetricsForQuery(opCtx,
- primaryFilter,
- secondaryFilter,
- write_ops::collationOf(deleteOp),
- deleteOp.getMulti(),
- cmd.getLegacyRuntimeConstants(),
- cmd.getLet());
+ auto info = _getTargetingInfoForQuery(opCtx,
+ deleteOp.getQ(),
+ write_ops::collationOf(deleteOp),
+ cmd.getLet(),
+ cmd.getLegacyRuntimeConstants());
+ _incrementMetricsForQuery(info, deleteOp.getMulti());
}
}
void WriteDistributionMetricsCalculator::_addFindAndModifyQuery(
OperationContext* opCtx, const write_ops::FindAndModifyCommandRequest& cmd) {
_numFindAndModify++;
- auto primaryFilter = cmd.getQuery();
- auto secondaryFilter = BSONObj();
- _incrementMetricsForQuery(opCtx,
- primaryFilter,
- secondaryFilter,
- cmd.getCollation().value_or(BSONObj()),
- false /* isMulti */,
- cmd.getLegacyRuntimeConstants(),
- cmd.getLet());
+ auto info = _getTargetingInfoForQuery(opCtx,
+ cmd.getQuery(),
+ cmd.getCollation().value_or(BSONObj()),
+ cmd.getLet(),
+ cmd.getLegacyRuntimeConstants());
+ _incrementMetricsForQuery(info, false /* isMulti */);
}
-void WriteDistributionMetricsCalculator::_incrementMetricsForQuery(
- OperationContext* opCtx,
- const BSONObj& primaryFilter,
- const BSONObj& secondaryFilter,
- const BSONObj& collation,
- bool isMulti,
- const boost::optional<LegacyRuntimeConstants>& runtimeConstants,
- const boost::optional<BSONObj>& letParameters) {
- auto shardKey = DistributionMetricsCalculator::_incrementMetricsForQuery(
- opCtx, primaryFilter, collation, secondaryFilter, runtimeConstants, letParameters);
-
- if (shardKey.isEmpty()) {
+void WriteDistributionMetricsCalculator::_incrementMetricsForQuery(const QueryTargetingInfo& info,
+ bool isMulti) {
+ DistributionMetricsCalculator::_incrementMetricsForQuery(info);
+ if (info.desc != QueryTargetingInfo::Description::kSingleKey) {
// Increment metrics about writes without shard key.
if (isMulti) {
_incrementNumMultiWritesWithoutShardKey();
diff --git a/src/mongo/db/s/analyze_shard_key_read_write_distribution.h b/src/mongo/db/s/analyze_shard_key_read_write_distribution.h
index 42eb5dd3d43..12a7109d8c7 100644
--- a/src/mongo/db/s/analyze_shard_key_read_write_distribution.h
+++ b/src/mongo/db/s/analyze_shard_key_read_write_distribution.h
@@ -36,10 +36,15 @@
#include "mongo/s/analyze_shard_key_documents_gen.h"
#include "mongo/s/analyze_shard_key_util.h"
#include "mongo/s/collection_routing_info_targeter.h"
+#include "mongo/s/shard_key_pattern_query_util.h"
namespace mongo {
namespace analyze_shard_key {
+namespace {
+using QueryTargetingInfo = shard_key_pattern_query_util::QueryTargetingInfo;
+}
+
/**
* The utility class for calculating read or write distribution metrics for sampled queries against
* the collection with the given routing info.
@@ -98,20 +103,21 @@ protected:
}
/**
- * The helper for 'addQuery'. Increments the metrics for the query with the given filter(s),
- * collation, run-time contants and let parameters. The secondary filter is only applicable to
- * non-upsert replacement updates, and the run-time constants and let parameters are only
- * applicable to writes.
- *
- * If the query filters by shard key equality, returns the shard key value.
+ * The helper used by 'addQuery' to get the targeting info for a query with the given filter,
+ * collation, let parameters and runtime contants.
*/
- BSONObj _incrementMetricsForQuery(
+ QueryTargetingInfo _getTargetingInfoForQuery(
OperationContext* opCtx,
- const BSONObj& primaryfilter,
+ const BSONObj& filter,
const BSONObj& collation,
- const BSONObj& secondaryFilter = BSONObj(),
- const boost::optional<LegacyRuntimeConstants>& runtimeConstants = boost::none,
- const boost::optional<BSONObj>& letParameters = boost::none);
+ const boost::optional<BSONObj>& letParameters = boost::none,
+ const boost::optional<LegacyRuntimeConstants>& runtimeConstants = boost::none);
+
+ /**
+ * The helper used by 'addQuery' to increment the metrics for a query with the given targeting
+ * info.
+ */
+ void _incrementMetricsForQuery(const QueryTargetingInfo& info);
const ChunkManager& _getChunkManager() const {
return _targeter.getRoutingInfo().cm;
@@ -199,13 +205,7 @@ private:
_numMultiWritesWithoutShardKey++;
}
- void _incrementMetricsForQuery(OperationContext* opCtx,
- const BSONObj& primaryFilter,
- const BSONObj& secondaryFilter,
- const BSONObj& collation,
- bool isMulti,
- const boost::optional<LegacyRuntimeConstants>& runtimeConstants,
- const boost::optional<BSONObj>& letParameters);
+ void _incrementMetricsForQuery(const QueryTargetingInfo& info, bool isMulti);
int64_t _numUpdate = 0;
int64_t _numDelete = 0;
diff --git a/src/mongo/db/s/analyze_shard_key_read_write_distribution_test.cpp b/src/mongo/db/s/analyze_shard_key_read_write_distribution_test.cpp
index 8af7634343e..276b5991868 100644
--- a/src/mongo/db/s/analyze_shard_key_read_write_distribution_test.cpp
+++ b/src/mongo/db/s/analyze_shard_key_read_write_distribution_test.cpp
@@ -159,9 +159,11 @@ protected:
}
SampledQueryDocument makeSampledUpdateQueryDocument(
- const std::vector<write_ops::UpdateOpEntry>& updateOps) const {
+ const std::vector<write_ops::UpdateOpEntry>& updateOps,
+ const boost::optional<BSONObj>& letParameters = boost::none) const {
write_ops::UpdateCommandRequest cmd(nss);
cmd.setUpdates(updateOps);
+ cmd.setLet(letParameters);
return {UUID::gen(),
nss,
collUuid,
@@ -173,9 +175,11 @@ protected:
}
SampledQueryDocument makeSampledDeleteQueryDocument(
- const std::vector<write_ops::DeleteOpEntry>& deleteOps) const {
+ const std::vector<write_ops::DeleteOpEntry>& deleteOps,
+ const boost::optional<BSONObj>& letParameters = boost::none) const {
write_ops::DeleteCommandRequest cmd(nss);
cmd.setDeletes(deleteOps);
+ cmd.setLet(letParameters);
return {UUID::gen(),
nss,
collUuid,
@@ -191,13 +195,15 @@ protected:
const write_ops::UpdateModification& update,
bool upsert,
bool remove,
- const BSONObj& collation = BSONObj()) const {
+ const BSONObj& collation = BSONObj(),
+ const boost::optional<BSONObj>& letParameters = boost::none) const {
write_ops::FindAndModifyCommandRequest cmd(nss);
cmd.setQuery(filter);
cmd.setUpdate(update);
cmd.setUpsert(upsert);
cmd.setRemove(remove);
cmd.setCollation(collation);
+ cmd.setLet(letParameters);
return {UUID::gen(),
nss,
collUuid,
@@ -821,6 +827,21 @@ TEST_F(ReadDistributionFilterByShardKeyEqualityTest, ShardKeyEqualityHashed) {
hasCollatableType);
}
+TEST_F(ReadDistributionFilterByShardKeyEqualityTest, ShardKeyEqualityExpression) {
+ auto targeter = makeCollectionRoutingInfoTargeter(chunkSplitInfoRangeSharding0);
+ auto filter = BSON("$expr" << BSON("$and" << BSON_ARRAY(BSON("$eq" << BSON_ARRAY("$a.x" << 100))
+ << BSON("$eq" << BSON_ARRAY("$b.y"
+ << "A")))));
+ auto numByRange = std::vector<int64_t>({0, 0, 1});
+ auto hasSimpleCollation = true;
+ auto hasCollatableType = true;
+ assertTargetMetrics(targeter,
+ makeSampledReadQueryDocument(getRandomSampledReadCommandName(), filter),
+ numByRange,
+ hasSimpleCollation,
+ hasCollatableType);
+}
+
class ReadDistributionFilterByShardKeyRangeTest : public ReadWriteDistributionTest {
protected:
void assertTargetMetrics(const CollectionRoutingInfoTargeter& targeter,
@@ -972,12 +993,18 @@ protected:
const SampledQueryDocument& queryDoc,
const std::vector<int64_t>& numByRange,
bool hasSimpleCollation,
- bool hasCollatableType) const {
+ bool hasCollatableType,
+ bool multi = false) const {
WriteMetrics metrics;
if (hasSimpleCollation || !hasCollatableType) {
metrics.numSingleShard = 1;
} else {
metrics.numMultiShard = 1;
+ if (multi) {
+ metrics.numMultiWritesWithoutShardKey = 1;
+ } else {
+ metrics.numSingleWritesWithoutShardKey = 1;
+ }
}
metrics.numByRange = numByRange;
assertMetricsForWriteQuery(targeter, queryDoc, metrics);
@@ -989,32 +1016,38 @@ protected:
SampledQueryDocument makeSampledUpdateQueryDocument(
const BSONObj& filter,
const BSONObj& updateMod,
- const BSONObj& collation = BSONObj()) const {
+ const BSONObj& collation = BSONObj(),
+ bool multi = false,
+ const boost::optional<BSONObj>& letParameters = boost::none) const {
auto updateOp = write_ops::UpdateOpEntry(filter, write_ops::UpdateModification(updateMod));
- updateOp.setMulti(getRandomBool());
+ updateOp.setMulti(multi);
updateOp.setUpsert(getRandomBool());
updateOp.setCollation(collation);
- return ReadWriteDistributionTest::makeSampledUpdateQueryDocument({updateOp});
+ return ReadWriteDistributionTest::makeSampledUpdateQueryDocument({updateOp}, letParameters);
}
SampledQueryDocument makeSampledDeleteQueryDocument(
- const BSONObj& filter, const BSONObj& collation = BSONObj()) const {
- auto deleteOp = write_ops::DeleteOpEntry(filter, getRandomBool() /* multi */);
+ const BSONObj& filter,
+ const BSONObj& collation = BSONObj(),
+ bool multi = false,
+ const boost::optional<BSONObj>& letParameters = boost::none) const {
+ auto deleteOp = write_ops::DeleteOpEntry(filter, multi);
deleteOp.setCollation(collation);
- return ReadWriteDistributionTest::makeSampledDeleteQueryDocument({deleteOp});
+ return ReadWriteDistributionTest::makeSampledDeleteQueryDocument({deleteOp}, letParameters);
}
SampledQueryDocument makeSampledFindAndModifyQueryDocument(
const BSONObj& filter,
const BSONObj& updateMod,
- const BSONObj& collation = BSONObj()) const {
-
+ const BSONObj& collation = BSONObj(),
+ const boost::optional<BSONObj>& letParameters = boost::none) const {
return ReadWriteDistributionTest::makeSampledFindAndModifyQueryDocument(
filter,
updateMod,
getRandomBool() /* upsert */,
getRandomBool() /* remove */,
- collation);
+ collation,
+ letParameters);
}
};
@@ -1116,29 +1149,35 @@ TEST_F(WriteDistributionFilterByShardKeyEqualityTest,
const BSONObj& filter,
const BSONObj& updateMod,
const std::vector<int64_t>& numByRange) {
- // The collection has a non-simple default collation and the query specifies an empty
- // collation.
- auto targeter0 = makeCollectionRoutingInfoTargeter(
- chunkSplitInfo,
- uassertStatusOK(CollatorFactoryInterface::get(getServiceContext())
- ->makeFromBSON(caseInsensitiveCollation)));
- assertTargetMetrics(targeter0,
- makeSampledUpdateQueryDocument(filter, updateMod, emptyCollation),
- numByRange,
- hasSimpleCollation,
- hasCollatableType);
-
- // The collection has a simple default collation and the query specifies a non-simple
- // collation.
- auto targeter1 = makeCollectionRoutingInfoTargeter(
- chunkSplitInfo,
- uassertStatusOK(
- CollatorFactoryInterface::get(getServiceContext())->makeFromBSON(simpleCollation)));
- assertTargetMetrics(targeter1,
- makeSampledDeleteQueryDocument(filter, caseInsensitiveCollation),
- numByRange,
- hasSimpleCollation,
- hasCollatableType);
+ for (auto& multi : {true, false}) {
+ // The collection has a non-simple default collation and the query specifies an empty
+ // collation.
+ auto targeter0 = makeCollectionRoutingInfoTargeter(
+ chunkSplitInfo,
+ uassertStatusOK(CollatorFactoryInterface::get(getServiceContext())
+ ->makeFromBSON(caseInsensitiveCollation)));
+ assertTargetMetrics(
+ targeter0,
+ makeSampledUpdateQueryDocument(filter, updateMod, emptyCollation, multi),
+ numByRange,
+ hasSimpleCollation,
+ hasCollatableType,
+ multi);
+
+ // The collection has a simple default collation and the query specifies a non-simple
+ // collation.
+ auto targeter1 = makeCollectionRoutingInfoTargeter(
+ chunkSplitInfo,
+ uassertStatusOK(CollatorFactoryInterface::get(getServiceContext())
+ ->makeFromBSON(simpleCollation)));
+ assertTargetMetrics(
+ targeter1,
+ makeSampledDeleteQueryDocument(filter, caseInsensitiveCollation, multi),
+ numByRange,
+ hasSimpleCollation,
+ hasCollatableType,
+ multi);
+ }
// The collection doesn't have a default collation and the query specifies a non-simple
// collation.
@@ -1348,6 +1387,43 @@ TEST_F(WriteDistributionFilterByShardKeyEqualityTest, ShardKeyEqualitySuffixFiel
hasCollatableType);
}
+TEST_F(WriteDistributionFilterByShardKeyEqualityTest, ShardKeyEqualityExpressionWithLetParameters) {
+ auto targeter = makeCollectionRoutingInfoTargeter(chunkSplitInfoRangeSharding0);
+ auto filter = BSON("$expr" << BSON("$and" << BSON_ARRAY(BSON("$eq" << BSON_ARRAY("$a.x"
+ << "$$value"))
+ << BSON("$eq" << BSON_ARRAY("$b.y"
+ << "A")))));
+ auto updateMod = BSON("$set" << BSON("c" << 100));
+ auto collation = BSONObj();
+ auto letParameters = BSON("value" << 100);
+
+ auto numByRange = std::vector<int64_t>({0, 0, 1});
+ auto hasSimpleCollation = true;
+ auto hasCollatableType = true;
+
+ for (auto& multi : {true, false}) {
+ assertTargetMetrics(
+ targeter,
+ makeSampledUpdateQueryDocument(filter, updateMod, collation, multi, letParameters),
+ numByRange,
+ hasSimpleCollation,
+ hasCollatableType,
+ multi);
+ assertTargetMetrics(targeter,
+ makeSampledDeleteQueryDocument(filter, collation, multi, letParameters),
+ numByRange,
+ hasSimpleCollation,
+ hasCollatableType,
+ multi);
+ }
+ assertTargetMetrics(
+ targeter,
+ makeSampledFindAndModifyQueryDocument(filter, updateMod, collation, letParameters),
+ numByRange,
+ hasSimpleCollation,
+ hasCollatableType);
+}
+
class WriteDistributionFilterByShardKeyRangeTest : public ReadWriteDistributionTest {
protected:
void assertTargetMetrics(const CollectionRoutingInfoTargeter& targeter,
diff --git a/src/mongo/s/chunk_manager_query_test.cpp b/src/mongo/s/chunk_manager_query_test.cpp
index bc545d68557..9b47635c0b6 100644
--- a/src/mongo/s/chunk_manager_query_test.cpp
+++ b/src/mongo/s/chunk_manager_query_test.cpp
@@ -43,6 +43,8 @@
namespace mongo {
namespace {
+using shard_key_pattern_query_util::QueryTargetingInfo;
+
const NamespaceString kNss = NamespaceString::createNamespaceString_forTest("TestDB", "TestColl");
class ChunkManagerQueryTest : public CatalogCacheTestFixture {
@@ -70,7 +72,7 @@ protected:
const BSONObj& query,
const BSONObj& queryCollation,
const std::set<ShardId>& expectedShardIds,
- bool expectTargetMinKeyToMaxKey) {
+ QueryTargetingInfo expectedQueryTargetingInfo) {
const ShardKeyPattern shardKeyPattern(shardKey);
auto chunkManager =
makeCollectionRoutingInfo(
@@ -78,8 +80,7 @@ protected:
.cm;
std::set<ShardId> shardIds;
- std::set<ChunkRange> chunkRanges;
- bool targetMinKeyToMaxKey = false;
+ QueryTargetingInfo info;
auto&& cif = [&]() {
if (queryCollation.isEmpty()) {
@@ -91,15 +92,10 @@ protected:
}();
auto expCtx =
make_intrusive<ExpressionContextForTest>(operationContext(), kNss, std::move(cif));
- getShardIdsForQuery(expCtx,
- query,
- queryCollation,
- chunkManager,
- &shardIds,
- &chunkRanges,
- &targetMinKeyToMaxKey);
+ getShardIdsForQuery(expCtx, query, queryCollation, chunkManager, &shardIds, &info);
_assertShardIdsMatch(expectedShardIds, shardIds);
- ASSERT_EQ(expectTargetMinKeyToMaxKey, targetMinKeyToMaxKey);
+ // The test coverage for chunk ranges is in CollectionRoutingInfoTargeterTest.
+ ASSERT_EQ(expectedQueryTargetingInfo.desc, info.desc);
}
private:
@@ -147,7 +143,6 @@ TEST_F(ChunkManagerQueryTest, GetShardIdsForRangeMinAndMaxAreTheSameAtLastChunkM
}
TEST_F(ChunkManagerQueryTest, EmptyQuerySingleShard) {
- bool expectTargetMinKeyToMaxKey = true;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -155,11 +150,10 @@ TEST_F(ChunkManagerQueryTest, EmptyQuerySingleShard) {
BSONObj(),
BSONObj(),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMinKeyToMaxKey, {}});
}
TEST_F(ChunkManagerQueryTest, EmptyQueryMultiShard) {
- bool expectTargetMinKeyToMaxKey = true;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -172,11 +166,10 @@ TEST_F(ChunkManagerQueryTest, EmptyQueryMultiShard) {
BSONObj(),
BSONObj(),
{ShardId("0"), ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMinKeyToMaxKey, {}});
}
TEST_F(ChunkManagerQueryTest, UniversalRangeMultiShard) {
- bool expectTargetMinKeyToMaxKey = true;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -189,11 +182,10 @@ TEST_F(ChunkManagerQueryTest, UniversalRangeMultiShard) {
BSON("b" << 1),
BSONObj(),
{ShardId("0"), ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMinKeyToMaxKey, {}});
}
TEST_F(ChunkManagerQueryTest, EqualityRangeSingleShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -202,11 +194,10 @@ TEST_F(ChunkManagerQueryTest, EqualityRangeSingleShard) {
<< "x"),
BSONObj(),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kSingleKey, {}});
}
TEST_F(ChunkManagerQueryTest, EqualityRangeMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -220,11 +211,10 @@ TEST_F(ChunkManagerQueryTest, EqualityRangeMultiShard) {
<< "y"),
BSONObj(),
{ShardId("2")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kSingleKey, {}});
}
TEST_F(ChunkManagerQueryTest, SetRangeMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -237,11 +227,10 @@ TEST_F(ChunkManagerQueryTest, SetRangeMultiShard) {
fromjson("{a:{$in:['u','y']}}"),
BSONObj(),
{ShardId("0"), ShardId("2")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, GTRangeMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -254,11 +243,10 @@ TEST_F(ChunkManagerQueryTest, GTRangeMultiShard) {
BSON("a" << GT << "x"),
BSONObj(),
{ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, GTERangeMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -271,13 +259,12 @@ TEST_F(ChunkManagerQueryTest, GTERangeMultiShard) {
BSON("a" << GTE << "x"),
BSONObj(),
{ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, LTRangeMultiShard) {
// NOTE (SERVER-4791): It isn't actually necessary to return shard 2 because its lowest key is
// "y", which is excluded from the query
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -290,11 +277,10 @@ TEST_F(ChunkManagerQueryTest, LTRangeMultiShard) {
BSON("a" << LT << "y"),
BSONObj(),
{ShardId("0"), ShardId("1"), ShardId("2")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, LTERangeMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -307,11 +293,10 @@ TEST_F(ChunkManagerQueryTest, LTERangeMultiShard) {
BSON("a" << LTE << "y"),
BSONObj(),
{ShardId("0"), ShardId("1"), ShardId("2")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, OrEqualities) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -324,11 +309,10 @@ TEST_F(ChunkManagerQueryTest, OrEqualities) {
fromjson("{$or:[{a:'u'},{a:'y'}]}"),
BSONObj(),
{ShardId("0"), ShardId("2")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, OrEqualityInequality) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -341,11 +325,10 @@ TEST_F(ChunkManagerQueryTest, OrEqualityInequality) {
fromjson("{$or:[{a:'u'},{a:{$gte:'y'}}]}"),
BSONObj(),
{ShardId("0"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, OrEqualityInequalityUnhelpful) {
- bool expectTargetMinKeyToMaxKey = true;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -358,11 +341,10 @@ TEST_F(ChunkManagerQueryTest, OrEqualityInequalityUnhelpful) {
fromjson("{$or:[{a:'u'},{a:{$gte:'zz'}},{}]}"),
BSONObj(),
{ShardId("0"), ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMinKeyToMaxKey, {}});
}
TEST_F(ChunkManagerQueryTest, UnsatisfiableRangeSingleShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -370,11 +352,10 @@ TEST_F(ChunkManagerQueryTest, UnsatisfiableRangeSingleShard) {
BSON("a" << GT << "x" << LT << "x"),
BSONObj(),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, UnsatisfiableRangeMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -387,11 +368,10 @@ TEST_F(ChunkManagerQueryTest, UnsatisfiableRangeMultiShard) {
BSON("a" << GT << "x" << LT << "x"),
BSONObj(),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, EqualityThenUnsatisfiable) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1 << "b" << 1),
nullptr,
false,
@@ -404,11 +384,10 @@ TEST_F(ChunkManagerQueryTest, EqualityThenUnsatisfiable) {
BSON("a" << 1 << "b" << GT << 4 << LT << 4),
BSONObj(),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, InequalityThenUnsatisfiable) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1 << "b" << 1),
nullptr,
false,
@@ -421,11 +400,10 @@ TEST_F(ChunkManagerQueryTest, InequalityThenUnsatisfiable) {
BSON("a" << GT << 1 << "b" << GT << 4 << LT << 4),
BSONObj(),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, OrEqualityUnsatisfiableInequality) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -438,11 +416,10 @@ TEST_F(ChunkManagerQueryTest, OrEqualityUnsatisfiableInequality) {
fromjson("{$or:[{a:'x'},{a:{$gt:'u',$lt:'u'}},{a:{$gte:'y'}}]}"),
BSONObj(),
{ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, InMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(BSON("a" << 1 << "b" << 1),
nullptr,
false,
@@ -451,11 +428,10 @@ TEST_F(ChunkManagerQueryTest, InMultiShard) {
<< BSON("$in" << BSON_ARRAY(0 << 5 << 25))),
BSONObj(),
{ShardId("0"), ShardId("1"), ShardId("2")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMultipleKeys, {}});
}
TEST_F(ChunkManagerQueryTest, CollationStringsMultiShard) {
- bool expectTargetMinKeyToMaxKey = true;
runQueryTest(BSON("a" << 1),
nullptr,
false,
@@ -470,11 +446,10 @@ TEST_F(ChunkManagerQueryTest, CollationStringsMultiShard) {
BSON("locale"
<< "mock_reverse_string"),
{ShardId("0"), ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMinKeyToMaxKey, {}});
}
TEST_F(ChunkManagerQueryTest, DefaultCollationStringsMultiShard) {
- bool expectTargetMinKeyToMaxKey = true;
runQueryTest(
BSON("a" << 1),
std::make_unique<CollatorInterfaceMock>(CollatorInterfaceMock::MockType::kReverseString),
@@ -490,11 +465,10 @@ TEST_F(ChunkManagerQueryTest, DefaultCollationStringsMultiShard) {
BSON("locale"
<< "mock_reverse_string"),
{ShardId("0"), ShardId("1"), ShardId("2"), ShardId("3")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kMinKeyToMaxKey, {}});
}
TEST_F(ChunkManagerQueryTest, SimpleCollationStringsMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(
BSON("a" << 1),
std::make_unique<CollatorInterfaceMock>(CollatorInterfaceMock::MockType::kReverseString),
@@ -510,11 +484,10 @@ TEST_F(ChunkManagerQueryTest, SimpleCollationStringsMultiShard) {
BSON("locale"
<< "simple"),
{ShardId("2")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kSingleKey, {}});
}
TEST_F(ChunkManagerQueryTest, CollationNumbersMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(
BSON("a" << 1),
std::make_unique<CollatorInterfaceMock>(CollatorInterfaceMock::MockType::kReverseString),
@@ -529,11 +502,10 @@ TEST_F(ChunkManagerQueryTest, CollationNumbersMultiShard) {
BSON("locale"
<< "mock_reverse_string"),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kSingleKey, {}});
}
TEST_F(ChunkManagerQueryTest, DefaultCollationNumbersMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(
BSON("a" << 1),
std::make_unique<CollatorInterfaceMock>(CollatorInterfaceMock::MockType::kReverseString),
@@ -547,11 +519,10 @@ TEST_F(ChunkManagerQueryTest, DefaultCollationNumbersMultiShard) {
BSON("a" << 5),
BSONObj(),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kSingleKey, {}});
}
TEST_F(ChunkManagerQueryTest, SimpleCollationNumbersMultiShard) {
- bool expectTargetMinKeyToMaxKey = false;
runQueryTest(
BSON("a" << 1),
std::make_unique<CollatorInterfaceMock>(CollatorInterfaceMock::MockType::kReverseString),
@@ -566,7 +537,7 @@ TEST_F(ChunkManagerQueryTest, SimpleCollationNumbersMultiShard) {
BSON("locale"
<< "simple"),
{ShardId("0")},
- expectTargetMinKeyToMaxKey);
+ {QueryTargetingInfo::Description::kSingleKey, {}});
}
TEST_F(ChunkManagerQueryTest, SnapshotQueryWithMoreShardsThanLatestMetadata) {
@@ -615,13 +586,8 @@ TEST_F(ChunkManagerQueryTest, SnapshotQueryWithMoreShardsThanLatestMetadata) {
const auto expCtx = make_intrusive<ExpressionContextForTest>();
shardIds.clear();
- getShardIdsForQuery(expCtx,
- BSON("x" << BSON("$gt" << -20)),
- {},
- chunkManager,
- &shardIds,
- nullptr /* chunkRanges */,
- nullptr /* targetMinKeyToMaxKey */);
+ getShardIdsForQuery(
+ expCtx, BSON("x" << BSON("$gt" << -20)), {}, chunkManager, &shardIds, nullptr /* info */);
ASSERT_EQ(2, shardIds.size());
}
diff --git a/src/mongo/s/cluster_commands_helpers.cpp b/src/mongo/s/cluster_commands_helpers.cpp
index 11c2e3b68d5..345c4eae3f7 100644
--- a/src/mongo/s/cluster_commands_helpers.cpp
+++ b/src/mongo/s/cluster_commands_helpers.cpp
@@ -181,13 +181,7 @@ std::vector<AsyncRequestsSender::Request> buildVersionedRequestsForTargetedShard
}
auto expCtx = make_intrusive<ExpressionContext>(opCtx, std::move(collator), nss);
- getShardIdsForQuery(expCtx,
- query,
- collation,
- cm,
- &shardIds,
- nullptr /* chunkRanges */,
- nullptr /* targetMinKeyToMaxKey */);
+ getShardIdsForQuery(expCtx, query, collation, cm, &shardIds, nullptr /* info */);
const auto targetedSampleId = eligibleForSampling
? analyze_shard_key::tryGenerateTargetedSampleId(
@@ -662,13 +656,7 @@ std::set<ShardId> getTargetedShardsForQuery(boost::intrusive_ptr<ExpressionConte
// The collection is sharded. Use the routing table to decide which shards to target based
// on the query and collation.
std::set<ShardId> shardIds;
- getShardIdsForQuery(expCtx,
- query,
- collation,
- cm,
- &shardIds,
- nullptr /* chunkRanges */,
- nullptr /* targetMinKeyToMaxKey */);
+ getShardIdsForQuery(expCtx, query, collation, cm, &shardIds, nullptr /* info */);
return shardIds;
}
diff --git a/src/mongo/s/collection_routing_info_targeter.cpp b/src/mongo/s/collection_routing_info_targeter.cpp
index 4204e355da8..af6713a6e5e 100644
--- a/src/mongo/s/collection_routing_info_targeter.cpp
+++ b/src/mongo/s/collection_routing_info_targeter.cpp
@@ -68,6 +68,7 @@ constexpr auto kIdFieldName = "_id"_sd;
const ShardKeyPattern kVirtualIdShardKey(BSON(kIdFieldName << 1));
using UpdateType = write_ops::UpdateModification::Type;
+using shard_key_pattern_query_util::QueryTargetingInfo;
// Tracks the number of {multi:false} updates with an exact match on _id that are broadcasted to
// multiple shards.
@@ -667,8 +668,12 @@ StatusWith<std::vector<ShardEndpoint>> CollectionRoutingInfoTargeter::_targetQue
}
std::set<ShardId> shardIds;
+ QueryTargetingInfo info;
try {
- getShardIdsForQuery(expCtx, query, collation, _cri.cm, &shardIds, chunkRanges);
+ getShardIdsForQuery(expCtx, query, collation, _cri.cm, &shardIds, &info);
+ if (chunkRanges) {
+ chunkRanges->swap(info.chunkRanges);
+ }
} catch (const DBException& ex) {
return ex.toStatus();
}
diff --git a/src/mongo/s/commands/cluster_query_without_shard_key_cmd.cpp b/src/mongo/s/commands/cluster_query_without_shard_key_cmd.cpp
index b4af18f9f74..65bdc43f1d3 100644
--- a/src/mongo/s/commands/cluster_query_without_shard_key_cmd.cpp
+++ b/src/mongo/s/commands/cluster_query_without_shard_key_cmd.cpp
@@ -89,13 +89,8 @@ std::set<ShardId> getShardsToTarget(OperationContext* opCtx,
CollatorFactoryInterface::get(opCtx->getServiceContext())->makeFromBSON(collation));
}
auto expCtx = make_intrusive<ExpressionContext>(opCtx, std::move(collator), nss);
- getShardIdsForQuery(expCtx,
- query,
- collation,
- cm,
- &allShardsContainingChunksForNs,
- nullptr /* chunkRanges */,
- nullptr /* targetMinKeyToMaxKey */);
+ getShardIdsForQuery(
+ expCtx, query, collation, cm, &allShardsContainingChunksForNs, nullptr /* info */);
// We must either get a subset of shards to target in the case of a partial shard key or we must
// target all shards.
diff --git a/src/mongo/s/shard_key_pattern_query_util.cpp b/src/mongo/s/shard_key_pattern_query_util.cpp
index 227a53bc4cc..37d27d23e10 100644
--- a/src/mongo/s/shard_key_pattern_query_util.cpp
+++ b/src/mongo/s/shard_key_pattern_query_util.cpp
@@ -44,6 +44,7 @@ namespace mongo {
namespace {
using pathsupport::EqualityMatches;
+using shard_key_pattern_query_util::QueryTargetingInfo;
// Maximum number of intervals produced by $in queries
constexpr size_t kMaxFlattenedInCombinations = 4000000;
@@ -404,10 +405,9 @@ void getShardIdsForQuery(boost::intrusive_ptr<ExpressionContext> expCtx,
const BSONObj& collation,
const ChunkManager& cm,
std::set<ShardId>* shardIds,
- std::set<ChunkRange>* chunkRanges,
- bool* targetMinKeyToMaxKey) {
- if (chunkRanges) {
- invariant(chunkRanges->empty());
+ QueryTargetingInfo* info) {
+ if (info) {
+ invariant(info->chunkRanges.empty());
}
auto findCommand = std::make_unique<FindCommandRequest>(cm.getNss());
@@ -437,11 +437,9 @@ void getShardIdsForQuery(boost::intrusive_ptr<ExpressionContext> expCtx,
try {
auto chunk = cm.findIntersectingChunk(shardKeyToFind, collation);
shardIds->insert(chunk.getShardId());
- if (chunkRanges) {
- chunkRanges->insert(chunk.getRange());
- }
- if (targetMinKeyToMaxKey) {
- *targetMinKeyToMaxKey = false;
+ if (info) {
+ info->desc = QueryTargetingInfo::Description::kSingleKey;
+ info->chunkRanges.insert(chunk.getRange());
}
return;
} catch (const DBException&) {
@@ -468,12 +466,7 @@ void getShardIdsForQuery(boost::intrusive_ptr<ExpressionContext> expCtx,
const auto& min = it->first;
const auto& max = it->second;
- cm.getShardIdsForRange(min, max, shardIds, chunkRanges);
-
- if (targetMinKeyToMaxKey && ChunkMap::allElementsAreOfType(MinKey, min) &&
- ChunkMap::allElementsAreOfType(MaxKey, max)) {
- *targetMinKeyToMaxKey = true;
- }
+ cm.getShardIdsForRange(min, max, shardIds, info ? &info->chunkRanges : nullptr);
// Once we know we need to visit all shards no need to keep looping.
// However, this optimization does not apply when we are reading from a snapshot
@@ -491,15 +484,29 @@ void getShardIdsForQuery(boost::intrusive_ptr<ExpressionContext> expCtx,
if (shardIds->empty()) {
cm.forEachChunk([&](const Chunk& chunk) {
shardIds->insert(chunk.getShardId());
- if (chunkRanges) {
- chunkRanges->insert(chunk.getRange());
- }
- if (targetMinKeyToMaxKey) {
- *targetMinKeyToMaxKey = false;
+ if (info) {
+ info->chunkRanges.insert(chunk.getRange());
}
return false;
});
}
+
+ if (info) {
+ info->desc = [&] {
+ if (ranges.size() == 1) {
+ auto min = ranges.begin()->first;
+ auto max = ranges.begin()->second;
+ if (SimpleBSONObjComparator::kInstance.evaluate(min == max)) {
+ return QueryTargetingInfo::Description::kSingleKey;
+ }
+ if (ChunkMap::allElementsAreOfType(MinKey, min) &&
+ ChunkMap::allElementsAreOfType(MaxKey, max)) {
+ return QueryTargetingInfo::Description::kMinKeyToMaxKey;
+ }
+ }
+ return QueryTargetingInfo::Description::kMultipleKeys;
+ }();
+ }
}
} // namespace mongo
diff --git a/src/mongo/s/shard_key_pattern_query_util.h b/src/mongo/s/shard_key_pattern_query_util.h
index 07c3757370c..5696f444e20 100644
--- a/src/mongo/s/shard_key_pattern_query_util.h
+++ b/src/mongo/s/shard_key_pattern_query_util.h
@@ -108,20 +108,29 @@ BoundList flattenBounds(const ShardKeyPattern& shardKeyPattern, const IndexBound
*/
IndexBounds getIndexBoundsForQuery(const BSONObj& key, const CanonicalQuery& canonicalQuery);
+namespace shard_key_pattern_query_util {
+
+struct QueryTargetingInfo {
+ enum Description { kSingleKey, kMultipleKeys, kMinKeyToMaxKey };
+
+ Description desc;
+ std::set<ChunkRange> chunkRanges;
+};
+
+} // namespace shard_key_pattern_query_util
+
/**
- * Finds the shard IDs for a given filter and collation. If collation is empty, we use the
- * collection default collation for targeting.
- *
- * If 'chunkRanges' is not null, populates it with ChunkRanges that would be targeted by the query.
- * If 'targetMinKeyToMaxKey' is not null, sets it to true if the query targets the entire shard key
- * space.
+ * Populates 'shardIds' with the shard ids for a query with given filter and collation. If the
+ * collation is empty, it uses the collection default collation for targeting. If 'info' is not
+ * null, populates it with the ChunkRanges that the query targets and a description about whether
+ * the query targets a single shard key value, multiple but not all shard key values or all shard
+ * key values.
*/
void getShardIdsForQuery(boost::intrusive_ptr<ExpressionContext> expCtx,
const BSONObj& query,
const BSONObj& collation,
const ChunkManager& cm,
std::set<ShardId>* shardIds,
- std::set<ChunkRange>* chunkRanges = nullptr,
- bool* targetMinKeyToMaxKey = nullptr);
+ shard_key_pattern_query_util::QueryTargetingInfo* info = nullptr);
} // namespace mongo