/// <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();
 }
Exemple #5
0
 internal abstract Task <List <Documents.Routing.Range <string> > > GetEffectiveRangesAsync(
     IRoutingMapProvider routingMapProvider,
     string containerRid,
     Documents.PartitionKeyDefinition partitionKeyDefinition);
Exemple #6
0
 internal abstract Task <IEnumerable <string> > GetPartitionKeyRangesAsync(
     IRoutingMapProvider routingMapProvider,
     string containerRid,
     Documents.PartitionKeyDefinition partitionKeyDefinition,
     CancellationToken cancellationToken);
Exemple #7
0
        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));
            }
        }
Exemple #8
0
        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);
        }
Exemple #10
0
        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));
            }
        }
Exemple #11
0
        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);
            }
        }