public void FeedRangeCompositeContinuation_ShouldRetry()
        {
            List <CompositeContinuationToken> compositeContinuationTokens = new List <CompositeContinuationToken>()
            {
                FeedRangeContinuationTests.BuildTokenForRange("A", "C", "token1"),
                FeedRangeContinuationTests.BuildTokenForRange("C", "F", "token2")
            };

            FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(Guid.NewGuid().ToString(), Mock.Of <FeedRangeInternal>(), compositeContinuationTokens);

            ResponseMessage okResponse = new ResponseMessage(HttpStatusCode.OK);

            okResponse.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1";
            okResponse.Headers[Documents.HttpConstants.HttpHeaders.ETag]      = "1";
            Assert.IsFalse(feedRangeCompositeContinuation.HandleChangeFeedNotModified(okResponse).ShouldRetry);

            ResponseMessage notModified = new ResponseMessage(HttpStatusCode.NotModified);

            notModified.Headers[Documents.HttpConstants.HttpHeaders.ETag] = "1";

            // A 304 on a multi Range token should cycle on all available ranges before stopping retrying
            Assert.IsTrue(feedRangeCompositeContinuation.HandleChangeFeedNotModified(notModified).ShouldRetry);
            Assert.IsTrue(feedRangeCompositeContinuation.HandleChangeFeedNotModified(notModified).ShouldRetry);
            Assert.IsFalse(feedRangeCompositeContinuation.HandleChangeFeedNotModified(notModified).ShouldRetry);
        }
        public async Task ChangeFeedIteratorCore_BreathFirst()
        {
            int expected = 500;
            List <CompositeContinuationToken> previousToken = null;

            await this.CreateRandomItems(this.LargerContainer, expected, randomPartitionKey : true);

            ContainerInternal      itemsCore    = this.LargerContainer;
            ChangeFeedIteratorCore feedIterator = itemsCore.GetChangeFeedStreamIterator(
                ChangeFeedStartFrom.Beginning(),
                new ChangeFeedRequestOptions()
            {
                PageSizeHint = 1,
            }) as ChangeFeedIteratorCore;

            while (true)
            {
                using (ResponseMessage responseMessage =
                           await feedIterator.ReadNextAsync(this.cancellationToken))
                {
                    Assert.IsTrue(FeedRangeCompositeContinuation.TryParse(responseMessage.ContinuationToken, out FeedRangeContinuation continuation));
                    FeedRangeCompositeContinuation    compositeContinuation = continuation as FeedRangeCompositeContinuation;
                    List <CompositeContinuationToken> deserializedToken     = compositeContinuation.CompositeContinuationTokens.ToList();
                    if (previousToken != null)
                    {
                        // Verify that the token, even though it yielded results, it moved to a new range
                        Assert.AreNotEqual(previousToken[0].Range.Min, deserializedToken[0].Range.Min);
                        Assert.AreNotEqual(previousToken[0].Range.Max, deserializedToken[0].Range.Max);
                        break;
                    }

                    previousToken = deserializedToken;
                }
            }
        }
        public void FeedRangeCompositeContinuation_IsDone_MultipleRanges()
        {
            const string containerRid            = "containerRid";
            FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(
                containerRid,
                Mock.Of <FeedRangeInternal>(),
                new List <Documents.Routing.Range <string> >()
            {
                new Documents.Routing.Range <string>("A", "B", true, false),
                new Documents.Routing.Range <string>("B", "C", true, false),
                new Documents.Routing.Range <string>("C", "D", true, false)
            }, continuation: null);

            // First range has continuation
            token.ReplaceContinuation(Guid.NewGuid().ToString());
            Assert.IsFalse(token.IsDone);

            // Second range is done
            token.ReplaceContinuation(null);
            Assert.IsFalse(token.IsDone);

            // Third range is done
            token.ReplaceContinuation(null);
            Assert.IsFalse(token.IsDone);

            // First range has continuation
            token.ReplaceContinuation(Guid.NewGuid().ToString());
            Assert.IsFalse(token.IsDone);

            // MoveNext should skip the second and third
            // Finish first one
            token.ReplaceContinuation(null);
            Assert.IsTrue(token.IsDone);
        }
        public async Task FeedRangeCompositeContinuation_HandleSplits_ReadFeed()
        {
            List <CompositeContinuationToken> compositeContinuationTokens = new List <CompositeContinuationToken>()
            {
                FeedRangeContinuationTests.BuildTokenForRange("A", "C", JsonConvert.SerializeObject(new CompositeContinuationToken()
                {
                    Token = "token1", Range = new Documents.Routing.Range <string>("A", "C", true, false)
                })),
                FeedRangeContinuationTests.BuildTokenForRange("C", "F", JsonConvert.SerializeObject(new CompositeContinuationToken()
                {
                    Token = "token2", Range = new Documents.Routing.Range <string>("C", "F", true, false)
                })),
            };

            FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(Guid.NewGuid().ToString(), Mock.Of <FeedRangeInternal>(), JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(JsonConvert.SerializeObject(compositeContinuationTokens)));

            MultiRangeMockDocumentClient documentClient = new MultiRangeMockDocumentClient();

            Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>();

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext.Setup(c => c.DocumentClient).Returns(documentClient);

            Mock <ContainerInternal> containerCore = new Mock <ContainerInternal>();

            containerCore
            .Setup(c => c.ClientContext).Returns(cosmosClientContext.Object);

            Assert.AreEqual(2, feedRangeCompositeContinuation.CompositeContinuationTokens.Count);

            ResponseMessage split = new ResponseMessage(HttpStatusCode.Gone);

            split.Headers.SubStatusCode = Documents.SubStatusCodes.PartitionKeyRangeGone;
            Assert.IsTrue((await feedRangeCompositeContinuation.HandleSplitAsync(containerCore.Object, split, default(CancellationToken))).ShouldRetry);

            // verify token state
            // Split should have updated initial and created a new token at the end
            Assert.AreEqual(3, feedRangeCompositeContinuation.CompositeContinuationTokens.Count);
            CompositeContinuationToken[] continuationTokens = feedRangeCompositeContinuation.CompositeContinuationTokens.ToArray();
            // First token is split
            Assert.AreEqual(JsonConvert.DeserializeObject <CompositeContinuationToken>(compositeContinuationTokens[0].Token).Range.Min, JsonConvert.DeserializeObject <CompositeContinuationToken>(continuationTokens[0].Token).Range.Min);
            Assert.AreEqual(JsonConvert.DeserializeObject <CompositeContinuationToken>(compositeContinuationTokens[0].Token).Token, JsonConvert.DeserializeObject <CompositeContinuationToken>(continuationTokens[0].Token).Token);
            Assert.AreEqual(documentClient.AvailablePartitionKeyRanges[0].MinInclusive, continuationTokens[0].Range.Min);
            Assert.AreEqual(documentClient.AvailablePartitionKeyRanges[0].MaxExclusive, continuationTokens[0].Range.Max);

            // Second token remains the same
            Assert.AreEqual(compositeContinuationTokens[1].Token, continuationTokens[1].Token);
            Assert.AreEqual(compositeContinuationTokens[1].Range.Min, continuationTokens[1].Range.Min);
            Assert.AreEqual(compositeContinuationTokens[1].Range.Max, continuationTokens[1].Range.Max);

            // New third token
            Assert.AreEqual(JsonConvert.DeserializeObject <CompositeContinuationToken>(compositeContinuationTokens[0].Token).Range.Max, JsonConvert.DeserializeObject <CompositeContinuationToken>(continuationTokens[2].Token).Range.Max);
            Assert.AreEqual(JsonConvert.DeserializeObject <CompositeContinuationToken>(compositeContinuationTokens[0].Token).Token, JsonConvert.DeserializeObject <CompositeContinuationToken>(continuationTokens[2].Token).Token);
            Assert.AreEqual(documentClient.AvailablePartitionKeyRanges[1].MinInclusive, continuationTokens[2].Range.Min);
            Assert.AreEqual(documentClient.AvailablePartitionKeyRanges[1].MaxExclusive, continuationTokens[2].Range.Max);
        }
