public ChangeFeedIteratorCore(
            ContainerInternal container,
            ChangeFeedStartFrom changeFeedStartFrom,
            ChangeFeedRequestOptions changeFeedRequestOptions)
        {
            this.container         = container ?? throw new ArgumentNullException(nameof(container));
            this.clientContext     = container.ClientContext;
            this.changeFeedOptions = changeFeedRequestOptions ?? new ChangeFeedRequestOptions();
            this.lazyContainerRid  = new AsyncLazy <TryCatch <string> >(valueFactory: (innerCancellationToken) =>
            {
                return(this.TryInitializeContainerRIdAsync(innerCancellationToken));
            });
            this.hasMoreResults = true;

            this.changeFeedStartFrom = changeFeedStartFrom;
            if (this.changeFeedStartFrom is ChangeFeedStartFromContinuation startFromContinuation)
            {
                if (!FeedRangeContinuation.TryParse(startFromContinuation.Continuation, out FeedRangeContinuation feedRangeContinuation))
                {
                    throw new ArgumentException(string.Format(ClientResources.FeedToken_UnknownFormat, startFromContinuation.Continuation));
                }

                this.FeedRangeContinuation = feedRangeContinuation;
                FeedRange feedRange = feedRangeContinuation.GetFeedRange();
                string    etag      = feedRangeContinuation.GetContinuation();

                this.changeFeedStartFrom = new ChangeFeedStartFromContinuationAndFeedRange(etag, (FeedRangeInternal)feedRange);
            }
        }
        public async Task ReadFeedIteratorCore_ReadNextAsync_Conflicts_Query()
        {
            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("{}"));

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

            cosmosClientContext.Setup(c => c.SerializerCore).Returns(new CosmosSerializerCore());
            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <Uri>(),
                       It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Conflict),
                       It.Is <Documents.OperationType>(ot => ot == Documents.OperationType.Query),
                       It.IsAny <RequestOptions>(),
                       It.IsAny <ContainerInternal>(),
                       It.IsAny <PartitionKey?>(),
                       It.Is <Stream>(stream => stream != null),
                       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);
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.GetContinuation())
            .Returns(continuation);
            Mock.Get(feedToken)
            .Setup(f => f.IsDone)
            .Returns(true);

            FeedRangeIteratorCore feedTokenIterator = new FeedRangeIteratorCore(containerCore, feedToken, new QueryRequestOptions(), Documents.ResourceType.Conflict, queryDefinition: new QueryDefinition("select * from c"));
            ResponseMessage       response          = await feedTokenIterator.ReadNextAsync();

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
        }
        public async Task ChangeFeedIteratorCore_ReadNextAsync()
        {
            string          continuation    = "TBD";
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK);

            responseMessage.Headers.ETag = continuation;
            responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1";

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

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <Uri>(),
                       It.IsAny <Documents.ResourceType>(),
                       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);

            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >()));
            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()))
            .Returns(Documents.ShouldRetryResult.NoRetry());

            ChangeFeedIteratorCore changeFeedIteratorCore = new ChangeFeedIteratorCore(containerCore, feedToken, null);
            ResponseMessage        response = await changeFeedIteratorCore.ReadNextAsync();

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
            Mock.Get(feedToken)
            .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once);
        }
        public async Task ChangeFeedIteratorCore_DoesNotUpdateContinuation_OnError()
        {
            string          continuation    = "TBD";
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.Gone);

            responseMessage.Headers.ETag = continuation;

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

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <string>(),
                       It.IsAny <Documents.ResourceType>(),
                       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);
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()))
            .Returns(Documents.ShouldRetryResult.NoRetry());

            ChangeFeedIteratorCore changeFeedIteratorCore = CreateWithCustomFeedToken(containerCore, feedToken);
            ResponseMessage        response = await changeFeedIteratorCore.ReadNextAsync();

            Assert.IsFalse(changeFeedIteratorCore.HasMoreResults);

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Never);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
            Mock.Get(feedToken)
            .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once);
        }
        public async Task ChangeFeedIteratorCore_HandlesSplitsThroughPipeline()
        {
            int executionCount = 0;
            CosmosClientContext cosmosClientContext = GetMockedClientContext((RequestMessage requestMessage, CancellationToken cancellationToken) =>
            {
                // Force OnBeforeRequestActions call
                requestMessage.ToDocumentServiceRequest();
                if (executionCount++ == 0)
                {
                    return(TestHandler.ReturnStatusCode(HttpStatusCode.Gone, Documents.SubStatusCodes.PartitionKeyRangeGone));
                }

                return(TestHandler.ReturnStatusCode(HttpStatusCode.OK));
            });

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

            Mock.Get(containerCore)
            .Setup(c => c.ClientContext)
            .Returns(cosmosClientContext);
            Mock.Get(containerCore)
            .Setup(c => c.LinkUri)
            .Returns(new Uri("https://dummy.documents.azure.com:443/dbs"));
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >()));
            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()))
            .Returns(Documents.ShouldRetryResult.NoRetry());

            ChangeFeedIteratorCore changeFeedIteratorCore = new ChangeFeedIteratorCore(containerCore, feedToken, null);

            ResponseMessage response = await changeFeedIteratorCore.ReadNextAsync();

            Assert.AreEqual(1, executionCount, "PartitionKeyRangeGoneRetryHandler handled the Split");
            Assert.AreEqual(HttpStatusCode.Gone, response.StatusCode);

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.IsAny <string>()), Times.Never);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
            Mock.Get(feedToken)
            .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once);
        }
