diff options
Diffstat (limited to 'src/mongo/db/pipeline/sharded_agg_helpers.cpp')
| -rw-r--r-- | src/mongo/db/pipeline/sharded_agg_helpers.cpp | 64 |
1 files changed, 37 insertions, 27 deletions
diff --git a/src/mongo/db/pipeline/sharded_agg_helpers.cpp b/src/mongo/db/pipeline/sharded_agg_helpers.cpp index 9a7b4df2242..42e8ae24d32 100644 --- a/src/mongo/db/pipeline/sharded_agg_helpers.cpp +++ b/src/mongo/db/pipeline/sharded_agg_helpers.cpp @@ -61,7 +61,7 @@ #include "mongo/s/query/cluster_query_knobs_gen.h" #include "mongo/s/query/document_source_merge_cursors.h" #include "mongo/s/query/establish_cursors.h" -#include "mongo/s/router.h" +#include "mongo/s/router_role.h" #include "mongo/s/stale_exception.h" #include "mongo/s/transaction_router.h" #include "mongo/util/fail_point.h" @@ -414,7 +414,7 @@ void moveEligibleStreamingStagesBeforeSortOnShards(Pipeline* shardPipe, // Expected last stage on the shards to be a $sort. return; } - auto sortPaths = sortPattern.getFieldNames<std::set<std::string>>(); + auto sortPaths = sortPattern.getFieldNames<OrderedPathSet>(); auto firstMergeStage = mergePipe->getSources().cbegin(); std::function<bool(DocumentSource*)> distributedPlanLogicCallback = [](DocumentSource* stage) { return !static_cast<bool>(stage->distributedPlanLogic()); @@ -561,7 +561,7 @@ void limitFieldsSentFromShardsToMerger(Pipeline* shardPipe, Pipeline* mergePipe) } bool stageCanRunInParallel(const boost::intrusive_ptr<DocumentSource>& stage, - const std::set<std::string>& nameOfShardKeyFieldsUponEntryToStage) { + const OrderedPathSet& nameOfShardKeyFieldsUponEntryToStage) { if (stage->distributedPlanLogic()) { return stage->canRunInParallelBeforeWriteStage(nameOfShardKeyFieldsUponEntryToStage); } else { @@ -598,7 +598,7 @@ BSONObj buildNewKeyPattern(const ShardKeyPattern& shardKey, StringMap<std::strin } StringMap<std::string> computeShardKeyRenameMap(const Pipeline* mergePipeline, - std::set<std::string>&& pathsOfShardKey) { + OrderedPathSet&& pathsOfShardKey) { auto traversalStart = mergePipeline->getSources().crbegin(); auto traversalEnd = mergePipeline->getSources().crend(); const auto leadingGroup = @@ -626,7 +626,7 @@ StringMap<std::string> computeShardKeyRenameMap(const Pipeline* mergePipeline, * * Purposefully takes 'shardKeyPaths' by value so that it can be modified throughout. */ -bool anyStageModifiesShardKeyOrNeedsMerge(std::set<std::string> shardKeyPaths, +bool anyStageModifiesShardKeyOrNeedsMerge(OrderedPathSet shardKeyPaths, const Pipeline* mergePipeline) { const auto& stages = mergePipeline->getSources(); for (auto it = stages.crbegin(); it != stages.crend(); ++it) { @@ -653,7 +653,7 @@ boost::optional<ShardedExchangePolicy> walkPipelineBackwardsTrackingShardKey( OperationContext* opCtx, const Pipeline* mergePipeline, const ChunkManager& chunkManager) { const ShardKeyPattern& shardKey = chunkManager.getShardKeyPattern(); - std::set<std::string> shardKeyPaths; + OrderedPathSet shardKeyPaths; for (auto&& path : shardKey.getKeyPatternFields()) { shardKeyPaths.emplace(path->dottedField().toString()); } @@ -769,10 +769,16 @@ std::unique_ptr<Pipeline, PipelineDeleter> targetShardsAndAddMergeCursors( LiteParsedPipeline liteParsedPipeline(aggRequest); auto hasChangeStream = liteParsedPipeline.hasChangeStream(); + auto startsWithDocuments = liteParsedPipeline.startsWithDocuments(); auto shardDispatchResults = dispatchShardPipeline(aggregation_request_helper::serializeToCommandDoc(aggRequest), hasChangeStream, + startsWithDocuments, std::move(pipeline), + // Even if the overall operation is an explain, callers of this + // function always intend to actually execute a regular agg command + // and merge the results with $mergeCursors. + boost::none /*explain*/, shardTargetingPolicy, std::move(readConcern)); @@ -955,6 +961,7 @@ BSONObj createCommandForTargetedShards(const boost::intrusive_ptr<ExpressionCont const SplitPipeline& splitPipeline, const boost::optional<ShardedExchangePolicy> exchangeSpec, bool needsMerge, + boost::optional<ExplainOptions::Verbosity> explain, boost::optional<BSONObj> readConcern) { // Create the command for the shards. MutableDocument targetedCmd(serializedCommand); @@ -986,28 +993,22 @@ BSONObj createCommandForTargetedShards(const boost::intrusive_ptr<ExpressionCont targetedCmd[AggregateCommandRequest::kExchangeFieldName] = exchangeSpec ? Value(exchangeSpec->exchangeSpec.toBSON()) : Value(); - auto shardCommand = genericTransformForShards(std::move(targetedCmd), - expCtx, - expCtx->explain, - expCtx->getCollatorBSON(), - std::move(readConcern)); + auto shardCommand = genericTransformForShards( + std::move(targetedCmd), expCtx, explain, expCtx->getCollatorBSON(), std::move(readConcern)); // Apply RW concern to the final shard command. return applyReadWriteConcern(expCtx->opCtx, - true, /* appendRC */ - !expCtx->explain, /* appendWC */ + true, /* appendRC */ + !explain, /* appendWC */ shardCommand); } -/** - * Targets shards for the pipeline and returns a struct with the remote cursors or results, and - * the pipeline that will need to be executed to merge the results from the remotes. If a stale - * shard version is encountered, refreshes the routing table and tries again. - */ DispatchShardPipelineResults dispatchShardPipeline( Document serializedCommand, bool hasChangeStream, + bool startsWithDocuments, std::unique_ptr<Pipeline, PipelineDeleter> pipeline, + boost::optional<ExplainOptions::Verbosity> explain, ShardTargetingPolicy shardTargetingPolicy, boost::optional<BSONObj> readConcern) { auto expCtx = pipeline->getContext(); @@ -1051,7 +1052,7 @@ DispatchShardPipelineResults dispatchShardPipeline( : expCtx->getCollatorBSON(); // Determine whether we can run the entire aggregation on a single shard. - const bool mustRunOnAll = mustRunOnAllShards(expCtx->ns, hasChangeStream); + const bool mustRunOnAll = mustRunOnAllShards(expCtx->ns, hasChangeStream, startsWithDocuments); std::set<ShardId> shardIds = getTargetedShards( expCtx, mustRunOnAll, executionNsRoutingInfo, shardQuery, shardTargetingCollation); @@ -1103,10 +1104,11 @@ DispatchShardPipelineResults dispatchShardPipeline( *splitPipelines, exchangeSpec, true /* needsMerge */, + explain, std::move(readConcern)) : createPassthroughCommandForShard(expCtx, serializedCommand, - expCtx->explain, + explain, pipeline.get(), expCtx->getCollatorBSON(), std::move(readConcern), @@ -1139,7 +1141,7 @@ DispatchShardPipelineResults dispatchShardPipeline( shardIds.size() > 0); // Explain does not produce a cursor, so instead we scatter-gather commands to the shards. - if (expCtx->explain) { + if (explain) { if (mustRunOnAll) { // Some stages (such as $currentOp) need to be broadcast to all shards, and // should not participate in the shard version protocol. @@ -1154,7 +1156,7 @@ DispatchShardPipelineResults dispatchShardPipeline( // shards, and should participate in the shard version protocol. invariant(executionNsRoutingInfo); shardResults = - scatterGatherVersionedTargetByRoutingTable(opCtx, + scatterGatherVersionedTargetByRoutingTable(expCtx, expCtx->ns.db(), expCtx->ns, *executionNsRoutingInfo, @@ -1175,7 +1177,7 @@ DispatchShardPipelineResults dispatchShardPipeline( targetedCommand, ReadPreferenceSetting::get(opCtx)); - } catch (const StaleConfigException& e) { + } catch (const ExceptionFor<ErrorCodes::StaleConfig>& e) { // Check to see if the command failed because of a stale shard version or something // else. auto staleInfo = e.extraInfo<StaleConfigInfo>(); @@ -1486,10 +1488,13 @@ BSONObj targetShardsForExplain(Pipeline* ownedPipeline) { AggregateCommandRequest aggRequest(expCtx->ns, rawStages); LiteParsedPipeline liteParsedPipeline(aggRequest); auto hasChangeStream = liteParsedPipeline.hasChangeStream(); + auto startsWithDocuments = liteParsedPipeline.startsWithDocuments(); auto shardDispatchResults = dispatchShardPipeline(aggregation_request_helper::serializeToCommandDoc(aggRequest), hasChangeStream, - std::move(pipeline)); + startsWithDocuments, + std::move(pipeline), + expCtx->explain); BSONObjBuilder explainBuilder; auto appendStatus = appendExplainResults(std::move(shardDispatchResults), expCtx, &explainBuilder); @@ -1523,11 +1528,13 @@ Shard::RetryPolicy getDesiredRetryPolicy(OperationContext* opCtx) { return Shard::RetryPolicy::kIdempotent; } -bool mustRunOnAllShards(const NamespaceString& nss, bool hasChangeStream) { +bool mustRunOnAllShards(const NamespaceString& nss, + bool hasChangeStream, + bool startsWithDocuments) { // The following aggregations must be routed to all shards: // - Any collectionless aggregation, such as non-localOps $currentOp. // - Any aggregation which begins with a $changeStream stage. - return nss.isCollectionlessAggregateNS() || hasChangeStream; + return !startsWithDocuments && (nss.isCollectionlessAggregateNS() || hasChangeStream); } std::unique_ptr<Pipeline, PipelineDeleter> attachCursorToPipeline( @@ -1583,11 +1590,14 @@ std::unique_ptr<Pipeline, PipelineDeleter> attachCursorToPipeline( [&](OperationContext* opCtx, const ChunkManager& cm) { auto pipelineToTarget = pipeline->clone(); - if (!cm.isSharded()) { + if (!cm.isSharded() && expCtx->ns != NamespaceString::kConfigsvrCollectionsNamespace) { // If the collection is unsharded and we are on the primary, we should be able to // do a local read. The primary may be moved right after the primary shard check, // but the local read path will do a db version check before it establishes a cursor // to catch this case and ensure we fail to read locally. + // There is the case where we are in config.collections (collection unsharded) and + // we want to broadcast to all shards. In this case we don't want to do a local read + // and we must target the config servers. try { auto expectUnshardedCollection( expCtx->mongoProcessInterface->expectUnshardedCollectionInScope( |