Exemple #5
0
        public async Task ReadFeedIteratorCore_WithNoInitialState_ReadNextAsync()
        {
            string          continuation    = "TBD";
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK);

            responseMessage.Headers.ContinuationToken = continuation;
            responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1";
            responseMessage.Content = new MemoryStream(Encoding.UTF8.GetBytes("{}"));

            MultiRangeMockDocumentClient documentClient = new MultiRangeMockDocumentClient();

            Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>();

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext.Setup(c => c.DocumentClient).Returns(documentClient);
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <string>(),
                       It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Document),
                       It.IsAny <Documents.OperationType>(),
                       It.IsAny <RequestOptions>(),
                       It.IsAny <ContainerInternal>(),
                       It.IsAny <PartitionKey?>(),
                       It.IsAny <Stream>(),
                       It.IsAny <Action <RequestMessage> >(),
                       It.IsAny <CosmosDiagnosticsContext>(),
                       It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(responseMessage));

            ContainerInternal containerCore = Mock.Of <ContainerInternal>();

            Mock.Get(containerCore)
            .Setup(c => c.ClientContext)
            .Returns(cosmosClientContext.Object);
            Mock.Get(containerCore)
            .Setup(c => c.GetRIDAsync(It.IsAny <CancellationToken>()))
            .ReturnsAsync(Guid.NewGuid().ToString());

            FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(containerCore, null, null, new QueryRequestOptions());
            ResponseMessage       response          = await feedTokenIterator.ReadNextAsync();

            Assert.IsTrue(FeedRangeContinuation.TryParse(response.ContinuationToken, out FeedRangeContinuation parsedToken));
            FeedRangeCompositeContinuation feedRangeCompositeContinuation = parsedToken as FeedRangeCompositeContinuation;
            FeedRangeEPK feedTokenEPKRange = feedRangeCompositeContinuation.FeedRange as FeedRangeEPK;

            // Assert that a FeedToken for the entire range is used
            Assert.AreEqual(Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, feedTokenEPKRange.Range.Min);
            Assert.AreEqual(Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, feedTokenEPKRange.Range.Max);
            Assert.AreEqual(continuation, feedRangeCompositeContinuation.CompositeContinuationTokens.Peek().Token);
            Assert.IsFalse(feedRangeCompositeContinuation.IsDone);
        }
        public void FeedRangeCompositeContinuation_TryParse()
        {
            const string containerRid = "containerRid";
            List <Documents.Routing.Range <string> > keyRanges = new List <Documents.Routing.Range <string> >()
            {
                new Documents.Routing.Range <string>("A", "B", true, false),
                new Documents.Routing.Range <string>("D", "E", true, false),
            };
            FeedRangeInternal feedRangeInternal  = new FeedRangeEPK(new Documents.Routing.Range <string>("A", "E", true, false));
            FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(containerRid, feedRangeInternal, keyRanges);

            Assert.IsTrue(FeedRangeContinuation.TryParse(token.ToString(), out _));
            Assert.IsFalse(FeedRangeContinuation.TryParse("whatever", out _));
        }
        public void FeedRangeCompositeContinuation_IsDone()
        {
            const string containerRid            = "containerRid";
            FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(
                containerRid,
                Mock.Of <FeedRangeInternal>(),
                new List <Documents.Routing.Range <string> >()
            {
                new Documents.Routing.Range <string>("A", "B", true, false)
            }, continuation: Guid.NewGuid().ToString());

            Assert.IsFalse(token.IsDone);

            token.ReplaceContinuation(null);
            Assert.IsTrue(token.IsDone);
        }