Example #6
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 _));
        }
Example #8
0
        public async Task ReadFeedIteratorCore_HandlesSplitsThroughPipeline()
        {
            int executionCount = 0;
            CosmosClientContext cosmosClientContext = GetMockedClientContext((RequestMessage requestMessage, CancellationToken cancellationToken) =>
            {
                // Force OnBeforeRequestActions call
                requestMessage.ToDocumentServiceRequest();
                if (executionCount++ == 0)
                {
                    return(TestHandler.ReturnStatusCode(HttpStatusCode.Gone, Documents.SubStatusCodes.PartitionKeyRangeGone));
                }

                return(TestHandler.ReturnStatusCode(HttpStatusCode.OK));
            });

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

            Mock.Get(containerCore)
            .Setup(c => c.ClientContext)
            .Returns(cosmosClientContext);
            Mock.Get(containerCore)
            .Setup(c => c.LinkUri)
            .Returns("/dbs/db/colls/colls");
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));

            FeedRangeIteratorCore changeFeedIteratorCore = new FeedRangeIteratorCore(containerCore, feedToken, new QueryRequestOptions(), Documents.ResourceType.Document, queryDefinition: null);

            ResponseMessage response = await changeFeedIteratorCore.ReadNextAsync();

            Assert.AreEqual(1, executionCount, "Pipeline handled the Split");
            Assert.AreEqual(HttpStatusCode.Gone, response.StatusCode);
        }
        private static ChangeFeedIteratorCore CreateWithCustomFeedToken(
            ContainerInternal containerInternal,
            FeedRangeContinuation feedToken)
        {
            ChangeFeedIteratorCore changeFeedIteratorCore = new ChangeFeedIteratorCore(
                containerInternal,
                ChangeFeedStartFrom.Beginning(),
                changeFeedRequestOptions: default);

            System.Reflection.FieldInfo prop = changeFeedIteratorCore
                                               .GetType()
                                               .GetField(
                "FeedRangeContinuation",
                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            prop.SetValue(changeFeedIteratorCore, feedToken);

            return(changeFeedIteratorCore);
        }
Example #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());
        }
Example #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);
            }
        }
Example #12
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());
        }
