/// <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 #2
0
 /// <summary>
 /// Callback to create a child document producer tree based on the partition key range.
 /// </summary>
 /// <param name="queryContext">request context</param>
 /// <param name="querySpecForInit">query spec for initialization</param>
 /// <param name="produceAsyncCompleteCallback">Callback to invoke once a fetch finishes.</param>
 /// <param name="itemProducerTreeComparer">Comparer to determine, which tree to produce from.</param>
 /// <param name="equalityComparer">Comparer to see if we need to return the continuation token for a partition.</param>
 /// <param name="testFlags">Test flags.</param>
 /// <param name="deferFirstPage">Whether or not to defer fetching the first page.</param>
 /// <param name="collectionRid">The collection to drain from.</param>
 /// <param name="initialPageSize">The initial page size.</param>
 /// <returns>A function that given a partition key range and continuation token will create a document producer.</returns>
 private static Func <Documents.PartitionKeyRange, string, ItemProducerTree> CreateItemProducerTreeCallback(
     CosmosQueryContext queryContext,
     SqlQuerySpec querySpecForInit,
     ProduceAsyncCompleteDelegate produceAsyncCompleteCallback,
     IComparer <ItemProducerTree> itemProducerTreeComparer,
     IEqualityComparer <CosmosElement> equalityComparer,
     TestInjections testFlags,
     bool deferFirstPage,
     string collectionRid,
     long initialPageSize = 50)
 {
     return((partitionKeyRange, continuationToken) =>
     {
         return new ItemProducerTree(
             queryContext,
             querySpecForInit,
             partitionKeyRange,
             produceAsyncCompleteCallback,
             itemProducerTreeComparer,
             equalityComparer,
             testFlags,
             deferFirstPage,
             collectionRid,
             initialPageSize,
             continuationToken);
     });
 }
        /// <summary>
        /// Initializes a new instance of the ItemProducer class.
        /// </summary>
        /// <param name="queryContext">request context</param>
        /// <param name="querySpecForInit">query spec for initialization</param>
        /// <param name="partitionKeyRange">The partition key range.</param>
        /// <param name="produceAsyncCompleteCallback">The callback to call once you are done fetching.</param>
        /// <param name="equalityComparer">The comparer to use to determine whether the producer has seen a new document.</param>
        /// <param name="testFlags">Flags used to help faciliate testing.</param>
        /// <param name="initialPageSize">The initial page size.</param>
        /// <param name="initialContinuationToken">The initial continuation token.</param>
        public ItemProducer(
            CosmosQueryContext queryContext,
            SqlQuerySpec querySpecForInit,
            PartitionKeyRange partitionKeyRange,
            ProduceAsyncCompleteDelegate produceAsyncCompleteCallback,
            IEqualityComparer <CosmosElement> equalityComparer,
            TestInjections testFlags,
            long initialPageSize            = 50,
            string initialContinuationToken = null)
        {
            this.bufferedPages = new AsyncCollection <QueryResponseCore>();

            // We use a binary semaphore to get the behavior of a mutex,
            // since fetching documents from the backend using a continuation token is a critical section.
            this.fetchSemaphore               = new SemaphoreSlim(1, 1);
            this.queryContext                 = queryContext;
            this.querySpecForInit             = querySpecForInit;
            this.PartitionKeyRange            = partitionKeyRange ?? throw new ArgumentNullException(nameof(partitionKeyRange));
            this.produceAsyncCompleteCallback = produceAsyncCompleteCallback ?? throw new ArgumentNullException(nameof(produceAsyncCompleteCallback));
            this.equalityComparer             = equalityComparer ?? throw new ArgumentNullException(nameof(equalityComparer));
            this.pageSize = initialPageSize;
            this.CurrentContinuationToken  = initialContinuationToken;
            this.BackendContinuationToken  = initialContinuationToken;
            this.PreviousContinuationToken = initialContinuationToken;
            if (!string.IsNullOrEmpty(initialContinuationToken))
            {
                this.hasStartedFetching = true;
                this.IsActive           = true;
            }

            this.testFlags = testFlags;

            this.HasMoreResults = true;
        }
            /// <summary>
            /// Initializes a new instance of the InitParams struct.
            /// </summary>
            /// <param name="sqlQuerySpec">The Sql query spec</param>
            /// <param name="collectionRid">The collection rid.</param>
            /// <param name="partitionedQueryExecutionInfo">The partitioned query execution info.</param>
            /// <param name="partitionKeyRanges">The partition key ranges.</param>
            /// <param name="initialPageSize">The initial page size.</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="testSettings">Test settings.</param>
            public CrossPartitionInitParams(
                SqlQuerySpec sqlQuerySpec,
                string collectionRid,
                PartitionedQueryExecutionInfo partitionedQueryExecutionInfo,
                List <PartitionKeyRange> partitionKeyRanges,
                int initialPageSize,
                int?maxConcurrency,
                int?maxItemCount,
                int?maxBufferedItemCount,
                TestInjections testSettings)
            {
                if (string.IsNullOrWhiteSpace(collectionRid))
                {
                    throw new ArgumentException($"{nameof(collectionRid)} can not be null, empty, or white space.");
                }

                if (partitionedQueryExecutionInfo == null)
                {
                    throw new ArgumentNullException($"{nameof(partitionedQueryExecutionInfo)} can not be null.");
                }

                if (partitionKeyRanges == null)
                {
                    throw new ArgumentNullException($"{nameof(partitionKeyRanges)} can not be null.");
                }

                foreach (PartitionKeyRange partitionKeyRange in partitionKeyRanges)
                {
                    if (partitionKeyRange == null)
                    {
                        throw new ArgumentNullException($"{nameof(partitionKeyRange)} can not be null.");
                    }
                }

                if (initialPageSize <= 0)
                {
                    throw new ArgumentOutOfRangeException($"{nameof(initialPageSize)} must be at least 1.");
                }

                if (sqlQuerySpec == null)
                {
                    throw new ArgumentNullException($"{nameof(sqlQuerySpec)} can not be null.");
                }

                //// Request continuation is allowed to be null
                this.SqlQuerySpec  = sqlQuerySpec;
                this.CollectionRid = collectionRid;
                this.PartitionedQueryExecutionInfo = partitionedQueryExecutionInfo;
                this.PartitionKeyRanges            = partitionKeyRanges;
                this.InitialPageSize      = initialPageSize;
                this.MaxBufferedItemCount = maxBufferedItemCount;
                this.MaxConcurrency       = maxConcurrency;
                this.MaxItemCount         = maxItemCount;
                this.TestSettings         = testSettings;
            }
            public InputParameters(
                SqlQuerySpec sqlQuerySpec,
                string initialUserContinuationToken,
                int?maxConcurrency,
                int?maxItemCount,
                int?maxBufferedItemCount,
                PartitionKey?partitionKey,
                IReadOnlyDictionary <string, object> properties,
                PartitionedQueryExecutionInfo partitionedQueryExecutionInfo,
                ExecutionEnvironment?executionEnvironment,
                TestInjections testInjections)
            {
                if (sqlQuerySpec == null)
                {
                    throw new ArgumentNullException(nameof(sqlQuerySpec));
                }

                this.SqlQuerySpec = sqlQuerySpec;
                this.InitialUserContinuationToken = initialUserContinuationToken;

                int resolvedMaxConcurrency = maxConcurrency.GetValueOrDefault(InputParameters.DefaultMaxConcurrency);

                if (resolvedMaxConcurrency < 0)
                {
                    resolvedMaxConcurrency = int.MaxValue;
                }
                this.MaxConcurrency = resolvedMaxConcurrency;

                int resolvedMaxItemCount = maxItemCount.GetValueOrDefault(InputParameters.DefaultMaxItemCount);

                if (resolvedMaxItemCount < 0)
                {
                    resolvedMaxItemCount = int.MaxValue;
                }
                this.MaxItemCount = resolvedMaxItemCount;

                int resolvedMaxBufferedItemCount = maxBufferedItemCount.GetValueOrDefault(InputParameters.DefaultMaxBufferedItemCount);

                if (resolvedMaxBufferedItemCount < 0)
                {
                    resolvedMaxBufferedItemCount = int.MaxValue;
                }
                this.MaxBufferedItemCount = resolvedMaxBufferedItemCount;

                this.PartitionKey = partitionKey;
                this.Properties   = properties;
                this.PartitionedQueryExecutionInfo = partitionedQueryExecutionInfo;

                ExecutionEnvironment resolvedExecutionEnvironment = executionEnvironment.GetValueOrDefault(InputParameters.DefaultExecutionEnvironment);

                this.ExecutionEnvironment = resolvedExecutionEnvironment;
                this.TestInjections       = testInjections;
            }
