public DefaultDocumentQueryExecutionContext( DocumentQueryExecutionContextBase.InitParams constructorParams, bool isContinuationExpected) : base(constructorParams) { this.isContinuationExpected = isContinuationExpected; this.fetchSchedulingMetrics = new SchedulingStopwatch(); this.fetchSchedulingMetrics.Ready(); this.fetchExecutionRangeAccumulator = new FetchExecutionRangeAccumulator(); this.providedRangesCache = new Dictionary <string, IReadOnlyList <Range <string> > >(); this.retries = -1; this.partitionRoutingHelper = new PartitionRoutingHelper(); }
public async Task <DocumentFeedResponse <CosmosElement> > ExecuteNextFeedResponseAsync(CancellationToken token) { if (this.IsDone) { throw new InvalidOperationException(RMResources.DocumentQueryExecutionContextIsDone); } Error error = null; try { return(await this.innerExecutionContext.ExecuteNextFeedResponseAsync(token)); } catch (DocumentClientException ex) { if (ex.StatusCode != HttpStatusCode.BadRequest || ex.GetSubStatus() != SubStatusCodes.CrossPartitionQueryNotServable) { throw; } error = ex.Error; } PartitionedQueryExecutionInfo partitionedQueryExecutionInfo = JsonConvert.DeserializeObject <PartitionedQueryExecutionInfo>(error.AdditionalErrorInfo); DefaultDocumentQueryExecutionContext queryExecutionContext = (DefaultDocumentQueryExecutionContext)this.innerExecutionContext; List <PartitionKeyRange> partitionKeyRanges = await queryExecutionContext.GetTargetPartitionKeyRangesAsync(collection.ResourceId, partitionedQueryExecutionInfo.QueryRanges); DocumentQueryExecutionContextBase.InitParams constructorParams = new DocumentQueryExecutionContextBase.InitParams(this.client, this.resourceTypeEnum, this.resourceType, this.expression, this.feedOptions, this.resourceLink, false, correlatedActivityId); this.innerExecutionContext = await DocumentQueryExecutionContextFactory.CreateSpecializedDocumentQueryExecutionContextAsync( constructorParams, partitionedQueryExecutionInfo, partitionKeyRanges, this.collection.ResourceId, this.isContinuationExpected, token); return(await this.innerExecutionContext.ExecuteNextFeedResponseAsync(token)); }
/// <summary> /// Creates a ParallelDocumentQueryExecutionContext /// </summary> /// <param name="constructorParams">The params the construct the base class.</param> /// <param name="initParams">The params to initialize the cross partition context.</param> /// <param name="token">The cancellation token.</param> /// <returns>A task to await on, which in turn returns a ParallelDocumentQueryExecutionContext.</returns> public static async Task <ParallelDocumentQueryExecutionContext> CreateAsync( DocumentQueryExecutionContextBase.InitParams constructorParams, CrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams, CancellationToken token) { Debug.Assert( !initParams.PartitionedQueryExecutionInfo.QueryInfo.HasOrderBy, "Parallel~Context must not have order by query info."); ParallelDocumentQueryExecutionContext context = new ParallelDocumentQueryExecutionContext( constructorParams, initParams.PartitionedQueryExecutionInfo.QueryInfo.RewrittenQuery); await context.InitializeAsync( initParams.CollectionRid, initParams.PartitionKeyRanges, initParams.InitialPageSize, initParams.RequestContinuation, token); return(context); }
public static async Task <IDocumentQueryExecutionContext> CreateDocumentQueryExecutionContextAsync( IDocumentQueryClient client, ResourceType resourceTypeEnum, Type resourceType, Expression expression, FeedOptions feedOptions, string resourceLink, bool isContinuationExpected, CancellationToken token, Guid correlatedActivityId) { ContainerProperties collection = null; if (resourceTypeEnum.IsCollectionChild()) { CollectionCache collectionCache = await client.GetCollectionCacheAsync(); using ( DocumentServiceRequest request = DocumentServiceRequest.Create( OperationType.Query, resourceTypeEnum, resourceLink, AuthorizationTokenType.Invalid)) //this request doesnt actually go to server { collection = await collectionCache.ResolveCollectionAsync(request, token); } if (feedOptions != null && feedOptions.PartitionKey != null && feedOptions.PartitionKey.Equals(Documents.PartitionKey.None)) { feedOptions.PartitionKey = Documents.PartitionKey.FromInternalKey(collection.GetNoneValue()); } } DocumentQueryExecutionContextBase.InitParams constructorParams = new DocumentQueryExecutionContextBase.InitParams( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, false, correlatedActivityId); // For non-Windows platforms(like Linux and OSX) in .NET Core SDK, we cannot use ServiceInterop, so need to bypass in that case. // We are also now bypassing this for 32 bit host process running even on Windows as there are many 32 bit apps that will not work without this if (CustomTypeExtensions.ByPassQueryParsing()) { // We create a ProxyDocumentQueryExecutionContext that will be initialized with DefaultDocumentQueryExecutionContext // which will be used to send the query to Gateway and on getting 400(bad request) with 1004(cross partition query not servable), we initialize it with // PipelinedDocumentQueryExecutionContext by providing the partition query execution info that's needed(which we get from the exception returned from Gateway). ProxyDocumentQueryExecutionContext proxyQueryExecutionContext = ProxyDocumentQueryExecutionContext.Create( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, token, collection, isContinuationExpected, correlatedActivityId); return(proxyQueryExecutionContext); } DefaultDocumentQueryExecutionContext queryExecutionContext = await DefaultDocumentQueryExecutionContext.CreateAsync( constructorParams, isContinuationExpected, token); // If isContinuationExpected is false, we want to check if there are aggregates. if ( resourceTypeEnum.IsCollectionChild() && resourceTypeEnum.IsPartitioned() && (feedOptions.EnableCrossPartitionQuery || !isContinuationExpected)) { //todo:elasticcollections this may rely on information from collection cache which is outdated //if collection is deleted/created with same name. //need to make it not rely on information from collection cache. PartitionedQueryExecutionInfo partitionedQueryExecutionInfo = await queryExecutionContext.GetPartitionedQueryExecutionInfoAsync( partitionKeyDefinition : collection.PartitionKey, requireFormattableOrderByQuery : true, isContinuationExpected : isContinuationExpected, allowNonValueAggregateQuery : true, hasLogicalPartitionKey : feedOptions.PartitionKey != null, cancellationToken : token); if (DocumentQueryExecutionContextFactory.ShouldCreateSpecializedDocumentQueryExecutionContext( resourceTypeEnum, feedOptions, partitionedQueryExecutionInfo, collection.PartitionKey, isContinuationExpected)) { List <PartitionKeyRange> targetRanges = await GetTargetPartitionKeyRangesAsync( queryExecutionContext, partitionedQueryExecutionInfo, collection, feedOptions); // Devnote this will get replace by the new v3 to v2 logic throw new NotSupportedException("v2 query excution context is currently not supported."); } } return(queryExecutionContext); }
/// <summary> /// Creates a PipelinedDocumentQueryExecutionContext. /// </summary> /// <param name="constructorParams">The parameters for constructing the base class.</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="requestContinuation">The request continuation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task to await on, which in turn returns a PipelinedDocumentQueryExecutionContext.</returns> public static async Task <IDocumentQueryExecutionContext> CreateDocumentQueryExecutionContextAsync( DocumentQueryExecutionContextBase.InitParams constructorParams, string collectionRid, PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, List <PartitionKeyRange> partitionKeyRanges, int initialPageSize, string requestContinuation, CancellationToken cancellationToken) { DefaultTrace.TraceInformation( string.Format( CultureInfo.InvariantCulture, "{0}, CorrelatedActivityId: {1} | Pipelined~Context.CreateAsync", DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture), constructorParams.CorrelatedActivityId)); QueryInfo queryInfo = partitionedQueryExecutionInfo.QueryInfo; int actualPageSize = initialPageSize; if (queryInfo.HasGroupBy) { initialPageSize = int.MaxValue; constructorParams.FeedOptions.MaxItemCount = int.MaxValue; } Func <string, Task <IDocumentQueryExecutionComponent> > createOrderByComponentFunc = async(continuationToken) => { CrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CrossPartitionQueryExecutionContext.CrossPartitionInitParams( collectionRid, partitionedQueryExecutionInfo, partitionKeyRanges, initialPageSize, continuationToken); return(await OrderByDocumentQueryExecutionContext.CreateAsync( constructorParams, initParams, cancellationToken)); }; Func <string, Task <IDocumentQueryExecutionComponent> > createParallelComponentFunc = async(continuationToken) => { CrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CrossPartitionQueryExecutionContext.CrossPartitionInitParams( collectionRid, partitionedQueryExecutionInfo, partitionKeyRanges, initialPageSize, continuationToken); return(await ParallelDocumentQueryExecutionContext.CreateAsync( constructorParams, initParams, cancellationToken)); }; return((IDocumentQueryExecutionContext)(await PipelinedDocumentQueryExecutionContext.CreateHelperAsync( partitionedQueryExecutionInfo.QueryInfo, initialPageSize, requestContinuation, constructorParams.FeedOptions.EnableGroupBy, createOrderByComponentFunc, createParallelComponentFunc))); }
/// <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 <IDocumentQueryExecutionContext> CreateSpecializedDocumentQueryExecutionContext( DocumentQueryExecutionContextBase.InitParams constructorParams, PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, List <PartitionKeyRange> targetRanges, string collectionRid, bool isContinuationExpected, CancellationToken cancellationToken) { // Figure out the optimal page size. long initialPageSize = constructorParams.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 = Math.Max(constructorParams.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( constructorParams, collectionRid, partitionedQueryExecutionInfo, targetRanges, (int)initialPageSize, constructorParams.FeedOptions.RequestContinuation, cancellationToken)); }
/// <summary> /// Creates a PipelinedDocumentQueryExecutionContext. /// </summary> /// <param name="constructorParams">The parameters for constructing the base class.</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="requestContinuation">The request continuation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task to await on, which in turn returns a PipelinedDocumentQueryExecutionContext.</returns> public static async Task <IDocumentQueryExecutionContext> CreateAsync( DocumentQueryExecutionContextBase.InitParams constructorParams, string collectionRid, PartitionedQueryExecutionInfo partitionedQueryExecutionInfo, List <PartitionKeyRange> partitionKeyRanges, int initialPageSize, string requestContinuation, CancellationToken cancellationToken) { DefaultTrace.TraceInformation( string.Format( CultureInfo.InvariantCulture, "{0}, CorrelatedActivityId: {1} | Pipelined~Context.CreateAsync", DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture), constructorParams.CorrelatedActivityId)); Func <string, Task <IDocumentQueryExecutionComponent> > createComponentFunc; QueryInfo queryInfo = partitionedQueryExecutionInfo.QueryInfo; if (queryInfo.HasOrderBy) { createComponentFunc = async(continuationToken) => { CrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CrossPartitionQueryExecutionContext.CrossPartitionInitParams( collectionRid, partitionedQueryExecutionInfo, partitionKeyRanges, initialPageSize, continuationToken); return(await OrderByDocumentQueryExecutionContext.CreateAsync( constructorParams, initParams, cancellationToken)); }; } else { createComponentFunc = async(continuationToken) => { CrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CrossPartitionQueryExecutionContext.CrossPartitionInitParams( collectionRid, partitionedQueryExecutionInfo, partitionKeyRanges, initialPageSize, continuationToken); return(await ParallelDocumentQueryExecutionContext.CreateAsync( constructorParams, initParams, cancellationToken)); }; } if (queryInfo.HasAggregates) { Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(continuationToken) => { return(await AggregateDocumentQueryExecutionComponent.CreateAsync( queryInfo.Aggregates, continuationToken, createSourceCallback)); }; } if (queryInfo.HasDistinct) { Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(continuationToken) => { return(await DistinctDocumentQueryExecutionComponent.CreateAsync( continuationToken, createSourceCallback, queryInfo.DistinctType)); }; } if (queryInfo.HasOffset) { if (!constructorParams.FeedOptions.EnableCrossPartitionSkipTake) { throw new ArgumentException("Cross Partition OFFSET / LIMIT is not supported."); } Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(continuationToken) => { return(await SkipDocumentQueryExecutionComponent.CreateAsync( queryInfo.Offset.Value, continuationToken, createSourceCallback)); }; } if (queryInfo.HasLimit) { if (!constructorParams.FeedOptions.EnableCrossPartitionSkipTake) { throw new ArgumentException("Cross Partition OFFSET / LIMIT is not supported."); } Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(continuationToken) => { return(await TakeDocumentQueryExecutionComponent.CreateLimitDocumentQueryExecutionComponentAsync( queryInfo.Limit.Value, continuationToken, createSourceCallback)); }; } if (queryInfo.HasTop) { Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc; createComponentFunc = async(continuationToken) => { return(await TakeDocumentQueryExecutionComponent.CreateTopDocumentQueryExecutionComponentAsync( queryInfo.Top.Value, continuationToken, createSourceCallback)); }; } return(new PipelinedDocumentQueryExecutionContext( await createComponentFunc(requestContinuation), initialPageSize)); }
public static async Task <IDocumentQueryExecutionContext> CreateDocumentQueryExecutionContextAsync( IDocumentQueryClient client, ResourceType resourceTypeEnum, Type resourceType, Expression expression, FeedOptions feedOptions, string resourceLink, bool isContinuationExpected, CancellationToken token, Guid correlatedActivityId) { DocumentQueryExecutionContextBase.InitParams constructorParams = new DocumentQueryExecutionContextBase.InitParams( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, false, correlatedActivityId); CosmosContainerSettings collection = null; if (resourceTypeEnum.IsCollectionChild()) { CollectionCache collectionCache = await client.GetCollectionCacheAsync(); using ( DocumentServiceRequest request = DocumentServiceRequest.Create( OperationType.Query, resourceTypeEnum, resourceLink, AuthorizationTokenType.Invalid)) //this request doesnt actually go to server { collection = await collectionCache.ResolveCollectionAsync(request, token); } } // For non-Windows platforms(like Linux and OSX) in .NET Core SDK, we cannot use ServiceInterop, so need to bypass in that case. // We are also now bypassing this for 32 bit host process running even on Windows as there are many 32 bit apps that will not work without this if (CustomTypeExtensions.ByPassQueryParsing()) { // We create a ProxyDocumentQueryExecutionContext that will be initialized with DefaultDocumentQueryExecutionContext // which will be used to send the query to Gateway and on getting 400(bad request) with 1004(cross partition query not servable), we initialize it with // PipelinedDocumentQueryExecutionContext by providing the partition query execution info that's needed(which we get from the exception returned from Gateway). ProxyDocumentQueryExecutionContext proxyQueryExecutionContext = ProxyDocumentQueryExecutionContext.CreateAsync( client, resourceTypeEnum, resourceType, expression, feedOptions, resourceLink, token, collection, isContinuationExpected, correlatedActivityId); return(proxyQueryExecutionContext); } DefaultDocumentQueryExecutionContext queryExecutionContext = await DefaultDocumentQueryExecutionContext.CreateAsync( constructorParams, isContinuationExpected, token); // If isContinuationExpected is false, we want to check if there are aggregates. if ( resourceTypeEnum.IsCollectionChild() && resourceTypeEnum.IsPartitioned() && (feedOptions.EnableCrossPartitionQuery || !isContinuationExpected)) { //todo:elasticcollections this may rely on information from collection cache which is outdated //if collection is deleted/created with same name. //need to make it not rely on information from collection cache. PartitionedQueryExecutionInfo partitionedQueryExecutionInfo = await queryExecutionContext.GetPartitionedQueryExecutionInfoAsync( collection.PartitionKey, true, isContinuationExpected, token); if (DocumentQueryExecutionContextFactory.ShouldCreateSpecializedDocumentQueryExecutionContext( resourceTypeEnum, feedOptions, partitionedQueryExecutionInfo, collection.PartitionKey, isContinuationExpected)) { List <PartitionKeyRange> targetRanges; if (!string.IsNullOrEmpty(feedOptions.PartitionKeyRangeId)) { targetRanges = new List <PartitionKeyRange> { await queryExecutionContext.GetTargetPartitionKeyRangeById( collection.ResourceId, feedOptions.PartitionKeyRangeId) }; } else { List <Range <string> > queryRanges = partitionedQueryExecutionInfo.QueryRanges; if (feedOptions.PartitionKey != null) { queryRanges = new List <Range <string> > { Range <string> .GetPointRange( feedOptions.PartitionKey.InternalKey.GetEffectivePartitionKeyString( collection.PartitionKey)) }; } targetRanges = await queryExecutionContext.GetTargetPartitionKeyRanges(collection.ResourceId, queryRanges); } return(await CreateSpecializedDocumentQueryExecutionContext( constructorParams, partitionedQueryExecutionInfo, targetRanges, collection.ResourceId, isContinuationExpected, token)); } } return(queryExecutionContext); }