Пример #1
0
        public async Task AddPartitionKeyRangeToContinuationTokenOnNotNullBackendContinuation()
        {
            ResolvedRangeInfo currentPartitionKeyRange = new ResolvedRangeInfo(new PartitionKeyRange {
                Id = "1", MinInclusive = "B", MaxExclusive = "C"
            }, null);
            Mock <IRoutingMapProvider> routingMapProvider = new Mock <IRoutingMapProvider>();

            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.IsAny <Range <string> >(),
                                         It.IsAny <bool>()
                                         )).Returns(Task.FromResult <IReadOnlyList <PartitionKeyRange> >(null)).Verifiable();

            PartitionRoutingHelper        partitionRoutingHelper = new PartitionRoutingHelper();
            DictionaryNameValueCollection headers = new DictionaryNameValueCollection();

            headers.Add(HttpConstants.HttpHeaders.Continuation, "something");
            bool result = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                null,
                routingMapProvider.Object,
                CollectionId,
                currentPartitionKeyRange,
                RntdbEnumerationDirection.Reverse
                );

            Assert.IsTrue(true);
            routingMapProvider.Verify(m => m.TryGetOverlappingRangesAsync(
                                          It.IsAny <string>(),
                                          It.IsAny <Range <string> >(),
                                          It.IsAny <bool>()
                                          ), Times.Never);
        }
Пример #2
0
 public DefaultDocumentQueryExecutionContext(
     IDocumentQueryClient client,
     ResourceType resourceTypeEnum,
     Type resourceType,
     Expression expression,
     FeedOptions feedOptions,
     string resourceLink,
     bool isContinuationExpected,
     Guid correlatedActivityId) :
     base(
         client,
         resourceTypeEnum,
         resourceType,
         expression,
         feedOptions,
         resourceLink,
         false,
         correlatedActivityId)
 {
     this.isContinuationExpected = isContinuationExpected;
     this.fetchSchedulingMetrics = new SchedulingStopwatch();
     this.fetchSchedulingMetrics.Ready();
     this.providedRangesCache            = new Dictionary <string, IReadOnlyList <Range <string> > >();
     this.fetchExecutionRangeAccumulator = new FetchExecutionRangeAccumulator(singlePartitionKeyId);
     this.retries = -1;
     this.partitionRoutingHelper = new PartitionRoutingHelper();
 }
 public PartitionKeyRangeHandler(CosmosClient client, PartitionRoutingHelper partitionRoutingHelper = null)
 {
     if (client == null)
     {
         throw new ArgumentNullException(nameof(client));
     }
     this.client = client;
     this.partitionRoutingHelper = partitionRoutingHelper ?? new PartitionRoutingHelper();
 }
        public async Task GetTargetRangeFromContinuationTokenNonExistentContainer()
        {
            List <Range <string> > providedRanges = new List <Range <string> > {
                new Range <string>(
                    PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey,
                    PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey,
                    isMinInclusive: true,
                    isMaxInclusive: false)
            };

            //Not empty
            Range <string> range = new Range <string>("A", "B", true, false);
            List <CompositeContinuationToken> suppliedTokens = new List <CompositeContinuationToken>
            {
                new CompositeContinuationToken {
                    Range = range
                }
            };

            IReadOnlyList <PartitionKeyRange> overlappingRanges = new List <PartitionKeyRange> {
                new PartitionKeyRange {
                    Id = "0", MinInclusive = "A", MaxExclusive = "B"
                },
                new PartitionKeyRange {
                    Id = "1", MinInclusive = "B", MaxExclusive = "C"
                }
            }.AsReadOnly();

            Mock <IRoutingMapProvider> routingMapProvider = new Mock <IRoutingMapProvider>();

            routingMapProvider
            .SetupSequence(m => m.TryGetOverlappingRangesAsync(
                               It.IsAny <string>(),
                               It.Is <Range <string> >(x => x.Min == range.Min),
                               It.IsAny <ITrace>(),
                               It.IsAny <bool>()))
            .Returns(Task.FromResult((IReadOnlyList <PartitionKeyRange>)overlappingRanges.Skip(1).ToList()))
            .Returns(Task.FromResult((IReadOnlyList <PartitionKeyRange>)null));

            PartitionRoutingHelper partitionRoutingHelper = new PartitionRoutingHelper();
            ResolvedRangeInfo      resolvedRangeInfo      = await partitionRoutingHelper.TryGetTargetRangeFromContinuationTokenRangeAsync(
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                range,
                suppliedTokens,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Reverse);

            Assert.IsNotNull(resolvedRangeInfo);
            Assert.IsNull(resolvedRangeInfo.ResolvedRange);
            Assert.IsNull(resolvedRangeInfo.ContinuationTokens);
        }
        public async Task AddPartitionKeyRangeToContinuationTokenOnSplit()
        {
            const string BackendToken = "backendToken";
            RequestNameValueCollection        headers = new();
            List <CompositeContinuationToken> compositeContinuationTokensFromSplit = new List <CompositeContinuationToken>
            {
                new CompositeContinuationToken {
                    Token = "someToken", Range = new Range <string>("A", "B", true, false)
                },
                new CompositeContinuationToken {
                    Token = "anotherToken", Range = new Range <string>("B", "C", true, false)
                }
            };

            PartitionRoutingHelper partitionRoutingHelper = new PartitionRoutingHelper();

            //With backend header
            headers.Add(HttpConstants.HttpHeaders.Continuation, BackendToken);
            ResolvedRangeInfo resolvedRangeInfo = new ResolvedRangeInfo(new PartitionKeyRange(), new List <CompositeContinuationToken>(compositeContinuationTokensFromSplit));
            bool result = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                null,
                null,
                null,
                resolvedRangeInfo,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Reverse);

            List <CompositeContinuationToken> compositeContinuationTokens = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(headers.Get(HttpConstants.HttpHeaders.Continuation));

            Assert.IsTrue(result);
            Assert.AreEqual(compositeContinuationTokensFromSplit.Count, compositeContinuationTokens.Count);
            Assert.AreEqual(BackendToken, compositeContinuationTokens.First().Token);
            Assert.AreNotEqual(BackendToken, compositeContinuationTokens.Last().Token);

            //Without backend header
            headers.Remove(HttpConstants.HttpHeaders.Continuation);
            resolvedRangeInfo = new ResolvedRangeInfo(new PartitionKeyRange(), new List <CompositeContinuationToken>(compositeContinuationTokensFromSplit));
            result            = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                null,
                null,
                null,
                resolvedRangeInfo,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Reverse);

            compositeContinuationTokens = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(headers.Get(HttpConstants.HttpHeaders.Continuation));
            Assert.IsTrue(result);
            Assert.IsTrue(compositeContinuationTokens.Count == compositeContinuationTokensFromSplit.Count - 1);
            Assert.AreEqual(compositeContinuationTokensFromSplit.Last().Token, compositeContinuationTokens.First().Token);
        }
 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();
 }
