internal static TryCatch <ChangeFeedCrossFeedRangeState> CreateFromCosmosElement(CosmosElement cosmosElement) { if (cosmosElement == null) { throw new ArgumentNullException(nameof(cosmosElement)); } if (!(cosmosElement is CosmosArray cosmosArray)) { return(TryCatch <ChangeFeedCrossFeedRangeState> .FromException( new FormatException( $"Expected array: {cosmosElement}"))); } List <FeedRangeState <ChangeFeedState> > changeFeedFeedRangeStates = new List <FeedRangeState <ChangeFeedState> >(capacity: cosmosArray.Count); foreach (CosmosElement arrayItem in cosmosArray) { TryCatch <FeedRangeState <ChangeFeedState> > monadicChangeFeedFeedRangeState = ChangeFeedFeedRangeStateSerializer.Monadic.CreateFromCosmosElement(arrayItem); if (monadicChangeFeedFeedRangeState.Failed) { return(TryCatch <ChangeFeedCrossFeedRangeState> .FromException(monadicChangeFeedFeedRangeState.Exception)); } changeFeedFeedRangeStates.Add(monadicChangeFeedFeedRangeState.Result); } ImmutableArray <FeedRangeState <ChangeFeedState> > feedRangeStates = changeFeedFeedRangeStates.ToImmutableArray(); ChangeFeedCrossFeedRangeState changeFeedCrossFeedRangeState = new ChangeFeedCrossFeedRangeState(feedRangeStates); return(TryCatch <ChangeFeedCrossFeedRangeState> .FromResult(changeFeedCrossFeedRangeState)); }
public async Task SerializeAndDeserializeContinuationToken() { int batchSize = 25; (int totalCount, ChangeFeedCrossFeedRangeState state)countAndState; IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromBeginning(), ChangeFeedMode.Incremental); await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, countAndState.totalCount); // Serialize the state and send it over the wire for your user to resume execution. string continuationToken = countAndState.state.ToString(); await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); // Deserialize the state that the user came back with to resume from. ChangeFeedCrossFeedRangeState state = ChangeFeedCrossFeedRangeState.Parse(continuationToken); asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable(state, ChangeFeedMode.Incremental); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, countAndState.totalCount); }
public static ChangeFeedPage CreateNotModifiedPage( double requestCharge, string activityId, ChangeFeedCrossFeedRangeState state, ImmutableDictionary <string, string> additionalHeaders) { return(new ChangeFeedPage(CosmosArray.Empty, notModified: true, requestCharge, activityId, state, additionalHeaders)); }
public ChangeFeedCrossFeedRangeState Merge(ChangeFeedCrossFeedRangeState first) { Memory <FeedRangeState <ChangeFeedState> > mergedRange = CrossFeedRangeStateSplitterAndMerger.Merge <ChangeFeedState>( this.FeedRangeStates, first.FeedRangeStates); return(new ChangeFeedCrossFeedRangeState(mergedRange)); }
public ChangeFeedCrossFeedRangeAsyncEnumerable( IDocumentContainer documentContainer, ChangeFeedRequestOptions changeFeedRequestOptions, ChangeFeedCrossFeedRangeState state) { this.documentContainer = documentContainer ?? throw new ArgumentNullException(nameof(documentContainer)); this.changeFeedRequestOptions = changeFeedRequestOptions; this.state = state; }
public static ChangeFeedPage CreatePageWithChanges( CosmosArray documents, double requestCharge, string activityId, ChangeFeedCrossFeedRangeState state, ImmutableDictionary <string, string> additionalHeaders) { return(new ChangeFeedPage(documents, notModified: false, requestCharge, activityId, state, additionalHeaders)); }
public ChangeFeedCrossFeedRangeAsyncEnumerable( IDocumentContainer documentContainer, ChangeFeedCrossFeedRangeState state, ChangeFeedPaginationOptions changeFeedPaginationOptions, JsonSerializationFormatOptions jsonSerializationFormatOptions = null) { this.documentContainer = documentContainer ?? throw new ArgumentNullException(nameof(documentContainer)); this.changeFeedPaginationOptions = changeFeedPaginationOptions ?? ChangeFeedPaginationOptions.Default; this.state = state; this.jsonSerializationFormatOptions = jsonSerializationFormatOptions; }
private ChangeFeedPage( CosmosArray documents, bool notModified, double requestCharge, string activityId, ChangeFeedCrossFeedRangeState state) { this.Documents = documents ?? throw new ArgumentOutOfRangeException(nameof(documents)); this.NotModified = notModified; this.RequestCharge = requestCharge < 0 ? throw new ArgumentOutOfRangeException(nameof(requestCharge)) : requestCharge; this.ActivityId = activityId ?? throw new ArgumentNullException(nameof(activityId)); this.State = state; }
public async Task TestCancellationToken() { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.Cancel(); IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromBeginning()); await foreach (TryCatch <ChangeFeedPage> monadicPage in asyncEnumerable.WithCancellation(cancellationTokenSource.Token)) { monadicPage.ThrowIfFailed(); } }
public async Task TargetMultipleLogicalPartitionKeys() { int batchSize = 25; string pkToRead1 = "pkToRead1"; string pkToRead2 = "pkToRead2"; string otherPK = "otherPK"; for (int i = 0; i < batchSize; i++) { await this.Container.CreateItemAsync(this.CreateRandomToDoActivity(pkToRead1)); } for (int i = 0; i < batchSize; i++) { await this.Container.CreateItemAsync(this.CreateRandomToDoActivity(pkToRead2)); } for (int i = 0; i < batchSize; i++) { await this.Container.CreateItemAsync(this.CreateRandomToDoActivity(otherPK)); } // Create one start state for each logical partition key. List <FeedRangeState <ChangeFeedState> > feedRangeStates = new List <FeedRangeState <ChangeFeedState> >(); IReadOnlyList <string> partitionKeysToTarget = new List <string>() { pkToRead1, pkToRead2 }; foreach (string partitionKeyToTarget in partitionKeysToTarget) { feedRangeStates.Add( new FeedRangeState <ChangeFeedState>( (FeedRangeInternal)FeedRange.FromPartitionKey( new Cosmos.PartitionKey(partitionKeyToTarget)), ChangeFeedState.Beginning())); } // Use the list composition property of the constructor to merge them in to a single state. ChangeFeedCrossFeedRangeState multipleLogicalPartitionKeyState = new ChangeFeedCrossFeedRangeState(feedRangeStates.ToImmutableArray()); IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable(multipleLogicalPartitionKeyState, ChangeFeedMode.Incremental); (int totalCount, ChangeFeedCrossFeedRangeState _) = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(2 * batchSize, totalCount); }
public bool TrySplit(out ChangeFeedCrossFeedRangeState first, out ChangeFeedCrossFeedRangeState second) { if (!CrossFeedRangeStateSplitterAndMerger.TrySplit( this.FeedRangeStates, out ReadOnlyMemory <FeedRangeState <ChangeFeedState> > firstRange, out ReadOnlyMemory <FeedRangeState <ChangeFeedState> > secondRange)) { first = default; second = default; return(false); } first = new ChangeFeedCrossFeedRangeState(firstRange); second = new ChangeFeedCrossFeedRangeState(secondRange); return(true); }
public static bool TryParse(string text, out ChangeFeedCrossFeedRangeState state) { if (text == null) { throw new ArgumentNullException(nameof(text)); } TryCatch <ChangeFeedCrossFeedRangeState> monadicParse = Monadic.Parse(text); if (monadicParse.Failed) { state = default; return(false); } state = monadicParse.Result; return(true); }
public async Task TestContentSerializationOptions() { { // Native format IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromBeginning(), ChangeFeedMode.Incremental, new ChangeFeedRequestOptions() { JsonSerializationFormatOptions = JsonSerializationFormatOptions.Create(JsonSerializationFormat.Binary) }); await foreach (TryCatch <ChangeFeedPage> monadicPage in asyncEnumerable) { monadicPage.ThrowIfFailed(); if (monadicPage.Result.NotModified) { break; } } } { // Custom format IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromBeginning(), ChangeFeedMode.Incremental, new ChangeFeedRequestOptions() { JsonSerializationFormatOptions = JsonSerializationFormatOptions.Create( JsonSerializationFormat.Binary, (content) => JsonNavigator.Create(content)) }); await foreach (TryCatch <ChangeFeedPage> monadicPage in asyncEnumerable) { monadicPage.ThrowIfFailed(); if (monadicPage.Result.NotModified) { break; } } } }
public async Task TestScaleUpAndScaleDown() { int batchSize = 25; await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); // Start draining as 1 iterator IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromBeginning(), ChangeFeedMode.Incremental); (int totalCount, ChangeFeedCrossFeedRangeState state) = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, totalCount); // Continue draining as two iterators if (!state.TrySplit(out ChangeFeedCrossFeedRangeState first, out ChangeFeedCrossFeedRangeState second)) { Assert.Fail("Failed to split"); } await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); IAsyncEnumerable <TryCatch <ChangeFeedPage> > leftEnumerable = this.Container.GetChangeFeedAsyncEnumerable(first, ChangeFeedMode.Incremental); (int leftTotalCount, ChangeFeedCrossFeedRangeState leftResumeState) = await PartialDrainAsync(leftEnumerable); IAsyncEnumerable <TryCatch <ChangeFeedPage> > rightEnumerable = this.Container.GetChangeFeedAsyncEnumerable(second, ChangeFeedMode.Incremental); (int rightTotalCount, ChangeFeedCrossFeedRangeState rightResumeState) = await PartialDrainAsync(rightEnumerable); Assert.AreEqual(batchSize, leftTotalCount + rightTotalCount); // Finish draining again as a single enumerator ChangeFeedCrossFeedRangeState mergedState = leftResumeState.Merge(rightResumeState); await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); IAsyncEnumerable <TryCatch <ChangeFeedPage> > mergedEnumerable = this.Container.GetChangeFeedAsyncEnumerable(mergedState, ChangeFeedMode.Incremental); (int mergedTotalCount, ChangeFeedCrossFeedRangeState _) = await PartialDrainAsync(mergedEnumerable); Assert.AreEqual(batchSize, mergedTotalCount); }
public async Task TestCustomRequestOptionsAsync() { IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromBeginning(), ChangeFeedMode.Incremental, new ChangeFeedRequestOptions() { Properties = new Dictionary <string, object>() { { HttpConstants.HttpHeaders.SessionToken, "AnInvalidSessionToken" } } }); await foreach (TryCatch <ChangeFeedPage> monadicPage in asyncEnumerable) { Assert.IsTrue(monadicPage.Failed); Assert.AreEqual(((CosmosException)monadicPage.InnerMostException).StatusCode, System.Net.HttpStatusCode.BadRequest); break; } }
public async ValueTask <bool> MoveNextAsync() { if (!await this.enumerator.MoveNextAsync()) { throw new InvalidOperationException("Change Feed should always be able to move next."); } TryCatch <CrossFeedRangePage <Pagination.ChangeFeedPage, ChangeFeedState> > monadicInnerChangeFeedPage = this.enumerator.Current; if (monadicInnerChangeFeedPage.Failed) { this.Current = TryCatch <ChangeFeedPage> .FromException(monadicInnerChangeFeedPage.Exception); return(true); } CrossFeedRangePage <Pagination.ChangeFeedPage, ChangeFeedState> innerChangeFeedPage = monadicInnerChangeFeedPage.Result; CrossFeedRangeState <ChangeFeedState> crossFeedRangeState = innerChangeFeedPage.State; ChangeFeedCrossFeedRangeState state = new ChangeFeedCrossFeedRangeState(crossFeedRangeState.Value); ChangeFeedPage page = innerChangeFeedPage.Page switch { Pagination.ChangeFeedSuccessPage successPage => ChangeFeedPage.CreatePageWithChanges( RestFeedResponseParser.ParseRestFeedResponse( successPage.Content, this.jsonSerializationFormatOptions), successPage.RequestCharge, successPage.ActivityId, state, successPage.AdditionalHeaders), Pagination.ChangeFeedNotModifiedPage notModifiedPage => ChangeFeedPage.CreateNotModifiedPage( notModifiedPage.RequestCharge, notModifiedPage.ActivityId, state, notModifiedPage.AdditionalHeaders), _ => throw new InvalidOperationException($"Unknown type: {innerChangeFeedPage.Page.GetType()}"), }; this.Current = TryCatch <ChangeFeedPage> .FromResult(page); return(true); }
public async Task StartFromTime() { int batchSize = 25; (int totalCount, ChangeFeedCrossFeedRangeState state)countAndState; IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromTime(DateTime.UtcNow.Subtract(TimeSpan.FromSeconds(1)))); await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, countAndState.totalCount); await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable(countAndState.state); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, countAndState.totalCount); }
private static async Task <(int, ChangeFeedCrossFeedRangeState)> PartialDrainAsync(IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable) { ChangeFeedCrossFeedRangeState state = default; int totalCount = 0; await foreach (TryCatch <ChangeFeedPage> monadicPage in asyncEnumerable) { monadicPage.ThrowIfFailed(); ChangeFeedPage page = monadicPage.Result; state = page.State; if (page.NotModified) { break; } totalCount += page.Documents.Count; } return(totalCount, state); }
public async ValueTask <bool> MoveNextAsync() { if (!await this.enumerator.MoveNextAsync()) { throw new InvalidOperationException("Change Feed should always be able to move next."); } TryCatch <CrossFeedRangePage <Pagination.ChangeFeedPage, ChangeFeedState> > monadicInnerChangeFeedPage = this.enumerator.Current; if (monadicInnerChangeFeedPage.Failed) { this.Current = TryCatch <ChangeFeedPage> .FromException(monadicInnerChangeFeedPage.Exception); return(true); } CrossFeedRangePage <Pagination.ChangeFeedPage, ChangeFeedState> innerChangeFeedPage = monadicInnerChangeFeedPage.Result; CrossFeedRangeState <ChangeFeedState> crossFeedRangeState = innerChangeFeedPage.State; ChangeFeedCrossFeedRangeState state = new ChangeFeedCrossFeedRangeState(crossFeedRangeState.Value); ChangeFeedPage page = innerChangeFeedPage.Page switch { Pagination.ChangeFeedSuccessPage successPage => ChangeFeedPage.CreatePageWithChanges( CosmosQueryClientCore.ParseElementsFromRestStream( successPage.Content, Documents.ResourceType.Document, cosmosSerializationOptions: null), successPage.RequestCharge, successPage.ActivityId, state), Pagination.ChangeFeedNotModifiedPage notModifiedPage => ChangeFeedPage.CreateNotModifiedPage( notModifiedPage.RequestCharge, notModifiedPage.ActivityId, state), _ => throw new InvalidOperationException($"Unknown type: {innerChangeFeedPage.Page.GetType()}"), }; this.Current = TryCatch <ChangeFeedPage> .FromResult(page); return(true); }
public async Task StartFromBeginning() { int batchSize = 25; await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); (int totalCount, ChangeFeedCrossFeedRangeState state)countAndState; IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromBeginning()); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, countAndState.totalCount); // Insert another batch of 25 and use the state from the first cycle await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable(countAndState.state); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, countAndState.totalCount); }
public async Task StartFromNow() { int batchSize = 25; (int totalCount, ChangeFeedCrossFeedRangeState state)countAndState; IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable( ChangeFeedCrossFeedRangeState.CreateFromNow(), ChangeFeedMode.Incremental); await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(0, countAndState.totalCount); await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable(countAndState.state, ChangeFeedMode.Incremental); countAndState = await PartialDrainAsync(asyncEnumerable); Assert.AreEqual(batchSize, countAndState.totalCount); }
public async Task ParallelizeAcrossFeedRanges() { int batchSize = 25; await this.CreateRandomItems(this.Container, batchSize, randomPartitionKey : true); // Create one start state for each physical partition. List <ChangeFeedCrossFeedRangeState> startStates = new List <ChangeFeedCrossFeedRangeState>(); IReadOnlyList <FeedRange> feedRanges = await this.Container.GetFeedRangesAsync(); foreach (FeedRange feedRange in feedRanges) { startStates.Add(ChangeFeedCrossFeedRangeState.CreateFromBeginning(feedRange)); } // Create an independant enumerable for each of those start states. List <IAsyncEnumerable <TryCatch <ChangeFeedPage> > > asyncEnumerables = new List <IAsyncEnumerable <TryCatch <ChangeFeedPage> > >(); foreach (ChangeFeedCrossFeedRangeState state in startStates) { IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable = this.Container.GetChangeFeedAsyncEnumerable(state, ChangeFeedMode.Incremental); asyncEnumerables.Add(asyncEnumerable); } int totalCount = 0; foreach (IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable in asyncEnumerables) { // This part can be done in parallel on the same machine or on different machines, // since they are independant enumerables. (int totalCount, ChangeFeedCrossFeedRangeState state)countAndState = await PartialDrainAsync(asyncEnumerable); totalCount += countAndState.totalCount; } Assert.AreEqual(batchSize, totalCount); }
private async Task <ResponseMessage> ReadNextInternalAsync(ITrace trace, CancellationToken cancellationToken = default) { if (trace == null) { throw new ArgumentNullException(nameof(trace)); } TryCatch <CrossPartitionChangeFeedAsyncEnumerator> monadicEnumerator = await this.lazyMonadicEnumerator.GetValueAsync(trace, cancellationToken); if (monadicEnumerator.Failed) { Exception createException = monadicEnumerator.Exception; if (!ExceptionToCosmosException.TryCreateFromException( createException, trace, out CosmosException cosmosException)) { // Initialization issue, there are no enumerators to invoke this.hasMoreResults = false; throw createException; } return(new ResponseMessage( cosmosException.StatusCode, requestMessage: null, headers: cosmosException.Headers, cosmosException: cosmosException, trace: trace)); } CrossPartitionChangeFeedAsyncEnumerator enumerator = monadicEnumerator.Result; enumerator.SetCancellationToken(cancellationToken); try { if (!await enumerator.MoveNextAsync(trace)) { throw new InvalidOperationException("ChangeFeed enumerator should always have a next continuation"); } } catch (OperationCanceledException ex) when(!(ex is CosmosOperationCanceledException)) { throw new CosmosOperationCanceledException(ex, trace); } if (enumerator.Current.Failed) { if (!ExceptionToCosmosException.TryCreateFromException( enumerator.Current.Exception, trace, out CosmosException cosmosException)) { throw ExceptionWithStackTraceException.UnWrapMonadExcepion(enumerator.Current.Exception, trace); } if (!IsRetriableException(cosmosException)) { this.hasMoreResults = false; } return(new ResponseMessage( cosmosException.StatusCode, requestMessage: null, headers: cosmosException.Headers, cosmosException: cosmosException, trace: trace)); } 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; ChangeFeedCrossFeedRangeState changeFeedCrossFeedRangeState = new ChangeFeedCrossFeedRangeState(crossFeedRangeState.Value); string continuationToken = VersionedAndRidCheckedCompositeToken.ToCosmosElement( new VersionedAndRidCheckedCompositeToken( VersionedAndRidCheckedCompositeToken.Version.V2, changeFeedCrossFeedRangeState.ToCosmosElement(), await this.documentContainer.GetResourceIdentifierAsync(trace, cancellationToken))).ToString(); responseMessage.Headers.ContinuationToken = continuationToken; responseMessage.Headers.RequestCharge = changeFeedPage.RequestCharge; responseMessage.Headers.ActivityId = changeFeedPage.ActivityId; responseMessage.Trace = trace; return(responseMessage); }
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 static ChangeFeedPage CreateNotModifiedPage(double requestCharge, string activityId, ChangeFeedCrossFeedRangeState state) { return(new ChangeFeedPage(CosmosArray.Empty, notModified: true, requestCharge, activityId, state)); }
public static ChangeFeedPage CreatePageWithChanges(CosmosArray documents, double requestCharge, string activityId, ChangeFeedCrossFeedRangeState state) { return(new ChangeFeedPage(documents, notModified: false, requestCharge, activityId, state)); }