Example #13
0
        public async Task ReadFeedIteratorCore_OfT_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("{}"));

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

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            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);
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.GetContinuation())
            .Returns(continuation);
            Mock.Get(feedToken)
            .Setup(f => f.IsDone)
            .Returns(true);

            FeedRangeIteratorCore feedTokenIterator = new FeedRangeIteratorCore(containerCore, feedToken, new QueryRequestOptions(), Documents.ResourceType.Document, queryDefinition: null);
            bool creatorCalled = false;
            Func <ResponseMessage, FeedResponse <dynamic> > creator = (ResponseMessage r) =>
            {
                creatorCalled = true;
                return(Mock.Of <FeedResponse <dynamic> >());
            };

            FeedIteratorCore <dynamic> feedTokenIteratorOfT = new FeedIteratorCore <dynamic>(feedTokenIterator, creator);
            FeedResponse <dynamic>     response             = await feedTokenIteratorOfT.ReadNextAsync();

            Assert.IsTrue(creatorCalled, "Response creator not called");
            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
        }
        public async Task ChangeFeedIteratorCore_OfT_ReadNextAsync()
        {
            string          continuation    = "TBD";
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK);

            responseMessage.Headers.ETag = continuation;
            responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1";

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

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <string>(),
                       It.IsAny <Documents.ResourceType>(),
                       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);
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()))
            .Returns(Documents.ShouldRetryResult.NoRetry());

            ChangeFeedIteratorCore changeFeedIteratorCore = CreateWithCustomFeedToken(containerCore, feedToken);

            bool creatorCalled = false;
            Func <ResponseMessage, FeedResponse <dynamic> > creator = (ResponseMessage r) =>
            {
                creatorCalled = true;
                return(Mock.Of <FeedResponse <dynamic> >());
            };

            FeedIteratorCore <dynamic> changeFeedIteratorCoreOfT = new FeedIteratorCore <dynamic>(changeFeedIteratorCore, creator);
            FeedResponse <dynamic>     response = await changeFeedIteratorCoreOfT.ReadNextAsync();

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
            Mock.Get(feedToken)
            .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once);

            Assert.IsTrue(creatorCalled, "Response creator not called");
        }
        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;
        }
