/// <summary> /// TryAddPartitionKeyRangeToContinuationTokenAsync /// </summary> /// <returns><c>false</c> if collectionRid is likely wrong because range was not found. Cache needs to be refreshed probably.</returns> public virtual async Task<bool> TryAddPartitionKeyRangeToContinuationTokenAsync( INameValueCollection backendResponseHeaders, IReadOnlyList<Range<string>> providedPartitionKeyRanges, IRoutingMapProvider routingMapProvider, string collectionRid, ResolvedRangeInfo resolvedRangeInfo, RntdbEnumerationDirection direction = RntdbEnumerationDirection.Forward) { Debug.Assert(resolvedRangeInfo.ResolvedRange != null, "ResolvedRange can't be null"); PartitionKeyRange currentRange = resolvedRangeInfo.ResolvedRange; // IF : Split happened, or already had multiple target ranges in the continuation if (resolvedRangeInfo.ContinuationTokens != null && resolvedRangeInfo.ContinuationTokens.Count > 1) { if (!string.IsNullOrEmpty(backendResponseHeaders[HttpConstants.HttpHeaders.Continuation])) { resolvedRangeInfo.ContinuationTokens[0].Token = backendResponseHeaders[HttpConstants.HttpHeaders.Continuation]; } else { resolvedRangeInfo.ContinuationTokens.RemoveAt(0); } backendResponseHeaders[HttpConstants.HttpHeaders.Continuation] = JsonConvert.SerializeObject(resolvedRangeInfo.ContinuationTokens); } else { //// ELSE: Single target Range was provided, and no split happened PartitionKeyRange rangeToUse = currentRange; // We only need to get the next range if we have to if (string.IsNullOrEmpty(backendResponseHeaders[HttpConstants.HttpHeaders.Continuation])) { if (direction == RntdbEnumerationDirection.Reverse) { rangeToUse = PartitionRoutingHelper.MinBefore( (await routingMapProvider.TryGetOverlappingRangesAsync(collectionRid, providedPartitionKeyRanges.Single())).ToList(), currentRange); } else { Range<string> nextProvidedRange = PartitionRoutingHelper.MinAfter( providedPartitionKeyRanges, currentRange.ToRange(), Range<string>.MaxComparer.Instance); if (nextProvidedRange == null) { return true; } string max = string.CompareOrdinal(nextProvidedRange.Min, currentRange.MaxExclusive) > 0 ? nextProvidedRange.Min : currentRange.MaxExclusive; if (string.CompareOrdinal(max, PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey) == 0) { return true; } PartitionKeyRange nextRange = await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(collectionRid, max); if (nextRange == null) { return false; } rangeToUse = nextRange; } } if (rangeToUse != null) { backendResponseHeaders[HttpConstants.HttpHeaders.Continuation] = PartitionRoutingHelper.AddPartitionKeyRangeToContinuationToken( backendResponseHeaders[HttpConstants.HttpHeaders.Continuation], rangeToUse); } } return true; }
public static async Task <List <PartitionKeyRange> > TryGetOverlappingRangesAsync( this IRoutingMapProvider routingMapProvider, string collectionResourceId, IEnumerable <Range <string> > sortedRanges, ITrace trace, bool forceRefresh = false) { if (sortedRanges == null) { throw new ArgumentNullException(nameof(sortedRanges)); } // Remove the duplicates SortedSet <Range <string> > distinctSortedRanges = new SortedSet <Range <string> >(sortedRanges, Range <string> .MinComparer.Instance); // Make sure there is no overlap if (!IRoutingMapProviderExtensions.IsNonOverlapping(distinctSortedRanges)) { throw new ArgumentException($"{nameof(sortedRanges)} had overlaps."); } // For each range try to figure out what PartitionKeyRanges it spans. List <PartitionKeyRange> targetRanges = new List <PartitionKeyRange>(); foreach (Range <string> range in sortedRanges) { // if the range is empty then it by definition does not span any ranges. if (range.IsEmpty) { continue; } // If current range already is covered by the most recently added PartitionKeyRange, then we can skip it // (to avoid duplicates). if ((targetRanges.Count != 0) && (Range <string> .MaxComparer.Instance.Compare(range, targetRanges.Last().ToRange()) <= 0)) { continue; } // Calculate what range to look up. Range <string> queryRange; if (targetRanges.Count == 0) { // If there are no existing partition key ranges, // then we take the first to get the ball rolling. queryRange = range; } else { // We don't want to double count a partition key range // So we form a new range where // * left of the range is Max(lastPartitionKeyRange.Right(), currentRange.Left()) // * right is just the right of the currentRange. // That way if the current range overlaps with the partition key range it won't double count it when doing: // routingMapProvider.TryGetOverlappingRangesAsync string left = IRoutingMapProviderExtensions.Max(targetRanges.Last().MaxExclusive, range.Min); bool leftInclusive = string.CompareOrdinal(left, range.Min) == 0 ? range.IsMinInclusive : false; queryRange = new Range <string>( left, range.Max, leftInclusive, range.IsMaxInclusive); } IReadOnlyList <PartitionKeyRange> overlappingRanges = await routingMapProvider.TryGetOverlappingRangesAsync( collectionResourceId, queryRange, trace, forceRefresh); if (overlappingRanges == null) { // null means we weren't able to find the overlapping ranges. // This is due to a stale cache. // It is the caller's responsiblity to recall this method with forceRefresh = true return(null); // Design note: It would be better if this method just returned a bool and followed the standard TryGet Pattern. // It would be even better to remove the forceRefresh flag just replace it with a non TryGet method call. } targetRanges.AddRange(overlappingRanges); } return(targetRanges); }
/// <summary> /// Gets <see cref="PartitionKeyRange"/> instance which corresponds to <paramref name="rangeFromContinuationToken"/> /// </summary> /// <param name="providedPartitionKeyRanges"></param> /// <param name="routingMapProvider"></param> /// <param name="collectionRid"></param> /// <param name="rangeFromContinuationToken"></param> /// <param name="suppliedTokens"></param> /// <param name="direction"></param> /// <returns>null if collection with specified <paramref name="collectionRid"/> doesn't exist, which potentially means /// that collection was resolved to outdated Rid by name. Also null can be returned if <paramref name="rangeFromContinuationToken"/> /// is not found - this means it was split. /// </returns> public virtual async Task<ResolvedRangeInfo> TryGetTargetRangeFromContinuationTokenRangeAsync( IReadOnlyList<Range<string>> providedPartitionKeyRanges, IRoutingMapProvider routingMapProvider, string collectionRid, Range<string> rangeFromContinuationToken, List<CompositeContinuationToken> suppliedTokens, RntdbEnumerationDirection direction = RntdbEnumerationDirection.Forward) { // For queries such as "SELECT * FROM root WHERE false", // we will have empty ranges and just forward the request to the first partition if (providedPartitionKeyRanges.Count == 0) { return new ResolvedRangeInfo( await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync( collectionRid, PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey), suppliedTokens); } // Initially currentRange will be empty if (rangeFromContinuationToken.IsEmpty) { if (direction == RntdbEnumerationDirection.Reverse) { IReadOnlyList<PartitionKeyRange> partitionKeyRanges = await routingMapProvider.TryGetOverlappingRangesAsync(collectionRid, providedPartitionKeyRanges.Single()); PartitionKeyRange lastPartitionKeyRange = partitionKeyRanges[partitionKeyRanges.Count - 1]; return new ResolvedRangeInfo(lastPartitionKeyRange, suppliedTokens); } Range<string> minimumRange = PartitionRoutingHelper.Min( providedPartitionKeyRanges, Range<string>.MinComparer.Instance); return new ResolvedRangeInfo( await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(collectionRid, minimumRange.Min), suppliedTokens); } PartitionKeyRange targetPartitionKeyRange = await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(collectionRid, rangeFromContinuationToken.Min); if (targetPartitionKeyRange == null) { return new ResolvedRangeInfo(null, suppliedTokens); } if (!rangeFromContinuationToken.Equals(targetPartitionKeyRange.ToRange())) { // Cannot find target range. Either collection was resolved incorrectly or the range was split List<PartitionKeyRange> replacedRanges = (await routingMapProvider.TryGetOverlappingRangesAsync(collectionRid, rangeFromContinuationToken, true)).ToList(); if (replacedRanges == null || replacedRanges.Count < 1) { return new ResolvedRangeInfo(null, null); } else { if (!(replacedRanges[0].MinInclusive.Equals(rangeFromContinuationToken.Min) && replacedRanges[replacedRanges.Count - 1].MaxExclusive.Equals(rangeFromContinuationToken.Max))) { return new ResolvedRangeInfo(null, null); } } if (direction == RntdbEnumerationDirection.Reverse) { replacedRanges.Reverse(); } List<CompositeContinuationToken> continuationTokensToBePersisted = null; if (suppliedTokens != null && suppliedTokens.Count > 0) { continuationTokensToBePersisted = new List<CompositeContinuationToken>(replacedRanges.Count + suppliedTokens.Count - 1); foreach (PartitionKeyRange partitionKeyRange in replacedRanges) { CompositeContinuationToken token = (CompositeContinuationToken)suppliedTokens[0].ShallowCopy(); token.Range = partitionKeyRange.ToRange(); continuationTokensToBePersisted.Add(token); } continuationTokensToBePersisted.AddRange(suppliedTokens.Skip(1)); } return new ResolvedRangeInfo(replacedRanges[0], continuationTokensToBePersisted); } return new ResolvedRangeInfo(targetPartitionKeyRange, suppliedTokens); }
public static async Task<List<PartitionKeyRange>> GetReplacementRangesAsync(PartitionKeyRange targetRange, IRoutingMapProvider routingMapProvider, string collectionRid) { return (await routingMapProvider.TryGetOverlappingRangesAsync(collectionRid, targetRange.ToRange(), true)).ToList(); }
internal abstract Task <List <Documents.Routing.Range <string> > > GetEffectiveRangesAsync( IRoutingMapProvider routingMapProvider, string containerRid, Documents.PartitionKeyDefinition partitionKeyDefinition);
internal abstract Task <IEnumerable <string> > GetPartitionKeyRangesAsync( IRoutingMapProvider routingMapProvider, string containerRid, Documents.PartitionKeyDefinition partitionKeyDefinition, CancellationToken cancellationToken);
private async Task <Tuple <PartitionRoutingHelper.ResolvedRangeInfo, IReadOnlyList <Range <string> > > > TryGetTargetPartitionKeyRangeAsync( DocumentServiceRequest request, CosmosContainerProperties collection, QueryPartitionProvider queryPartitionProvider, IRoutingMapProvider routingMapProvider, Range <string> rangeFromContinuationToken, List <CompositeContinuationToken> suppliedTokens) { string version = request.Headers[HttpConstants.HttpHeaders.Version]; version = string.IsNullOrEmpty(version) ? HttpConstants.Versions.CurrentVersion : version; bool enableCrossPartitionQuery = false; string enableCrossPartitionQueryHeader = request.Headers[HttpConstants.HttpHeaders.EnableCrossPartitionQuery]; if (enableCrossPartitionQueryHeader != null) { if (!bool.TryParse(enableCrossPartitionQueryHeader, out enableCrossPartitionQuery)) { throw new BadRequestException( string.Format( CultureInfo.InvariantCulture, RMResources.InvalidHeaderValue, enableCrossPartitionQueryHeader, HttpConstants.HttpHeaders.EnableCrossPartitionQuery)); } } IReadOnlyList <Range <string> > providedRanges; if (!this.providedRangesCache.TryGetValue(collection.ResourceId, out providedRanges)) { if (this.ShouldExecuteQueryRequest) { FeedOptions feedOptions = this.GetFeedOptions(null); PartitionKeyDefinition partitionKeyDefinition; object partitionKeyDefinitionObject; if (feedOptions.Properties != null && feedOptions.Properties.TryGetValue(CosmosQueryExecutionContextFactory.InternalPartitionKeyDefinitionProperty, out partitionKeyDefinitionObject)) { if (partitionKeyDefinitionObject is PartitionKeyDefinition definition) { partitionKeyDefinition = definition; } else { throw new ArgumentException( "partitionkeydefinition has invalid type", nameof(partitionKeyDefinitionObject)); } } else { partitionKeyDefinition = collection.PartitionKey; } QueryInfo queryInfo; providedRanges = PartitionRoutingHelper.GetProvidedPartitionKeyRanges( this.QuerySpec, enableCrossPartitionQuery, false, this.isContinuationExpected, false, //haslogicalpartitionkey partitionKeyDefinition, queryPartitionProvider, version, out queryInfo); } else if (request.Properties != null && request.Properties.TryGetValue( WFConstants.BackendHeaders.EffectivePartitionKeyString, out object effectivePartitionKey)) { if (effectivePartitionKey is string effectivePartitionKeyString) { providedRanges = new List <Range <string> >() { Range <string> .GetPointRange(effectivePartitionKeyString), }; } else { throw new ArgumentException( "EffectivePartitionKey must be a string", WFConstants.BackendHeaders.EffectivePartitionKeyString); } } else { providedRanges = new List <Range <string> > { new Range <string>( PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, true, false) }; } this.providedRangesCache[collection.ResourceId] = providedRanges; } PartitionRoutingHelper.ResolvedRangeInfo resolvedRangeInfo = await this.partitionRoutingHelper.TryGetTargetRangeFromContinuationTokenRangeAsync( providedRanges, routingMapProvider, collection.ResourceId, rangeFromContinuationToken, suppliedTokens); if (resolvedRangeInfo.ResolvedRange == null) { return(null); } else { return(Tuple.Create(resolvedRangeInfo, providedRanges)); } }
private async Task <Tuple <DocumentFeedResponse <CosmosElement>, string> > ExecuteOnceAsync( IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) { // Don't reuse request, as the rest of client SDK doesn't reuse requests between retries. // The code leaves some temporary garbage in request (in RequestContext etc.), // which shold be erased during retries. using (DocumentServiceRequest request = await this.CreateRequestAsync()) { DocumentFeedResponse <CosmosElement> feedRespose; string partitionIdentifier; // We need to determine how to execute the request: if (LogicalPartitionKeyProvided(request)) { feedRespose = await this.ExecuteRequestAsync(request, retryPolicyInstance, cancellationToken); partitionIdentifier = $"PKId({request.Headers[HttpConstants.HttpHeaders.PartitionKey]})"; } else if (PhysicalPartitionKeyRangeIdProvided(this)) { CollectionCache collectionCache = await this.Client.GetCollectionCacheAsync(); CosmosContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None); request.RouteTo(new PartitionKeyRangeIdentity(collection.ResourceId, base.PartitionKeyRangeId)); feedRespose = await this.ExecuteRequestAsync(request, retryPolicyInstance, cancellationToken); partitionIdentifier = base.PartitionKeyRangeId; } else { // The query is going to become a full fan out, but we go one partition at a time. if (ServiceInteropAvailable()) { // Get the routing map provider CollectionCache collectionCache = await this.Client.GetCollectionCacheAsync(); CosmosContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None); QueryPartitionProvider queryPartitionProvider = await this.Client.GetQueryPartitionProviderAsync(cancellationToken); IRoutingMapProvider routingMapProvider = await this.Client.GetRoutingMapProviderAsync(); // Figure out what partition you are going to based on the range from the continuation token // If token is null then just start at partitionKeyRangeId "0" List <CompositeContinuationToken> suppliedTokens; Range <string> rangeFromContinuationToken = this.partitionRoutingHelper.ExtractPartitionKeyRangeFromContinuationToken( request.Headers, out suppliedTokens); Tuple <PartitionRoutingHelper.ResolvedRangeInfo, IReadOnlyList <Range <string> > > queryRoutingInfo = await this.TryGetTargetPartitionKeyRangeAsync( request, collection, queryPartitionProvider, routingMapProvider, rangeFromContinuationToken, suppliedTokens); if (request.IsNameBased && queryRoutingInfo == null) { request.ForceNameCacheRefresh = true; collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None); queryRoutingInfo = await this.TryGetTargetPartitionKeyRangeAsync( request, collection, queryPartitionProvider, routingMapProvider, rangeFromContinuationToken, suppliedTokens); } if (queryRoutingInfo == null) { throw new NotFoundException($"{DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}: Was not able to get queryRoutingInfo even after resolve collection async with force name cache refresh to the following collectionRid: {collection.ResourceId} with the supplied tokens: {JsonConvert.SerializeObject(suppliedTokens)}"); } request.RouteTo(new PartitionKeyRangeIdentity(collection.ResourceId, queryRoutingInfo.Item1.ResolvedRange.Id)); DocumentFeedResponse <CosmosElement> response = await this.ExecuteRequestAsync(request, retryPolicyInstance, cancellationToken); // Form a composite continuation token (range + backend continuation token). // If the backend continuation token was null for the range, // then use the next adjacent range. // This is how the default execution context serially visits every partition. if (!await this.partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync( response.Headers, providedPartitionKeyRanges: queryRoutingInfo.Item2, routingMapProvider: routingMapProvider, collectionRid: collection.ResourceId, resolvedRangeInfo: queryRoutingInfo.Item1)) { // Collection to which this request was resolved doesn't exist. // Retry policy will refresh the cache and return NotFound. throw new NotFoundException($"{DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}: Call to TryAddPartitionKeyRangeToContinuationTokenAsync failed to the following collectionRid: {collection.ResourceId} with the supplied tokens: {JsonConvert.SerializeObject(suppliedTokens)}"); } feedRespose = response; partitionIdentifier = queryRoutingInfo.Item1.ResolvedRange.Id; } else { // For non-Windows platforms(like Linux and OSX) in .NET Core SDK, we cannot use ServiceInterop for parsing the query, // so forcing the request through Gateway. We are also now by-passing this for 32-bit host process in NETFX on Windows // as the ServiceInterop dll is only available in 64-bit. request.UseGatewayMode = true; feedRespose = await this.ExecuteRequestAsync(request, retryPolicyInstance, cancellationToken); partitionIdentifier = "Gateway"; } } return(new Tuple <DocumentFeedResponse <CosmosElement>, string>(feedRespose, partitionIdentifier)); } }
public static async Task <List <PartitionKeyRange> > TryGetOverlappingRangesAsync( this IRoutingMapProvider routingMapProvider, string collectionResourceId, IList <Range <string> > sortedRanges, bool forceRefresh = false) { if (!IsSortedAndNonOverlapping(sortedRanges)) { throw new ArgumentException("sortedRanges"); } List <PartitionKeyRange> targetRanges = new List <PartitionKeyRange>(); int currentProvidedRange = 0; while (currentProvidedRange < sortedRanges.Count) { if (sortedRanges[currentProvidedRange].IsEmpty) { currentProvidedRange++; continue; } Range <string> queryRange; if (targetRanges.Count > 0) { string left = Max( targetRanges[targetRanges.Count - 1].MaxExclusive, sortedRanges[currentProvidedRange].Min); bool leftInclusive = string.CompareOrdinal(left, sortedRanges[currentProvidedRange].Min) == 0 ? sortedRanges[currentProvidedRange].IsMinInclusive : false; queryRange = new Range <string>( left, sortedRanges[currentProvidedRange].Max, leftInclusive, sortedRanges[currentProvidedRange].IsMaxInclusive); } else { queryRange = sortedRanges[currentProvidedRange]; } IReadOnlyList <PartitionKeyRange> overlappingRanges = await routingMapProvider.TryGetOverlappingRangesAsync(collectionResourceId, queryRange, forceRefresh); if (overlappingRanges == null) { return(null); } targetRanges.AddRange(overlappingRanges); Range <string> lastKnownTargetRange = targetRanges[targetRanges.Count - 1].ToRange(); while (currentProvidedRange < sortedRanges.Count && Range <string> .MaxComparer.Instance.Compare(sortedRanges[currentProvidedRange], lastKnownTargetRange) <= 0) { currentProvidedRange++; } } return(targetRanges); }
private async Task <Tuple <PartitionRoutingHelper.ResolvedRangeInfo, IReadOnlyList <Range <string> > > > TryGetTargetPartitionKeyRangeAsync( DocumentServiceRequest request, CosmosContainerSettings collection, QueryPartitionProvider queryPartitionProvider, IRoutingMapProvider routingMapProvider, Range <string> rangeFromContinuationToken, List <CompositeContinuationToken> suppliedTokens) { string version = request.Headers[HttpConstants.HttpHeaders.Version]; version = string.IsNullOrEmpty(version) ? HttpConstants.Versions.CurrentVersion : version; bool enableCrossPartitionQuery = false; string enableCrossPartitionQueryHeader = request.Headers[HttpConstants.HttpHeaders.EnableCrossPartitionQuery]; if (enableCrossPartitionQueryHeader != null) { if (!bool.TryParse(enableCrossPartitionQueryHeader, out enableCrossPartitionQuery)) { throw new BadRequestException( string.Format( CultureInfo.InvariantCulture, RMResources.InvalidHeaderValue, enableCrossPartitionQueryHeader, HttpConstants.HttpHeaders.EnableCrossPartitionQuery)); } } IReadOnlyList <Range <string> > providedRanges; if (!this.providedRangesCache.TryGetValue(collection.ResourceId, out providedRanges)) { if (this.ShouldExecuteQueryRequest) { QueryInfo queryInfo; providedRanges = PartitionRoutingHelper.GetProvidedPartitionKeyRanges( this.QuerySpec, enableCrossPartitionQuery, false, isContinuationExpected, collection.PartitionKey, queryPartitionProvider, version, out queryInfo); } else { providedRanges = new List <Range <string> > { new Range <string>( PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, true, false) }; } this.providedRangesCache[collection.ResourceId] = providedRanges; } PartitionRoutingHelper.ResolvedRangeInfo resolvedRangeInfo = await this.partitionRoutingHelper.TryGetTargetRangeFromContinuationTokenRange( providedRanges, routingMapProvider, collection.ResourceId, rangeFromContinuationToken, suppliedTokens); if (resolvedRangeInfo.ResolvedRange == null) { return(null); } else { return(Tuple.Create(resolvedRangeInfo, providedRanges)); } }
private async Task <FeedResponse <CosmosElement> > ExecuteOnceAsync(IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) { // Don't reuse request, as the rest of client SDK doesn't reuse requests between retries. // The code leaves some temporary garbage in request (in RequestContext etc.), // which shold be erased during retries. using (DocumentServiceRequest request = await this.CreateRequestAsync()) { if (!string.IsNullOrEmpty(request.Headers[HttpConstants.HttpHeaders.PartitionKey]) || !request.ResourceType.IsPartitioned()) { return(await this.ExecuteRequestAsync(request, cancellationToken)); } CollectionCache collectionCache = await this.Client.GetCollectionCacheAsync(); CosmosContainerSettings collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None); if (!string.IsNullOrEmpty(base.PartitionKeyRangeId)) { request.RouteTo(new PartitionKeyRangeIdentity(collection.ResourceId, base.PartitionKeyRangeId)); return(await this.ExecuteRequestAsync(request, cancellationToken)); } // For non-Windows platforms(like Linux and OSX) in .NET Core SDK, we cannot use ServiceInterop for parsing the query, // so forcing the request through Gateway. We are also now by-passing this for 32-bit host process in NETFX on Windows // as the ServiceInterop dll is only available in 64-bit. if (CustomTypeExtensions.ByPassQueryParsing()) { request.UseGatewayMode = true; return(await this.ExecuteRequestAsync(request, cancellationToken)); } QueryPartitionProvider queryPartitionProvider = await this.Client.GetQueryPartitionProviderAsync(cancellationToken); IRoutingMapProvider routingMapProvider = await this.Client.GetRoutingMapProviderAsync(); List <CompositeContinuationToken> suppliedTokens; Range <string> rangeFromContinuationToken = this.partitionRoutingHelper.ExtractPartitionKeyRangeFromContinuationToken(request.Headers, out suppliedTokens); Tuple <PartitionRoutingHelper.ResolvedRangeInfo, IReadOnlyList <Range <string> > > queryRoutingInfo = await this.TryGetTargetPartitionKeyRangeAsync( request, collection, queryPartitionProvider, routingMapProvider, rangeFromContinuationToken, suppliedTokens); if (request.IsNameBased && queryRoutingInfo == null) { request.ForceNameCacheRefresh = true; collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None); queryRoutingInfo = await this.TryGetTargetPartitionKeyRangeAsync( request, collection, queryPartitionProvider, routingMapProvider, rangeFromContinuationToken, suppliedTokens); } if (queryRoutingInfo == null) { throw new NotFoundException($"{DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}: Was not able to get queryRoutingInfo even after resolve collection async with force name cache refresh to the following collectionRid: {collection.ResourceId} with the supplied tokens: {JsonConvert.SerializeObject(suppliedTokens)}"); } request.RouteTo(new PartitionKeyRangeIdentity(collection.ResourceId, queryRoutingInfo.Item1.ResolvedRange.Id)); FeedResponse <CosmosElement> response = await this.ExecuteRequestLazyAsync(request, cancellationToken); if (!await this.partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync( response.Headers, providedPartitionKeyRanges: queryRoutingInfo.Item2, routingMapProvider: routingMapProvider, collectionRid: collection.ResourceId, resolvedRangeInfo: queryRoutingInfo.Item1)) { // Collection to which this request was resolved doesn't exist. // Retry policy will refresh the cache and return NotFound. throw new NotFoundException($"{DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}: Call to TryAddPartitionKeyRangeToContinuationTokenAsync failed to the following collectionRid: {collection.ResourceId} with the supplied tokens: {JsonConvert.SerializeObject(suppliedTokens)}"); } return(response); } }