Beispiel #6
0
            public InputParameters(
                SqlQuerySpec sqlQuerySpec,
                CosmosElement initialUserContinuationToken,
                FeedRangeInternal initialFeedRange,
                int?maxConcurrency,
                int?maxItemCount,
                int?maxBufferedItemCount,
                PartitionKey?partitionKey,
                IReadOnlyDictionary <string, object> properties,
                PartitionedQueryExecutionInfo partitionedQueryExecutionInfo,
                ExecutionEnvironment?executionEnvironment,
                bool?returnResultsInDeterministicOrder,
                bool forcePassthrough,
                TestInjections testInjections)
            {
                this.SqlQuerySpec = sqlQuerySpec ?? throw new ArgumentNullException(nameof(sqlQuerySpec));
                this.InitialUserContinuationToken = initialUserContinuationToken;
                this.InitialFeedRange             = initialFeedRange;

                int resolvedMaxConcurrency = maxConcurrency.GetValueOrDefault(InputParameters.DefaultMaxConcurrency);

                if (resolvedMaxConcurrency < 0)
                {
                    resolvedMaxConcurrency = int.MaxValue;
                }
                this.MaxConcurrency = resolvedMaxConcurrency;

                int resolvedMaxItemCount = maxItemCount.GetValueOrDefault(InputParameters.DefaultMaxItemCount);

                if (resolvedMaxItemCount < 0)
                {
                    resolvedMaxItemCount = int.MaxValue;
                }
                this.MaxItemCount = resolvedMaxItemCount;

                int resolvedMaxBufferedItemCount = maxBufferedItemCount.GetValueOrDefault(InputParameters.DefaultMaxBufferedItemCount);

                if (resolvedMaxBufferedItemCount < 0)
                {
                    resolvedMaxBufferedItemCount = int.MaxValue;
                }
                this.MaxBufferedItemCount = resolvedMaxBufferedItemCount;

                this.PartitionKey = partitionKey;
                this.Properties   = properties;
                this.PartitionedQueryExecutionInfo     = partitionedQueryExecutionInfo;
                this.ExecutionEnvironment              = executionEnvironment.GetValueOrDefault(InputParameters.DefaultExecutionEnvironment);
                this.ReturnResultsInDeterministicOrder = returnResultsInDeterministicOrder.GetValueOrDefault(InputParameters.DefaultReturnResultsInDeterministicOrder);
                this.ForcePassthrough = forcePassthrough;
                this.TestInjections   = testInjections;
            }
 /// <summary>
 /// Initializes a new instance of the CosmosParallelItemQueryExecutionContext class.
 /// </summary>
 /// <param name="queryContext">The parameters for constructing 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="testSettings">Test settings.</param>
 private CosmosParallelItemQueryExecutionContext(
     CosmosQueryContext queryContext,
     int?maxConcurrency,
     int?maxItemCount,
     int?maxBufferedItemCount,
     TestInjections testSettings)
     : base(
         queryContext: queryContext,
         maxConcurrency: maxConcurrency,
         maxItemCount: maxItemCount,
         maxBufferedItemCount: maxBufferedItemCount,
         moveNextComparer: CosmosParallelItemQueryExecutionContext.MoveNextComparer,
         fetchPrioirtyFunction: CosmosParallelItemQueryExecutionContext.FetchPriorityFunction,
         equalityComparer: CosmosParallelItemQueryExecutionContext.EqualityComparer,
         testSettings: testSettings)
 {
 }