Example #16
0
        public async Task ChangeFeed_FeedRange_FromV2SDK()
        {
            ContainerResponse largerContainer = await this.database.CreateContainerAsync(
                new ContainerProperties(id : Guid.NewGuid().ToString(), partitionKeyPath : "/status"),
                throughput : 20000,
                cancellationToken : this.cancellationToken);

            ContainerCore container = (ContainerInlineCore)largerContainer;

            int expected = 100;
            int count    = 0;

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

            IReadOnlyList <FeedRange> feedRanges = await container.GetFeedRangesAsync();

            List <string> continuations = new List <string>();

            // First do one request to construct the old model information based on Etag
            foreach (FeedRange feedRange in feedRanges)
            {
                IEnumerable <string> pkRangeIds = await container.GetPartitionKeyRangesAsync(feedRange);

                ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions()
                {
                    StartTime = DateTime.MinValue.ToUniversalTime(), MaxItemCount = 1
                };
                ChangeFeedIteratorCore feedIterator  = container.GetChangeFeedStreamIterator(feedRange: feedRange, changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore;
                ResponseMessage        firstResponse = await feedIterator.ReadNextAsync();

                if (firstResponse.IsSuccessStatusCode)
                {
                    Collection <ToDoActivity> response = TestCommon.SerializerCore.FromStream <CosmosFeedResponseUtil <ToDoActivity> >(firstResponse.Content).Data;
                    count += response.Count;
                }

                // Construct the continuation's range, using PKRangeId + ETag
                List <dynamic> ct = new List <dynamic>()
                {
                    new { min = string.Empty, max = string.Empty, token = firstResponse.Headers.ETag }
                };
                // Extract Etag and manually construct the continuation
                dynamic oldContinuation = new { V = 0, PKRangeId = pkRangeIds.First(), Continuation = ct };
                continuations.Add(JsonConvert.SerializeObject(oldContinuation));
            }

            // Now start the new iterators with the constructed continuations from migration
            foreach (string continuation in continuations)
            {
                ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions()
                {
                    MaxItemCount = 100
                };
                ChangeFeedIteratorCore feedIterator  = container.GetChangeFeedStreamIterator(continuationToken: continuation, changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore;
                ResponseMessage        firstResponse = await feedIterator.ReadNextAsync();

                if (firstResponse.IsSuccessStatusCode)
                {
                    Collection <ToDoActivity> response = TestCommon.SerializerCore.FromStream <CosmosFeedResponseUtil <ToDoActivity> >(firstResponse.Content).Data;
                    count += response.Count;
                    string migratedContinuation = firstResponse.ContinuationToken;
                    Assert.IsTrue(FeedRangeContinuation.TryParse(migratedContinuation, out FeedRangeContinuation feedRangeContinuation));
                    Assert.IsTrue(feedRangeContinuation.FeedRange is FeedRangeEPK);
                }
            }

            Assert.AreEqual(expected, count);
        }
Example #17
0
        public async Task ChangeFeed_FeedRange_FromV0Token()
        {
            ContainerResponse largerContainer = await this.database.CreateContainerAsync(
                new ContainerProperties(id : Guid.NewGuid().ToString(), partitionKeyPath : "/status"),
                throughput : 20000,
                cancellationToken : this.cancellationToken);

            ContainerInternal container = (ContainerInlineCore)largerContainer;

            int expected = 100;
            int count    = 0;

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

            IReadOnlyList <FeedRange> feedRanges = await container.GetFeedRangesAsync();

            List <string> continuations = new List <string>();

            // First do one request to construct the old model information based on Etag
            foreach (FeedRange feedRange in feedRanges)
            {
                IEnumerable <string> pkRangeIds = await container.GetPartitionKeyRangesAsync(feedRange);

                ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions()
                {
                    PageSizeHint = 1
                };
                ChangeFeedIteratorCore feedIterator = container.GetChangeFeedStreamIterator(
                    changeFeedStartFrom: ChangeFeedStartFrom.Beginning(feedRange),
                    changeFeedMode: ChangeFeedMode.Incremental,
                    changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore;
                ResponseMessage firstResponse = await feedIterator.ReadNextAsync();

                FeedRangeEpk FeedRangeEpk = feedRange as FeedRangeEpk;

                // Construct the continuation's range, using PKRangeId + ETag
                List <dynamic> ct = new List <dynamic>()
                {
                    new
                    {
                        min   = FeedRangeEpk.Range.Min,
                        max   = FeedRangeEpk.Range.Max,
                        token = (string)null
                    }
                };

                // Extract Etag and manually construct the continuation
                dynamic oldContinuation = new
                {
                    V            = 0,
                    Rid          = await container.GetCachedRIDAsync(cancellationToken : this.cancellationToken),
                    Continuation = ct
                };
                continuations.Add(JsonConvert.SerializeObject(oldContinuation));
            }

            // Now start the new iterators with the constructed continuations from migration
            foreach (string continuation in continuations)
            {
                ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions()
                {
                    PageSizeHint             = 100,
                    EmitOldContinuationToken = true,
                };
                ChangeFeedIteratorCore feedIterator = container.GetChangeFeedStreamIterator(
                    changeFeedStartFrom: ChangeFeedStartFrom.ContinuationToken(continuation),
                    changeFeedMode: ChangeFeedMode.Incremental,
                    changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore;
                ResponseMessage firstResponse = await feedIterator.ReadNextAsync();

                if (firstResponse.IsSuccessStatusCode)
                {
                    Collection <ToDoActivity> response = TestCommon.SerializerCore.FromStream <CosmosFeedResponseUtil <ToDoActivity> >(firstResponse.Content).Data;
                    count += response.Count;
                    string migratedContinuation = firstResponse.ContinuationToken;
                    Assert.IsTrue(FeedRangeContinuation.TryParse(migratedContinuation, out FeedRangeContinuation feedRangeContinuation));
                    Assert.IsTrue(feedRangeContinuation.FeedRange is FeedRangeEpk);
                }
            }

            Assert.AreEqual(expected, count);
        }