Exemple #8
0
        public void ReadFeedIteratorCore_Create_WithFeedContinuation()
        {
            string       continuation = Guid.NewGuid().ToString();
            FeedRangeEPK feedRangeEPK = FeedRangeEPK.ForFullRange();
            FeedRangeCompositeContinuation feedRangeSimpleContinuation = new FeedRangeCompositeContinuation(Guid.NewGuid().ToString(), feedRangeEPK, new List <Documents.Routing.Range <string> >()
            {
                feedRangeEPK.Range
            }, continuation);
            FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(Mock.Of <ContainerInternal>(), null, feedRangeSimpleContinuation.ToString(), null);
            FeedRangeEPK          defaultRange      = feedTokenIterator.FeedRangeInternal as FeedRangeEPK;

            Assert.AreEqual(FeedRangeEPK.ForFullRange().Range.Min, defaultRange.Range.Min);
            Assert.AreEqual(FeedRangeEPK.ForFullRange().Range.Max, defaultRange.Range.Max);
            Assert.IsNotNull(feedTokenIterator.FeedRangeContinuation);
            Assert.AreEqual(continuation, feedTokenIterator.FeedRangeContinuation.GetContinuation());
        }
        public void FeedRangeCompositeContinuation_MoveToNextTokenCircles()
        {
            const string containerRid = "containerRid";
            List <Documents.Routing.Range <string> > keyRanges = new List <Documents.Routing.Range <string> >()
            {
                new Documents.Routing.Range <string>("A", "B", true, false),
                new Documents.Routing.Range <string>("D", "E", true, false),
            };
            FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(containerRid, Mock.Of <FeedRangeInternal>(), keyRanges);

            Assert.AreEqual(keyRanges[0].Min, token.CompositeContinuationTokens.Peek().Range.Min);
            token.ReplaceContinuation("something");
            Assert.AreEqual(keyRanges[1].Min, token.CompositeContinuationTokens.Peek().Range.Min);
            token.ReplaceContinuation("something");
            Assert.AreEqual(keyRanges[0].Min, token.CompositeContinuationTokens.Peek().Range.Min);
            token.ReplaceContinuation("something");
            Assert.AreEqual(keyRanges[1].Min, token.CompositeContinuationTokens.Peek().Range.Min);
        }