Пример #7
0
        public async Task GetTargetRangeFromContinuationTokenWhenNotEmpty()
        {
            List <Range <string> > providedRanges = new List <Range <string> > {
                new Range <string>(
                    PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey,
                    PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey,
                    isMinInclusive: true,
                    isMaxInclusive: false)
            };

            //Not empty
            Range <string> range = new Range <string>("A", "B", true, false);
            List <CompositeContinuationToken> suppliedTokens = new List <CompositeContinuationToken>
            {
                new CompositeContinuationToken {
                    Range = range
                }
            };

            IReadOnlyList <PartitionKeyRange> overlappingRanges = new List <PartitionKeyRange> {
                new PartitionKeyRange {
                    Id = "0", MinInclusive = "A", MaxExclusive = "B"
                },
                new PartitionKeyRange {
                    Id = "1", MinInclusive = "B", MaxExclusive = "C"
                }
            }.AsReadOnly();
            Mock <IRoutingMapProvider> routingMapProvider = new Mock <IRoutingMapProvider>();

            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.Is <Range <string> >(x => x.Min == range.Min),
                                         It.IsAny <bool>()
                                         )).Returns(Task.FromResult((IReadOnlyList <PartitionKeyRange>)overlappingRanges.Take(1).ToList())).Verifiable();

            //Reverse
            PartitionRoutingHelper partitionRoutingHelper = new PartitionRoutingHelper();
            ResolvedRangeInfo      resolvedRangeInfo      = await partitionRoutingHelper.TryGetTargetRangeFromContinuationTokenRangeAsync(
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                range,
                suppliedTokens,
                RntdbEnumerationDirection.Reverse);

            Assert.AreEqual(overlappingRanges.First().Id, resolvedRangeInfo.ResolvedRange.Id);
            CollectionAssert.AreEqual(suppliedTokens, resolvedRangeInfo.ContinuationTokens);
            routingMapProvider.Verify();
        }
        public CosmosGatewayQueryExecutionContext(
            CosmosQueryContext cosmosQueryContext)
        {
            if (cosmosQueryContext == null)
            {
                throw new ArgumentNullException(nameof(cosmosQueryContext));
            }

            this.queryContext           = cosmosQueryContext;
            this.fetchSchedulingMetrics = new SchedulingStopwatch();
            this.fetchSchedulingMetrics.Ready();
            this.fetchExecutionRangeAccumulator = new FetchExecutionRangeAccumulator();
            this.retries = -1;
            this.partitionRoutingHelper = new PartitionRoutingHelper();
        }