Beispiel #8
0
 /// <summary>
 /// Initializes a new instance of the CosmosOrderByItemQueryExecutionContext class.
 /// </summary>
 /// <param name="initPararms">The params used to construct the base class.</param>
 /// For cross partition order by queries a query like "SELECT c.id, c.field_0 ORDER BY r.field_7 gets rewritten as:
 /// <![CDATA[
 /// SELECT r._rid, [{"item": r.field_7}] AS orderByItems, {"id": r.id, "field_0": r.field_0} AS payload
 /// FROM r
 /// WHERE({ document db - formattable order by query - filter})
 /// ORDER BY r.field_7]]>
 /// This is needed because we need to add additional filters to the query when we resume from a continuation,
 /// and it lets us easily parse out the _rid orderByItems, and payload without parsing the entire document (and having to remember the order by field).
 /// <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="consumeComparer">Comparer used to internally compare documents from different sorted partitions.</param>
 /// <param name="testSettings">Test settings.</param>
 private CosmosOrderByItemQueryExecutionContext(
     CosmosQueryContext initPararms,
     int?maxConcurrency,
     int?maxItemCount,
     int?maxBufferedItemCount,
     OrderByItemProducerTreeComparer consumeComparer,
     TestInjections testSettings)
     : base(
         queryContext: initPararms,
         maxConcurrency: maxConcurrency,
         maxItemCount: maxItemCount,
         maxBufferedItemCount: maxBufferedItemCount,
         moveNextComparer: consumeComparer,
         fetchPrioirtyFunction: CosmosOrderByItemQueryExecutionContext.FetchPriorityFunction,
         equalityComparer: new OrderByEqualityComparer(consumeComparer),
         returnResultsInDeterministicOrder: true,
         testSettings: testSettings)
 {
 }
