Beispiel #1
0
        /// <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;
        }
Beispiel #3
0
        /// <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));
        }
Beispiel #5
0
        /// <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));
        }
Beispiel #7
0
        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");
            }
        }
Beispiel #8
0
        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));
        }