Пример #9
0
        public void CompositeContinuationTokenIsNotPassedToBackend()
        {
            Range <string>             expectedRange = new Range <string>("A", "B", true, false);
            string                     expectedToken = "someToken";
            CompositeContinuationToken compositeContinuationToken = new CompositeContinuationToken {
                Range = expectedRange, Token = expectedToken
            };
            string continuation = JsonConvert.SerializeObject(compositeContinuationToken);
            PartitionRoutingHelper        partitionRoutingHelper = new PartitionRoutingHelper();
            DictionaryNameValueCollection headers = new DictionaryNameValueCollection();

            headers.Add(HttpConstants.HttpHeaders.Continuation, continuation);
            Range <string> range = partitionRoutingHelper.ExtractPartitionKeyRangeFromContinuationToken(headers, out List <CompositeContinuationToken> compositeContinuationTokens);

            Assert.IsTrue(expectedRange.Equals(range));
            Assert.AreEqual(expectedToken, headers.Get(HttpConstants.HttpHeaders.Continuation)); //not a composite token
        }
        private async Task <Tuple <PartitionRoutingHelper.ResolvedRangeInfo, IReadOnlyList <Range <string> > > > TryGetTargetPartitionKeyRangeAsync(
            DocumentServiceRequest request,
            ContainerProperties 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;
                    if ((feedOptions.Properties != null) && feedOptions.Properties.TryGetValue(
                            DefaultDocumentQueryExecutionContext.InternalPartitionKeyDefinitionProperty,
                            out object partitionKeyDefinitionObject))
                    {
                        if (partitionKeyDefinitionObject is PartitionKeyDefinition definition)
                        {
                            partitionKeyDefinition = definition;
                        }
                        else
                        {
                            throw new ArgumentException(
                                      "partitionkeydefinition has invalid type",
                                      nameof(partitionKeyDefinitionObject));
                        }
                    }
                    else
                    {
                        partitionKeyDefinition = collection.PartitionKey;
                    }

                    providedRanges = PartitionRoutingHelper.GetProvidedPartitionKeyRanges(
                        (errorMessage) => new BadRequestException(errorMessage),
                        this.QuerySpec,
                        enableCrossPartitionQuery,
                        false,
                        this.isContinuationExpected,
                        false, //haslogicalpartitionkey
                        partitionKeyDefinition,
                        queryPartitionProvider,
                        version,
                        out QueryInfo 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));
            }
        }
 public PartitionRoutingHelperTest()
 {
     this.partitionRoutingHelper = new PartitionRoutingHelper();
 }
        public async Task AddPartitionKeyRangeToContinuationTokenOnBoundry()
        {
            List <Range <string> > providedRanges = new List <Range <string> > {
                new Range <string>(
                    "A",
                    "D",
                    isMinInclusive: true,
                    isMaxInclusive: false)
            };

            //Reverse
            ResolvedRangeInfo currentPartitionKeyRange = new ResolvedRangeInfo(new PartitionKeyRange {
                Id = "0", MinInclusive = "A", MaxExclusive = "B"
            }, null);
            IReadOnlyList <PartitionKeyRange> overlappingRanges = new List <PartitionKeyRange> {
                new PartitionKeyRange {
                    Id = "0", MinInclusive = "A", MaxExclusive = "B"
                },
            }.AsReadOnly();
            Mock <IRoutingMapProvider> routingMapProvider = new Mock <IRoutingMapProvider>();

            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.Is <Range <string> >(x => x.Min == providedRanges.Single().Min&& x.Max == providedRanges.Single().Max),
                                         It.IsAny <ITrace>(),
                                         It.Is <bool>(x => x == false)
                                         )).Returns(Task.FromResult(overlappingRanges)).Verifiable();

            PartitionRoutingHelper     partitionRoutingHelper = new PartitionRoutingHelper();
            RequestNameValueCollection headers = new();
            bool result = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                currentPartitionKeyRange,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Reverse
                );

            Assert.IsTrue(result);
            routingMapProvider.Verify();
            string expectedContinuationToken = JsonConvert.SerializeObject(new CompositeContinuationToken
            {
                Token = null,
                Range = overlappingRanges.First().ToRange(),
            });

            Assert.IsNull(headers.Get(HttpConstants.HttpHeaders.Continuation));

            //Forward
            currentPartitionKeyRange = new ResolvedRangeInfo(new PartitionKeyRange {
                Id = "0", MinInclusive = "A", MaxExclusive = "D"
            }, null);
            overlappingRanges = new List <PartitionKeyRange> {
                new PartitionKeyRange {
                    Id = "0", MinInclusive = "A", MaxExclusive = "D"
                },
            }.AsReadOnly();
            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.IsAny <Range <string> >(),
                                         It.IsAny <ITrace>(),
                                         It.IsAny <bool>()
                                         )).Returns(Task.FromResult(overlappingRanges));
            headers = new RequestNameValueCollection();

            result = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                currentPartitionKeyRange,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Forward
                );

            Assert.IsTrue(result);
            routingMapProvider.Verify(m => m.TryGetOverlappingRangesAsync(
                                          It.IsAny <string>(),
                                          It.Is <Range <string> >(e => e.IsMaxInclusive),
                                          It.IsAny <ITrace>(),
                                          It.IsAny <bool>()
                                          ), Times.Never);
            expectedContinuationToken = JsonConvert.SerializeObject(new CompositeContinuationToken
            {
                Token = null,
                Range = overlappingRanges.Last().ToRange(),
            });
            Assert.IsNull(headers.Get(HttpConstants.HttpHeaders.Continuation));
        }
        public async Task GetTargetRangeFromContinuationTokenOnSplit()
        {
            const string Token = "token";

            List <Range <string> > providedRanges = new List <Range <string> > {
                new Range <string>(
                    PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey,
                    PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey,
                    isMinInclusive: true,
                    isMaxInclusive: false)
            };

            Range <string> rangeFromContinuationToken        = new Range <string>("A", "C", true, false);
            List <CompositeContinuationToken> suppliedTokens = new List <CompositeContinuationToken>
            {
                new CompositeContinuationToken {
                    Token = Token, Range = rangeFromContinuationToken
                }
            };

            IReadOnlyList <PartitionKeyRange> overlappingRanges = new List <PartitionKeyRange> {
                new PartitionKeyRange {
                    Id = "2", MinInclusive = "A", MaxExclusive = "B"
                },
                new PartitionKeyRange {
                    Id = "3", MinInclusive = "B", MaxExclusive = "C"
                },
                new PartitionKeyRange {
                    Id = "1", MinInclusive = "C", MaxExclusive = "D"
                }
            }.AsReadOnly();
            IReadOnlyList <PartitionKeyRange> replacedRanges     = overlappingRanges.Take(2).ToList().AsReadOnly();
            Mock <IRoutingMapProvider>        routingMapProvider = new Mock <IRoutingMapProvider>();

            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.Is <Range <string> >(x => x.Min == rangeFromContinuationToken.Min),
                                         It.IsAny <ITrace>(),
                                         It.Is <bool>(x => x == false)
                                         )).Returns(Task.FromResult((IReadOnlyList <PartitionKeyRange>)overlappingRanges.Take(1).ToList())).Verifiable();
            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.Is <Range <string> >(x => x.Min == rangeFromContinuationToken.Min && x.Max == rangeFromContinuationToken.Max),
                                         It.IsAny <ITrace>(),
                                         It.Is <bool>(x => x == true)
                                         )).Returns(Task.FromResult(replacedRanges)).Verifiable();

            //Reverse
            PartitionRoutingHelper partitionRoutingHelper = new PartitionRoutingHelper();
            ResolvedRangeInfo      resolvedRangeInfo      = await partitionRoutingHelper.TryGetTargetRangeFromContinuationTokenRangeAsync(
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                rangeFromContinuationToken,
                suppliedTokens,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Reverse);

            routingMapProvider.Verify();
            Assert.IsTrue(replacedRanges.Last().Equals(resolvedRangeInfo.ResolvedRange));
            List <PartitionKeyRange> reversedReplacedRanges = new List <PartitionKeyRange>(replacedRanges);

            reversedReplacedRanges.Reverse();
            Assert.AreEqual(replacedRanges.Count, resolvedRangeInfo.ContinuationTokens.Count);
            Assert.AreEqual(resolvedRangeInfo.ContinuationTokens[0].Token, Token);

            for (int i = 0; i < resolvedRangeInfo.ContinuationTokens.Count; i++)
            {
                Assert.IsTrue(reversedReplacedRanges[i].ToRange().Equals(resolvedRangeInfo.ContinuationTokens[i].Range));
            }

            //Forward
            partitionRoutingHelper = new PartitionRoutingHelper();
            resolvedRangeInfo      = await partitionRoutingHelper.TryGetTargetRangeFromContinuationTokenRangeAsync(
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                rangeFromContinuationToken,
                suppliedTokens,
                NoOpTrace.Singleton,
                RntdbEnumerationDirection.Forward);

            routingMapProvider.Verify();
            Assert.IsTrue(replacedRanges.First().Equals(resolvedRangeInfo.ResolvedRange));
            Assert.AreEqual(replacedRanges.Count, resolvedRangeInfo.ContinuationTokens.Count);
            Assert.AreEqual(resolvedRangeInfo.ContinuationTokens[0].Token, Token);

            for (int i = 0; i < resolvedRangeInfo.ContinuationTokens.Count; i++)
            {
                Assert.IsTrue(replacedRanges[i].ToRange().Equals(resolvedRangeInfo.ContinuationTokens[i].Range));
            }
        }
