/// <summary> /// Initializes a new instance of the CosmosCrossPartitionQueryExecutionContext class. /// </summary> /// <param name="initParams">Constructor parameters for the base class.</param> /// <param name="moveNextComparer">Comparer used to figure out that document producer tree to serve documents from next.</param> /// <param name="fetchPrioirtyFunction">The priority function to determine which partition to fetch documents from next.</param> /// <param name="equalityComparer">Used to determine whether we need to return the continuation token for a partition.</param> protected CosmosCrossPartitionQueryExecutionContext( CosmosQueryContext initParams, IComparer <ItemProducerTree> moveNextComparer, Func <ItemProducerTree, int> fetchPrioirtyFunction, IEqualityComparer <CosmosElement> equalityComparer) { if (moveNextComparer == null) { throw new ArgumentNullException(nameof(moveNextComparer)); } if (fetchPrioirtyFunction == null) { throw new ArgumentNullException(nameof(fetchPrioirtyFunction)); } if (equalityComparer == null) { throw new ArgumentNullException(nameof(equalityComparer)); } this.queryContext = initParams; this.queryRequestOptions = initParams.QueryRequestOptions; this.itemProducerForest = new PriorityQueue <ItemProducerTree>(moveNextComparer, isSynchronized: true); this.fetchPrioirtyFunction = fetchPrioirtyFunction; this.comparableTaskScheduler = new ComparableTaskScheduler(initParams.QueryRequestOptions.MaxConcurrency.GetValueOrDefault(0)); this.equalityComparer = equalityComparer; this.requestChargeTracker = new RequestChargeTracker(); this.partitionedQueryMetrics = new ConcurrentBag <Tuple <string, QueryMetrics> >(); this.actualMaxPageSize = this.queryRequestOptions.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxPageSize < 0) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be greater than int.MaxValue"); } if (this.queryRequestOptions.MaxBufferedItemCount.HasValue) { this.actualMaxBufferedItemCount = this.queryRequestOptions.MaxBufferedItemCount.Value; } else { this.actualMaxBufferedItemCount = ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize; } if (this.actualMaxBufferedItemCount < 0) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } }
/// <summary> /// Initializes a new instance of the CosmosCrossPartitionQueryExecutionContext class. /// </summary> /// <param name="queryContext">Constructor parameters for the base class.</param> /// <param name="maxConcurrency">The max concurrency</param> /// <param name="maxBufferedItemCount">The max buffered item count</param> /// <param name="maxItemCount">Max item count</param> /// <param name="moveNextComparer">Comparer used to figure out that document producer tree to serve documents from next.</param> /// <param name="fetchPrioirtyFunction">The priority function to determine which partition to fetch documents from next.</param> /// <param name="equalityComparer">Used to determine whether we need to return the continuation token for a partition.</param> /// <param name="returnResultsInDeterministicOrder">Whether or not to return results in deterministic order.</param> /// <param name="testSettings">Test settings.</param> protected CosmosCrossPartitionQueryExecutionContext( CosmosQueryContext queryContext, int?maxConcurrency, int?maxItemCount, int?maxBufferedItemCount, IComparer <ItemProducerTree> moveNextComparer, Func <ItemProducerTree, int> fetchPrioirtyFunction, IEqualityComparer <CosmosElement> equalityComparer, bool returnResultsInDeterministicOrder, TestInjections testSettings) { if (moveNextComparer == null) { throw new ArgumentNullException(nameof(moveNextComparer)); } this.queryContext = queryContext ?? throw new ArgumentNullException(nameof(queryContext)); this.queryClient = queryContext.QueryClient ?? throw new ArgumentNullException(nameof(queryContext.QueryClient)); this.itemProducerForest = new PriorityQueue <ItemProducerTree>(moveNextComparer, isSynchronized: true); this.fetchPrioirtyFunction = fetchPrioirtyFunction ?? throw new ArgumentNullException(nameof(fetchPrioirtyFunction)); this.comparableTaskScheduler = new ComparableTaskScheduler(maxConcurrency.GetValueOrDefault(0)); this.equalityComparer = equalityComparer ?? throw new ArgumentNullException(nameof(equalityComparer)); this.testSettings = testSettings; this.requestChargeTracker = new RequestChargeTracker(); this.diagnosticsPages = new ConcurrentBag <QueryPageDiagnostics>(); this.actualMaxPageSize = maxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxPageSize < 0) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxPageSize should never be greater than int.MaxValue"); } if (maxBufferedItemCount.HasValue) { this.actualMaxBufferedItemCount = maxBufferedItemCount.Value; } else { this.actualMaxBufferedItemCount = ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize; } if (this.actualMaxBufferedItemCount < 0) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new ArgumentOutOfRangeException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } this.CanPrefetch = maxConcurrency.HasValue && maxConcurrency.Value != 0; this.returnResultsInDeterministicOrder = returnResultsInDeterministicOrder; }
/// <summary> /// This function decides the maximum number of concurrent operations. /// 1. (feedOptions.MaximumDegreeOfParallelism == -1 or less) => Automatic /// 2. (feedOptions.MaximumDegreeOfParallelism == 0 ) => No parallel execution, serial (current implementation). /// The code should not come here. DefaultExecutor instead of Parallel executor will be executed. /// 3. (feedOptions.MaximumDegreeOfParallelism > 0 ) => Parallel with a max of specified number of tasks /// </summary> /// <param name="currentRunningTaskCount"> /// Current number of running tasks /// </param> /// <returns> /// Returns the number of tasks to run /// </returns> private int GetCurrentMaximumAllowedConcurrentTasks(int currentRunningTaskCount) { if (this.MaxDegreeOfParallelism >= 1) { return(this.MaxDegreeOfParallelism); } if (this.MaxDegreeOfParallelism == 0) { return(1); } if (currentRunningTaskCount <= 0) { return(ParallelQueryConfig.GetConfig().AutoModeTasksIncrementFactor); } double currentAverage = (double)Interlocked.Read(ref this.totalRequestRoundTrips) / currentRunningTaskCount; if (currentAverage > this.currentAverageNumberOfRequestsPerTask) { currentRunningTaskCount *= ParallelQueryConfig.GetConfig().AutoModeTasksIncrementFactor; } this.currentAverageNumberOfRequestsPerTask = currentAverage; return(Math.Max(currentRunningTaskCount, Math.Max(2, Environment.ProcessorCount * ParallelQueryConfig.GetConfig().NumberOfNetworkCallsPerProcessor))); }
public static async Task <CosmosQueryExecutionContext> CreateSpecializedDocumentQueryExecutionContextAsync( CosmosQueryContext cosmosQueryContext, PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, List <PartitionKeyRange> targetRanges, string collectionRid, CancellationToken cancellationToken) { if (!string.IsNullOrEmpty(partitionedQueryExecutionInfo.QueryInfo?.RewrittenQuery)) { cosmosQueryContext.SqlQuerySpec = new SqlQuerySpec( partitionedQueryExecutionInfo.QueryInfo.RewrittenQuery, cosmosQueryContext.SqlQuerySpec.Parameters); } // Figure out the optimal page size. long initialPageSize = cosmosQueryContext.QueryRequestOptions.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalPageSize); if (initialPageSize < -1 || initialPageSize == 0) { throw new BadRequestException(string.Format(CultureInfo.InvariantCulture, "Invalid MaxItemCount {0}", initialPageSize)); } QueryInfo queryInfo = partitionedQueryExecutionInfo.QueryInfo; bool getLazyFeedResponse = queryInfo.HasTop; // We need to compute the optimal initial page size for order-by queries if (queryInfo.HasOrderBy) { int top; if (queryInfo.HasTop && (top = partitionedQueryExecutionInfo.QueryInfo.Top.Value) > 0) { // All partitions should initially fetch about 1/nth of the top value. long pageSizeWithTop = (long)Math.Min( Math.Ceiling(top / (double)targetRanges.Count) * PageSizeFactorForTop, top); if (initialPageSize > 0) { initialPageSize = Math.Min(pageSizeWithTop, initialPageSize); } else { initialPageSize = pageSizeWithTop; } } else if (cosmosQueryContext.IsContinuationExpected) { if (initialPageSize < 0) { if (cosmosQueryContext.QueryRequestOptions.MaxBufferedItemCount.HasValue) { // Max of what the user is willing to buffer and the default (note this is broken if MaxBufferedItemCount = -1) initialPageSize = Math.Max(cosmosQueryContext.QueryRequestOptions.MaxBufferedItemCount.Value, ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize); } else { initialPageSize = ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize; } } initialPageSize = (long)Math.Min( Math.Ceiling(initialPageSize / (double)targetRanges.Count) * PageSizeFactorForTop, initialPageSize); } } Debug.Assert(initialPageSize > 0 && initialPageSize <= int.MaxValue, string.Format(CultureInfo.InvariantCulture, "Invalid MaxItemCount {0}", initialPageSize)); return(await PipelinedDocumentQueryExecutionContext.CreateAsync( cosmosQueryContext, collectionRid, partitionedQueryExecutionInfo, targetRanges, (int)initialPageSize, cosmosQueryContext.QueryRequestOptions.RequestContinuation, cancellationToken)); }
/// <summary> /// Initializes a new instance of the CrossPartitionQueryExecutionContext class. /// </summary> /// <param name="initParams">Constructor parameters for the base class.</param> /// <param name="rewrittenQuery"> /// Queries will get rewritten for different reasons. /// You can read more about this in the details from the concrete classes. /// </param> /// <param name="moveNextComparer">Comparer used to figure out that document producer tree to serve documents from next.</param> /// <param name="fetchPrioirtyFunction">The priority function to determine which partition to fetch documents from next.</param> /// <param name="equalityComparer">Used to determine whether we need to return the continuation token for a partition.</param> protected CrossPartitionQueryExecutionContext( DocumentQueryExecutionContextBase.InitParams initParams, string rewrittenQuery, IComparer <DocumentProducerTree> moveNextComparer, Func <DocumentProducerTree, int> fetchPrioirtyFunction, IEqualityComparer <CosmosElement> equalityComparer) : base(initParams) { if (!string.IsNullOrWhiteSpace(rewrittenQuery)) { this.querySpec = new SqlQuerySpec(rewrittenQuery, this.QuerySpec.Parameters); } if (moveNextComparer == null) { throw new ArgumentNullException($"{nameof(moveNextComparer)} can not be null"); } if (fetchPrioirtyFunction == null) { throw new ArgumentNullException($"{nameof(fetchPrioirtyFunction)} can not be null"); } if (equalityComparer == null) { throw new ArgumentNullException($"{nameof(equalityComparer)} can not be null"); } this.documentProducerForest = new PriorityQueue <DocumentProducerTree>(moveNextComparer, isSynchronized: true); this.fetchPrioirtyFunction = fetchPrioirtyFunction; this.comparableTaskScheduler = new ComparableTaskScheduler(initParams.FeedOptions.MaxDegreeOfParallelism); this.equalityComparer = equalityComparer; this.requestChargeTracker = new RequestChargeTracker(); this.partitionedQueryMetrics = new ConcurrentBag <Tuple <string, QueryMetrics> >(); this.actualMaxPageSize = this.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxPageSize < 0) { throw new OverflowException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new OverflowException("actualMaxPageSize should never be greater than int.MaxValue"); } if (IsMaxBufferedItemCountSet(this.MaxBufferedItemCount)) { this.actualMaxBufferedItemCount = this.MaxBufferedItemCount; } else { this.actualMaxBufferedItemCount = ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize; } if (this.actualMaxBufferedItemCount < 0) { throw new OverflowException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new OverflowException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } }
public static async Task <PipelinedDocumentQueryExecutionContext> CreateAsync( IDocumentQueryClient client, ResourceType resourceTypeEnum, Type resourceType, Expression expression, FeedOptions feedOptions, string resourceLink, string collectionRid, PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, List <PartitionKeyRange> targetRanges, int initialPageSize, bool isContinuationExpected, bool getLazyFeedResponse, CancellationToken token, Guid correlatedActivityId) { DefaultTrace.TraceInformation( string.Format( CultureInfo.InvariantCulture, "{0}, CorrelatedActivityId: {1} | Pipelined~Context.CreateAsync", DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture), correlatedActivityId)); Func <string, Task <IDocumentQueryExecutionComponent> > createComponentFunc; QueryInfo queryInfo = partitionedQueryExecutionInfo.QueryInfo; if (queryInfo.HasOrderBy) { createComponentFunc = async(requestContinuation) => { return(await OrderByDocumentQueryExecutionContext.CreateAsync( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, collectionRid, partitionedQueryExecutionInfo, targetRanges, initialPageSize, isContinuationExpected, getLazyFeedResponse, requestContinuation, token, correlatedActivityId)); }; } else { createComponentFunc = async(requestContinuation) => { return(await ParallelDocumentQueryExecutionContext.CreateAsync( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, collectionRid, partitionedQueryExecutionInfo, targetRanges, initialPageSize, isContinuationExpected, getLazyFeedResponse, requestContinuation, token, correlatedActivityId)); }; } if (queryInfo.HasAggregates) { Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(requestContinuation) => { return(await AggregateDocumentQueryExecutionComponent.CreateAsync( queryInfo.Aggregates, requestContinuation, createSourceCallback)); }; } if (queryInfo.HasDistinct) { Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(requestContinuation) => { return(await DistinctDocumentQueryExecutionComponent.CreateAsync( requestContinuation, createSourceCallback, queryInfo.DistinctType)); }; } if (queryInfo.HasTop) { Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(requestContinuation) => { return(await TopDocumentQueryExecutionComponent.CreateAsync( queryInfo.Top.Value, requestContinuation, createSourceCallback)); }; } int actualPageSize = feedOptions.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalPageSize); // If this contract changes, make the corresponding change in MongoDocumentClient.QueryInternalAsync if (actualPageSize == -1) { actualPageSize = int.MaxValue; } return(new PipelinedDocumentQueryExecutionContext( await createComponentFunc(feedOptions.RequestContinuation), Math.Min(actualPageSize, queryInfo.Top.GetValueOrDefault(actualPageSize)), correlatedActivityId)); }
protected ParallelDocumentQueryExecutionContextBase( IDocumentQueryClient client, ResourceType resourceTypeEnum, Type resourceType, Expression expression, FeedOptions feedOptions, string resourceLink, string rewrittenQuery, Guid correlatedActivityId, bool isContinuationExpected, bool getLazyFeedResponse, bool isDynamicPageSizeAllowed) : base( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, getLazyFeedResponse, correlatedActivityId) { this.DocumentProducers = new List <DocumentProducer <T> >(); this.chargeTracker = new RequestChargeTracker(); this.groupedQueryMetrics = new Dictionary <string, QueryMetrics>(); this.partitionedQueryMetrics = new ConcurrentBag <Tuple <string, QueryMetrics> >(); this.responseHeaders = new StringKeyValueCollection(); this.actualMaxBufferedItemCount = Math.Max(this.MaxBufferedItemCount, ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize); this.currentAverageNumberOfRequestsPerTask = 1d; if (!string.IsNullOrEmpty(rewrittenQuery)) { this.querySpec = new SqlQuerySpec(rewrittenQuery, this.QuerySpec.Parameters); } this.TaskScheduler = new ComparableTaskScheduler(this.GetCurrentMaximumAllowedConcurrentTasks(0)); this.ShouldPrefetch = feedOptions.MaxDegreeOfParallelism != 0; this.IsContinuationExpected = isContinuationExpected; this.DefaultContinuationToken = Guid.NewGuid().ToString(); this.InitializationSchedulingMetrics = new SchedulingStopwatch(); this.InitializationSchedulingMetrics.Ready(); this.CurrentContinuationTokens = new SortedList <DocumentProducer <T>, string>( Comparer <DocumentProducer <T> > .Create((producer1, producer2) => string.CompareOrdinal(producer1.TargetRange.MinInclusive, producer2.TargetRange.MinInclusive))); this.actualMaxPageSize = this.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalMaxItemCount); if (this.actualMaxBufferedItemCount < 0) { throw new OverflowException("actualMaxBufferedItemCount should never be less than 0"); } if (this.actualMaxBufferedItemCount > int.MaxValue) { throw new OverflowException("actualMaxBufferedItemCount should never be greater than int.MaxValue"); } if (this.actualMaxPageSize < 0) { throw new OverflowException("actualMaxPageSize should never be less than 0"); } if (this.actualMaxPageSize > int.MaxValue) { throw new OverflowException("actualMaxPageSize should never be greater than int.MaxValue"); } }
public static async Task <IDocumentQueryExecutionContext> CreateSpecializedDocumentQueryExecutionContext( IDocumentQueryClient client, ResourceType resourceTypeEnum, Type resourceType, Expression expression, FeedOptions feedOptions, string resourceLink, bool isContinuationExpected, PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, List <PartitionKeyRange> targetRanges, string collectionRid, CancellationToken token, Guid correlatedActivityId) { long initialPageSize = feedOptions.MaxItemCount.GetValueOrDefault(ParallelQueryConfig.GetConfig().ClientInternalPageSize); if (initialPageSize < -1 || initialPageSize == 0) { throw new BadRequestException(string.Format(CultureInfo.InvariantCulture, "Invalid MaxItemCount {0}", initialPageSize)); } QueryInfo queryInfo = partitionedQueryExecutionInfo.QueryInfo; bool getLazyFeedResponse = queryInfo.HasTop; // We need to compute the optimal initial page size for order-by queries if (queryInfo.HasOrderBy) { int top; if (queryInfo.HasTop && (top = partitionedQueryExecutionInfo.QueryInfo.Top.Value) > 0) { // All partitions should initially fetch about 1/nth of the top value. long pageSizeWithTop = (long)Math.Min( Math.Ceiling(top / (double)targetRanges.Count) * PageSizeFactorForTop, top); if (initialPageSize > 0) { initialPageSize = Math.Min(pageSizeWithTop, initialPageSize); } else { initialPageSize = pageSizeWithTop; } } else if (isContinuationExpected) { if (initialPageSize < 0) { // Max of what the user is willing to buffer and the default (note this is broken if MaxBufferedItemCount = -1) initialPageSize = (long)Math.Max(feedOptions.MaxBufferedItemCount, ParallelQueryConfig.GetConfig().DefaultMaximumBufferSize); } initialPageSize = (long)Math.Min( Math.Ceiling(initialPageSize / (double)targetRanges.Count) * PageSizeFactorForTop, initialPageSize); } } Debug.Assert(initialPageSize > 0 && initialPageSize <= int.MaxValue, string.Format(CultureInfo.InvariantCulture, "Invalid MaxItemCount {0}", initialPageSize)); return(await PipelinedDocumentQueryExecutionContext.CreateAsync( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, collectionRid, partitionedQueryExecutionInfo, targetRanges, (int)initialPageSize, isContinuationExpected, getLazyFeedResponse, token, correlatedActivityId)); }