Exemple #10
0
        private void FeedRange_PartitionKey_Validate(PartitionKey partitionKey)
        {
            string continuationToken = "TBD";
            string containerRid      = Guid.NewGuid().ToString();
            FeedRangePartitionKey          feedTokenPartitionKey       = new FeedRangePartitionKey(partitionKey);
            FeedRangeCompositeContinuation feedRangeSimpleContinuation = new FeedRangeCompositeContinuation(containerRid, feedTokenPartitionKey, new List <Documents.Routing.Range <string> >()
            {
                Documents.Routing.Range <string> .GetEmptyRange("AA")
            }, continuationToken);
            string serialized = feedRangeSimpleContinuation.ToString();

            Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation deserialized));
            FeedRangeCompositeContinuation deserializedContinuation = deserialized as FeedRangeCompositeContinuation;
            FeedRangePartitionKey          deserializedFeedRange    = deserializedContinuation.FeedRange as FeedRangePartitionKey;

            Assert.AreEqual(feedTokenPartitionKey.PartitionKey.ToJsonString(), deserializedFeedRange.PartitionKey.ToJsonString());
            Assert.AreEqual(continuationToken, deserializedContinuation.GetContinuation());
        }
Exemple #11
0
        public async Task FeedRange_EPK_Serialization()
        {
            string           continuation   = "TBD";
            string           containerRid   = Guid.NewGuid().ToString();
            List <FeedRange> ranges         = (await this.Container.GetFeedRangesAsync()).ToList();
            List <string>    serializations = new List <string>();
            List <FeedRangeCompositeContinuation> tokens = new List <FeedRangeCompositeContinuation>();

            foreach (FeedRange range in ranges)
            {
                FeedRangeEpk feedRangeEPK = range as FeedRangeEpk;
                FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(containerRid, feedRangeEPK, new List <Documents.Routing.Range <string> >()
                {
                    feedRangeEPK.Range
                }, continuation);
                tokens.Add(feedRangeCompositeContinuation);
                serializations.Add(feedRangeCompositeContinuation.ToString());
            }

            List <FeedRangeContinuation> deserialized = new List <FeedRangeContinuation>();

            foreach (string serialized in serializations)
            {
                Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation token));
                deserialized.Add(token);
            }

            Assert.AreEqual(tokens.Count, deserialized.Count);

            for (int i = 0; i < tokens.Count; i++)
            {
                FeedRangeCompositeContinuation originalToken     = tokens[i] as FeedRangeCompositeContinuation;
                FeedRangeCompositeContinuation deserializedToken = deserialized[i] as FeedRangeCompositeContinuation;
                Assert.AreEqual(originalToken.GetContinuation(), deserializedToken.GetContinuation());
                Assert.AreEqual(originalToken.ContainerRid, deserializedToken.ContainerRid);
                Assert.AreEqual(originalToken.CompositeContinuationTokens.Count, deserializedToken.CompositeContinuationTokens.Count);
                Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Token, deserializedToken.CompositeContinuationTokens.Peek().Token);
                Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.Min, deserializedToken.CompositeContinuationTokens.Peek().Range.Min);
                Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.Max, deserializedToken.CompositeContinuationTokens.Peek().Range.Max);
                Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.IsMinInclusive, deserializedToken.CompositeContinuationTokens.Peek().Range.IsMinInclusive);
                Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.IsMaxInclusive, deserializedToken.CompositeContinuationTokens.Peek().Range.IsMaxInclusive);
            }
        }
        public void FeedRangeCompositeContinuation_RequestVisitor_IfEPKAlreadyExists()
        {
            const string containerRid = "containerRid";
            const string continuation = "continuation";
            string       epkString    = Guid.NewGuid().ToString();

            Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("A", "B", true, false);
            FeedRangeCompositeContinuation   token = new FeedRangeCompositeContinuation(containerRid, Mock.Of <FeedRangeInternal>(), new List <Documents.Routing.Range <string> >()
            {
                range
            }, continuation);
            RequestMessage   requestMessage   = new RequestMessage();
            FeedRangeVisitor feedRangeVisitor = new FeedRangeVisitor(requestMessage);

            requestMessage.Properties[HandlerConstants.StartEpkString] = epkString;
            requestMessage.Properties[HandlerConstants.EndEpkString]   = epkString;
            token.Accept(feedRangeVisitor, ChangeFeedRequestOptions.FillContinuationToken);
            Assert.AreEqual(epkString, requestMessage.Properties[HandlerConstants.StartEpkString]);
            Assert.AreEqual(epkString, requestMessage.Properties[HandlerConstants.EndEpkString]);
        }
        public void FeedRangeCompositeContinuation_RequestVisitor()
        {
            const string containerRid = "containerRid";
            const string continuation = "continuation";

            Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("A", "B", true, false);
            FeedRangeCompositeContinuation   token = new FeedRangeCompositeContinuation(containerRid, Mock.Of <FeedRangeInternal>(), new List <Documents.Routing.Range <string> >()
            {
                range
            }, continuation);
            RequestMessage requestMessage = new RequestMessage();

            requestMessage.OperationType = Documents.OperationType.ReadFeed;
            requestMessage.ResourceType  = Documents.ResourceType.Document;
            FeedRangeVisitor feedRangeVisitor = new FeedRangeVisitor(requestMessage);

            token.Accept(feedRangeVisitor, ChangeFeedRequestOptions.FillContinuationToken);
            Assert.AreEqual(range.Min, requestMessage.Properties[HandlerConstants.StartEpkString]);
            Assert.AreEqual(range.Max, requestMessage.Properties[HandlerConstants.EndEpkString]);
            Assert.AreEqual(continuation, requestMessage.Headers.IfNoneMatch);
            Assert.IsTrue(requestMessage.IsPartitionKeyRangeHandlerRequired);
        }