Beispiel #9
0
 /// <summary>
 /// Initializes a new instance of the CosmosParallelItemQueryExecutionContext class.
 /// </summary>
 /// <param name="queryContext">The parameters for constructing 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">The comparer to use for the priority queue.</param>
 /// <param name="returnResultsInDeterministicOrder">Whether or not to return results in deterministic order.</param>
 /// <param name="testSettings">Test settings.</param>
 private CosmosParallelItemQueryExecutionContext(
     CosmosQueryContext queryContext,
     int?maxConcurrency,
     int?maxItemCount,
     int?maxBufferedItemCount,
     IComparer <ItemProducerTree> moveNextComparer,
     bool returnResultsInDeterministicOrder,
     TestInjections testSettings)
     : base(
         queryContext: queryContext,
         maxConcurrency: maxConcurrency,
         maxItemCount: maxItemCount,
         maxBufferedItemCount: maxBufferedItemCount,
         moveNextComparer: moveNextComparer,
         fetchPrioirtyFunction: CosmosParallelItemQueryExecutionContext.FetchPriorityFunction,
         equalityComparer: CosmosParallelItemQueryExecutionContext.EqualityComparer,
         returnResultsInDeterministicOrder: returnResultsInDeterministicOrder,
         testSettings: testSettings)
 {
     this.returnResultsInDeterministicOrder = returnResultsInDeterministicOrder;
 }
Beispiel #10
0
        /// <summary>
        /// Initializes a new instance of the ItemProducerTree class.
        /// </summary>
        /// <param name="queryContext">query context.</param>
        /// <param name="querySpecForInit">query spec init.</param>
        /// <param name="partitionKeyRange">The partition key range.</param>
        /// <param name="produceAsyncCompleteCallback">Callback to invoke once a fetch finishes.</param>
        /// <param name="itemProducerTreeComparer">Comparer to determine, which tree to produce from.</param>
        /// <param name="equalityComparer">Comparer to see if we need to return the continuation token for a partition.</param>
        /// <param name="testSettings">Test flags.</param>
        /// <param name="deferFirstPage">Whether or not to defer fetching the first page.</param>
        /// <param name="collectionRid">The collection to drain from.</param>
        /// <param name="initialPageSize">The initial page size.</param>
        /// <param name="initialContinuationToken">The initial continuation token.</param>
        public ItemProducerTree(
            CosmosQueryContext queryContext,
            SqlQuerySpec querySpecForInit,
            Documents.PartitionKeyRange partitionKeyRange,
            ProduceAsyncCompleteDelegate produceAsyncCompleteCallback,
            IComparer <ItemProducerTree> itemProducerTreeComparer,
            IEqualityComparer <CosmosElement> equalityComparer,
            TestInjections testSettings,
            bool deferFirstPage,
            string collectionRid,
            long initialPageSize            = 50,
            string initialContinuationToken = null)
        {
            if (queryContext == null)
            {
                throw new ArgumentNullException($"{nameof(queryContext)}");
            }

            if (itemProducerTreeComparer == null)
            {
                throw new ArgumentNullException($"{nameof(itemProducerTreeComparer)}");
            }

            if (produceAsyncCompleteCallback == null)
            {
                throw new ArgumentNullException($"{nameof(produceAsyncCompleteCallback)}");
            }

            if (itemProducerTreeComparer == null)
            {
                throw new ArgumentNullException($"{nameof(itemProducerTreeComparer)}");
            }

            if (equalityComparer == null)
            {
                throw new ArgumentNullException($"{nameof(equalityComparer)}");
            }

            if (string.IsNullOrEmpty(collectionRid))
            {
                throw new ArgumentException($"{nameof(collectionRid)} can not be null or empty.");
            }

            this.Root = new ItemProducer(
                queryContext,
                querySpecForInit,
                partitionKeyRange,
                (itemsBuffered, resourceUnitUsage, diagnostics, requestLength, token) => produceAsyncCompleteCallback(this, itemsBuffered, resourceUnitUsage, diagnostics, requestLength, token),
                equalityComparer,
                testSettings,
                initialPageSize,
                initialContinuationToken);

            this.queryClient    = queryContext.QueryClient;
            this.children       = new PriorityQueue <ItemProducerTree>(itemProducerTreeComparer, true);
            this.deferFirstPage = deferFirstPage;
            this.collectionRid  = collectionRid;
            this.createItemProducerTreeCallback = ItemProducerTree.CreateItemProducerTreeCallback(
                queryContext,
                querySpecForInit,
                produceAsyncCompleteCallback,
                itemProducerTreeComparer,
                equalityComparer,
                testSettings,
                deferFirstPage,
                collectionRid,
                initialPageSize);
            this.executeWithSplitProofingSemaphore = new SemaphoreSlim(1, 1);
        }