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); }
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); }
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); }
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()); }
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); }
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()); }
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; }