Пример #14
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));
            }
        }
        public async Task AddPartitionKeyRangeToContinuationTokenOnNullBackendContinuation()
        {
            List <Range <string> > providedRanges = new List <Range <string> > {
                new Range <string>(
                    "A",
                    "D",
                    isMinInclusive: true,
                    isMaxInclusive: false)
            };
            ResolvedRangeInfo currentPartitionKeyRange = new ResolvedRangeInfo(new PartitionKeyRange {
                Id = "1", MinInclusive = "B", MaxExclusive = "C"
            }, null);

            IReadOnlyList <PartitionKeyRange> overlappingRanges = new List <PartitionKeyRange> {
                new PartitionKeyRange {
                    Id = "0", MinInclusive = "A", MaxExclusive = "B"
                },
                currentPartitionKeyRange.ResolvedRange,
                new PartitionKeyRange {
                    Id = "3", MinInclusive = "C", MaxExclusive = "D"
                }
            }.AsReadOnly();
            Mock <IRoutingMapProvider> routingMapProvider = new Mock <IRoutingMapProvider>();

            routingMapProvider.Setup(m => m.TryGetOverlappingRangesAsync(
                                         It.IsAny <string>(),
                                         It.Is <Range <string> >(x => x.Min == providedRanges.Single().Min&& x.Max == providedRanges.Single().Max),
                                         It.Is <bool>(x => x == false)
                                         )).Returns(Task.FromResult(overlappingRanges)).Verifiable();

            //Reverse
            PartitionRoutingHelper   partitionRoutingHelper = new PartitionRoutingHelper();
            StringKeyValueCollection headers = new StringKeyValueCollection();
            bool result = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                currentPartitionKeyRange,
                RntdbEnumerationDirection.Reverse
                );

            Assert.IsTrue(result);
            routingMapProvider.Verify();
            string expectedContinuationToken = JsonConvert.SerializeObject(new CompositeContinuationToken
            {
                Token = null,
                Range = overlappingRanges.First().ToRange(),
            });

            Assert.AreEqual(expectedContinuationToken, headers.Get(HttpConstants.HttpHeaders.Continuation));

            //Forward
            routingMapProvider.Setup(m => m.TryGetRangeByEffectivePartitionKey(
                                         It.IsAny <string>(),
                                         It.Is <string>(x => x == currentPartitionKeyRange.ResolvedRange.MaxExclusive)
                                         )).Returns(Task.FromResult(overlappingRanges.Last())).Verifiable();
            headers = new StringKeyValueCollection();
            result  = await partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
                headers,
                providedRanges,
                routingMapProvider.Object,
                CollectionId,
                currentPartitionKeyRange,
                RntdbEnumerationDirection.Forward
                );

            Assert.IsTrue(result);
            routingMapProvider.Verify();
            expectedContinuationToken = JsonConvert.SerializeObject(new CompositeContinuationToken
            {
                Token = null,
                Range = overlappingRanges.Last().ToRange(),
            });
            Assert.AreEqual(expectedContinuationToken, headers.Get(HttpConstants.HttpHeaders.Continuation));
        }