Exemple #14
0
        public async Task FeedRange_PKRangeId_Serialization()
        {
            string continuationToken = "TBD";
            string containerRid      = Guid.NewGuid().ToString();
            DocumentFeedResponse <Documents.PartitionKeyRange> ranges = await this.Container.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(this.Container.LinkUri);

            Documents.PartitionKeyRange oneRange = ranges.First();

            FeedRangePartitionKeyRange     original = new FeedRangePartitionKeyRange(oneRange.Id);
            FeedRangeCompositeContinuation feedRangeSimpleContinuation = new FeedRangeCompositeContinuation(containerRid, original, new List <Documents.Routing.Range <string> >()
            {
                oneRange.ToRange()
            }, continuationToken);
            string serialized = feedRangeSimpleContinuation.ToString();

            Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation feedRangeContinuation));
            FeedRangeCompositeContinuation deserialized          = feedRangeContinuation as FeedRangeCompositeContinuation;
            FeedRangePartitionKeyRange     deserializedFeedRange = deserialized.FeedRange as FeedRangePartitionKeyRange;

            Assert.IsNotNull(deserialized, "Error deserializing to FeedRangePartitionKeyRange");
            Assert.AreEqual(original.PartitionKeyRangeId, deserializedFeedRange.PartitionKeyRangeId);
            Assert.AreEqual(continuationToken, deserialized.GetContinuation());
        }
