/// <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> /// 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; }
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) { }
/// <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) { }
/// <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; }
/// <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); }