Exemple #15
0
        public override async Task <ResponseMessage> ReadNextAsync(CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            TryCatch <CrossPartitionChangeFeedAsyncEnumerator> monadicEnumerator = await this.lazyMonadicEnumerator.GetValueAsync(cancellationToken);

            if (monadicEnumerator.Failed)
            {
                Exception       createException = monadicEnumerator.Exception;
                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(createException);
                return(new ResponseMessage(
                           cosmosException.StatusCode,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           diagnostics: new CosmosDiagnosticsContextCore()));
            }

            CrossPartitionChangeFeedAsyncEnumerator enumerator = monadicEnumerator.Result;

            if (!await enumerator.MoveNextAsync())
            {
                throw new InvalidOperationException("ChangeFeed enumerator should always have a next continuation");
            }

            if (enumerator.Current.Failed)
            {
                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(enumerator.Current.Exception);
                if (!IsRetriableException(cosmosException))
                {
                    this.hasMoreResults = false;
                }

                return(new ResponseMessage(
                           cosmosException.StatusCode,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           diagnostics: new CosmosDiagnosticsContextCore()));
            }

            CrossFeedRangePage <Pagination.ChangeFeedPage, ChangeFeedState> crossFeedRangePage = enumerator.Current.Result;

            Pagination.ChangeFeedPage changeFeedPage = crossFeedRangePage.Page;
            ResponseMessage           responseMessage;

            if (changeFeedPage is Pagination.ChangeFeedSuccessPage changeFeedSuccessPage)
            {
                responseMessage = new ResponseMessage(statusCode: System.Net.HttpStatusCode.OK)
                {
                    Content = changeFeedSuccessPage.Content
                };
            }
            else
            {
                responseMessage = new ResponseMessage(statusCode: System.Net.HttpStatusCode.NotModified);
            }

            CrossFeedRangeState <ChangeFeedState> crossFeedRangeState = crossFeedRangePage.State;
            string continuationToken;

            if (this.changeFeedRequestOptions.EmitOldContinuationToken)
            {
                List <CompositeContinuationToken> compositeContinuationTokens = new List <CompositeContinuationToken>();
                for (int i = 0; i < crossFeedRangeState.Value.Length; i++)
                {
                    FeedRangeState <ChangeFeedState> changeFeedFeedRangeState = crossFeedRangeState.Value.Span[i];
                    string token = changeFeedFeedRangeState.State is ChangeFeedStateContinuation changeFeedStateContinuation ? ((CosmosString)changeFeedStateContinuation.ContinuationToken).Value : null;
                    Documents.Routing.Range <string> range = ((FeedRangeEpk)changeFeedFeedRangeState.FeedRange).Range;
                    CompositeContinuationToken       compositeContinuationToken = new CompositeContinuationToken()
                    {
                        Range = range,
                        Token = token,
                    };

                    compositeContinuationTokens.Add(compositeContinuationToken);
                }

                FeedRangeCompositeContinuation feedRangeCompositeContinuationToken = new FeedRangeCompositeContinuation(
                    await this.documentContainer.GetResourceIdentifierAsync(cancellationToken),
                    FeedRangeEpk.FullRange,
                    compositeContinuationTokens);

                continuationToken = feedRangeCompositeContinuationToken.ToString();
            }
            else
            {
                ChangeFeedCrossFeedRangeState changeFeedCrossFeedRangeState = new ChangeFeedCrossFeedRangeState(crossFeedRangeState.Value);
                continuationToken = VersionedAndRidCheckedCompositeToken.ToCosmosElement(
                    new VersionedAndRidCheckedCompositeToken(
                        VersionedAndRidCheckedCompositeToken.Version.V2,
                        changeFeedCrossFeedRangeState.ToCosmosElement(),
                        await this.documentContainer.GetResourceIdentifierAsync(cancellationToken))).ToString();
            }

            responseMessage.Headers.ContinuationToken = continuationToken;
            responseMessage.Headers.RequestCharge     = changeFeedPage.RequestCharge;
            responseMessage.Headers.ActivityId        = changeFeedPage.ActivityId;

            return(responseMessage);
        }
        public override async Task <ResponseMessage> ReadNextAsync(
            ITrace trace,
            CancellationToken cancellationToken = default)
        {
            if (trace == null)
            {
                throw new ArgumentNullException(nameof(trace));
            }

            cancellationToken.ThrowIfCancellationRequested();

            if (!this.hasMoreResults)
            {
                throw new InvalidOperationException("Should not be calling FeedIterator that does not have any more results");
            }

            if (this.monadicEnumerator.Failed)
            {
                this.hasMoreResults = false;

                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(this.monadicEnumerator.Exception);
                return(new ResponseMessage(
                           statusCode: System.Net.HttpStatusCode.BadRequest,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           trace: trace));
            }

            CrossPartitionReadFeedAsyncEnumerator enumerator = this.monadicEnumerator.Result;
            TryCatch <CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> > monadicPage;

            try
            {
                if (!await enumerator.MoveNextAsync(trace))
                {
                    throw new InvalidOperationException("Should not be calling enumerator that does not have any more results");
                }

                monadicPage = enumerator.Current;
            }
            catch (Exception ex)
            {
                monadicPage = TryCatch <CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> > .FromException(ex);
            }

            if (monadicPage.Failed)
            {
                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(monadicPage.Exception);
                if (!IsRetriableException(cosmosException))
                {
                    this.hasMoreResults = false;
                }

                return(new ResponseMessage(
                           statusCode: cosmosException.StatusCode,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           trace: trace));
            }

            CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> crossFeedRangePage = monadicPage.Result;

            if (crossFeedRangePage.State == default)
            {
                this.hasMoreResults = false;
            }

            // Make the continuation token match the older format:
            string continuationToken;

            if (crossFeedRangePage.State != null)
            {
                List <CompositeContinuationToken>   compositeContinuationTokens = new List <CompositeContinuationToken>();
                CrossFeedRangeState <ReadFeedState> crossFeedRangeState         = crossFeedRangePage.State;
                for (int i = 0; i < crossFeedRangeState.Value.Length; i++)
                {
                    FeedRangeState <ReadFeedState> feedRangeState = crossFeedRangeState.Value.Span[i];
                    FeedRangeEpk feedRange;
                    if (feedRangeState.FeedRange is FeedRangeEpk feedRangeEpk)
                    {
                        feedRange = feedRangeEpk;
                    }
                    else
                    {
                        feedRange = FeedRangeEpk.FullRange;
                    }

                    ReadFeedState readFeedState = feedRangeState.State;
                    CompositeContinuationToken compositeContinuationToken = new CompositeContinuationToken()
                    {
                        Range = feedRange.Range,
                        Token = readFeedState is ReadFeedBeginningState ? null : ((ReadFeedContinuationState)readFeedState).ContinuationToken.ToString(),
                    };

                    compositeContinuationTokens.Add(compositeContinuationToken);
                }

                FeedRangeInternal outerFeedRange;
                if ((this.queryRequestOptions != null) && this.queryRequestOptions.PartitionKey.HasValue)
                {
                    outerFeedRange = new FeedRangePartitionKey(this.queryRequestOptions.PartitionKey.Value);
                }
                else if ((this.queryRequestOptions != null) && (this.queryRequestOptions.FeedRange != null))
                {
                    outerFeedRange = (FeedRangeInternal)this.queryRequestOptions.FeedRange;
                }
                else
                {
                    outerFeedRange = FeedRangeEpk.FullRange;
                }

                FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(
                    containerRid: string.Empty,
                    feedRange: outerFeedRange,
                    compositeContinuationTokens);

                continuationToken = feedRangeCompositeContinuation.ToString();
            }
            else
            {
                continuationToken = null;
            }

            Pagination.ReadFeedPage page = crossFeedRangePage.Page;
            Headers headers = new Headers()
            {
                RequestCharge     = page.RequestCharge,
                ActivityId        = page.ActivityId,
                ContinuationToken = continuationToken,
            };

            foreach (KeyValuePair <string, string> kvp in page.AdditionalHeaders)
            {
                headers[kvp.Key] = kvp.Value;
            }

            return(new ResponseMessage(
                       statusCode: System.Net.HttpStatusCode.OK,
                       requestMessage: default,
        public ReadFeedIteratorCore(
            IDocumentContainer documentContainer,
            string continuationToken,
            ReadFeedPaginationOptions readFeedPaginationOptions,
            QueryRequestOptions queryRequestOptions,
            CancellationToken cancellationToken)
        {
            this.queryRequestOptions = queryRequestOptions;
            readFeedPaginationOptions ??= ReadFeedPaginationOptions.Default;

            if (!string.IsNullOrEmpty(continuationToken))
            {
                bool isNewArrayFormat = (continuationToken.Length >= 2) && (continuationToken[0] == '[') && (continuationToken[continuationToken.Length - 1] == ']');
                if (!isNewArrayFormat)
                {
                    // One of the two older formats
                    if (!FeedRangeContinuation.TryParse(continuationToken, out FeedRangeContinuation feedRangeContinuation))
                    {
                        // Backward compatible with old format
                        feedRangeContinuation = new FeedRangeCompositeContinuation(
                            containerRid: string.Empty,
                            FeedRangeEpk.FullRange,
                            new List <Documents.Routing.Range <string> >()
                        {
                            new Documents.Routing.Range <string>(
                                Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey,
                                Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey,
                                isMinInclusive: true,
                                isMaxInclusive: false)
                        },
                            continuationToken);
                    }

                    // need to massage it a little
                    List <CosmosElement> feedRangeStates = new List <CosmosElement>();
                    string oldContinuationFormat         = feedRangeContinuation.ToString();
                    if (feedRangeContinuation.FeedRange is FeedRangePartitionKey feedRangePartitionKey)
                    {
                        CosmosObject cosmosObject  = CosmosObject.Parse(oldContinuationFormat);
                        CosmosArray  continuations = (CosmosArray)cosmosObject["Continuation"];
                        if (continuations.Count != 1)
                        {
                            throw new InvalidOperationException("Expected only one continuation for partition key queries");
                        }

                        CosmosElement continuation       = continuations[0];
                        CosmosObject  continuationObject = (CosmosObject)continuation;
                        CosmosElement token = continuationObject["token"];
                        ReadFeedState state;
                        if (token is CosmosNull)
                        {
                            state = ReadFeedState.Beginning();
                        }
                        else
                        {
                            CosmosString tokenAsString = (CosmosString)token;
                            state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value));
                        }

                        FeedRangeState <ReadFeedState> feedRangeState = new FeedRangeState <ReadFeedState>(feedRangePartitionKey, state);
                        feedRangeStates.Add(ReadFeedFeedRangeStateSerializer.ToCosmosElement(feedRangeState));
                    }
                    else
                    {
                        CosmosObject cosmosObject  = CosmosObject.Parse(oldContinuationFormat);
                        CosmosArray  continuations = (CosmosArray)cosmosObject["Continuation"];

                        foreach (CosmosElement continuation in continuations)
                        {
                            CosmosObject  continuationObject = (CosmosObject)continuation;
                            CosmosObject  rangeObject        = (CosmosObject)continuationObject["range"];
                            string        min   = ((CosmosString)rangeObject["min"]).Value;
                            string        max   = ((CosmosString)rangeObject["max"]).Value;
                            CosmosElement token = continuationObject["token"];

                            FeedRangeInternal feedRange = new FeedRangeEpk(new Documents.Routing.Range <string>(min, max, isMinInclusive: true, isMaxInclusive: false));
                            ReadFeedState     state;
                            if (token is CosmosNull)
                            {
                                state = ReadFeedState.Beginning();
                            }
                            else
                            {
                                CosmosString tokenAsString = (CosmosString)token;
                                state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value));
                            }

                            FeedRangeState <ReadFeedState> feedRangeState = new FeedRangeState <ReadFeedState>(feedRange, state);
                            feedRangeStates.Add(ReadFeedFeedRangeStateSerializer.ToCosmosElement(feedRangeState));
                        }
                    }

                    CosmosArray cosmosArrayContinuationTokens = CosmosArray.Create(feedRangeStates);
                    continuationToken = cosmosArrayContinuationTokens.ToString();
                }
            }

            TryCatch <ReadFeedCrossFeedRangeState> monadicReadFeedState;

            if (continuationToken == null)
            {
                FeedRange feedRange;
                if ((this.queryRequestOptions != null) && this.queryRequestOptions.PartitionKey.HasValue)
                {
                    feedRange = new FeedRangePartitionKey(this.queryRequestOptions.PartitionKey.Value);
                }
                else if ((this.queryRequestOptions != null) && (this.queryRequestOptions.FeedRange != null))
                {
                    feedRange = this.queryRequestOptions.FeedRange;
                }
                else
                {
                    feedRange = FeedRangeEpk.FullRange;
                }

                monadicReadFeedState = TryCatch <ReadFeedCrossFeedRangeState> .FromResult(ReadFeedCrossFeedRangeState.CreateFromBeginning(feedRange));
            }
            else
            {
                monadicReadFeedState = ReadFeedCrossFeedRangeState.Monadic.Parse(continuationToken);
            }

            if (monadicReadFeedState.Failed)
            {
                this.monadicEnumerator = TryCatch <CrossPartitionReadFeedAsyncEnumerator> .FromException(monadicReadFeedState.Exception);
            }
            else
            {
                this.monadicEnumerator = TryCatch <CrossPartitionReadFeedAsyncEnumerator> .FromResult(
                    CrossPartitionReadFeedAsyncEnumerator.Create(
                        documentContainer,
                        new CrossFeedRangeState <ReadFeedState>(monadicReadFeedState.Result.FeedRangeStates),
                        readFeedPaginationOptions,
                        cancellationToken));
            }

            this.hasMoreResults = true;
        }