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 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 ValueTask <bool> MoveNextAsync(ITrace trace) { this.cancellationToken.ThrowIfCancellationRequested(); if (trace == null) { throw new ArgumentNullException(nameof(trace)); } using (ITrace changeFeedMoveNextTrace = trace.StartChild("ChangeFeed MoveNextAsync", TraceComponent.ChangeFeed, TraceLevel.Info)) { if (this.bufferedException.HasValue) { this.Current = this.bufferedException.Value; this.bufferedException = null; return(true); } if (!await this.crossPartitionEnumerator.MoveNextAsync(changeFeedMoveNextTrace)) { throw new InvalidOperationException("ChangeFeed should always have a next page."); } TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > monadicCrossPartitionPage = this.crossPartitionEnumerator.Current; if (monadicCrossPartitionPage.Failed) { this.Current = TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > .FromException(monadicCrossPartitionPage.Exception); return(true); } CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> crossFeedRangePage = monadicCrossPartitionPage.Result; ChangeFeedPage backendPage = crossFeedRangePage.Page; if (backendPage is ChangeFeedNotModifiedPage) { // Keep draining the cross partition enumerator until // We get a non 304 page or we loop back to the same range or run into an exception FeedRangeInternal originalRange = this.crossPartitionEnumerator.CurrentRange; // No point on draining when the state has 1 range if (!IsNextRangeEqualToOriginal(this.crossPartitionEnumerator, originalRange)) { using (ITrace drainNotModifedPages = changeFeedMoveNextTrace.StartChild("Drain NotModified Pages", TraceComponent.ChangeFeed, TraceLevel.Info)) { double totalRequestCharge = backendPage.RequestCharge; do { if (!await this.crossPartitionEnumerator.MoveNextAsync(drainNotModifedPages)) { throw new InvalidOperationException("ChangeFeed should always have a next page."); } monadicCrossPartitionPage = this.crossPartitionEnumerator.Current; if (monadicCrossPartitionPage.Failed) { // Buffer the exception, since we need to return the request charge so far. this.bufferedException = TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > .FromException(monadicCrossPartitionPage.Exception); } else { crossFeedRangePage = monadicCrossPartitionPage.Result; backendPage = crossFeedRangePage.Page; totalRequestCharge += backendPage.RequestCharge; } }while (!(backendPage is ChangeFeedSuccessPage || IsNextRangeEqualToOriginal(this.crossPartitionEnumerator, originalRange) || this.bufferedException.HasValue)); // Create a page with the aggregated request charge if (backendPage is ChangeFeedSuccessPage changeFeedSuccessPage) { backendPage = new ChangeFeedSuccessPage( changeFeedSuccessPage.Content, totalRequestCharge, changeFeedSuccessPage.ActivityId, changeFeedSuccessPage.AdditionalHeaders, changeFeedSuccessPage.State); } else { backendPage = new ChangeFeedNotModifiedPage( totalRequestCharge, backendPage.ActivityId, backendPage.AdditionalHeaders, backendPage.State); } } } } crossFeedRangePage = new CrossFeedRangePage <ChangeFeedPage, ChangeFeedState>( backendPage, crossFeedRangePage.State); this.Current = TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > .FromResult(crossFeedRangePage); return(true); } }
public async ValueTask <bool> MoveNextAsync() { this.cancellationToken.ThrowIfCancellationRequested(); if (this.bufferedException.HasValue) { this.Current = this.bufferedException.Value; this.bufferedException = null; return(true); } if (!await this.crossPartitionEnumerator.MoveNextAsync()) { throw new InvalidOperationException("ChangeFeed should always have a next page."); } TryCatch <CrossPartitionPage <ChangeFeedPage, ChangeFeedState> > monadicCrossPartitionPage = this.crossPartitionEnumerator.Current; if (monadicCrossPartitionPage.Failed) { this.Current = TryCatch <ChangeFeedPage> .FromException(monadicCrossPartitionPage.Exception); return(true); } CrossPartitionPage <ChangeFeedPage, ChangeFeedState> crossPartitionPage = monadicCrossPartitionPage.Result; ChangeFeedPage backendPage = crossPartitionPage.Page; if (backendPage is ChangeFeedNotModifiedPage) { // Keep draining the cross partition enumerator until // We get a non 304 page or we loop back to the same range or run into an exception FeedRangeInternal originalRange = this.crossPartitionEnumerator.CurrentRange; double totalRequestCharge = backendPage.RequestCharge; do { if (!await this.crossPartitionEnumerator.MoveNextAsync()) { throw new InvalidOperationException("ChangeFeed should always have a next page."); } monadicCrossPartitionPage = this.crossPartitionEnumerator.Current; if (monadicCrossPartitionPage.Failed) { // Buffer the exception, since we need to return the request charge so far. this.bufferedException = TryCatch <ChangeFeedPage> .FromException(monadicCrossPartitionPage.Exception); } else { crossPartitionPage = monadicCrossPartitionPage.Result; backendPage = crossPartitionPage.Page; totalRequestCharge += backendPage.RequestCharge; } }while (!(backendPage is ChangeFeedSuccessPage || this.crossPartitionEnumerator.CurrentRange.Equals(originalRange) || this.bufferedException.HasValue)); // Create a page with the aggregated request charge if (backendPage is ChangeFeedSuccessPage changeFeedSuccessPage) { backendPage = new ChangeFeedSuccessPage( changeFeedSuccessPage.Content, totalRequestCharge, changeFeedSuccessPage.ActivityId, changeFeedSuccessPage.State); } else { backendPage = new ChangeFeedNotModifiedPage( totalRequestCharge, backendPage.ActivityId, backendPage.State); } } CrossPartitionState <ChangeFeedState> crossPartitionState = crossPartitionPage.State; List <CosmosElement> changeFeedContinuationTokens = new List <CosmosElement>(); foreach ((FeedRangeInternal range, ChangeFeedState state)rangeAndState in crossPartitionState.Value) { ChangeFeedContinuationToken changeFeedContinuationToken = new ChangeFeedContinuationToken( rangeAndState.range, rangeAndState.state); CosmosElement cosmosElementChangeFeedContinuationToken = ChangeFeedContinuationToken.ToCosmosElement(changeFeedContinuationToken); changeFeedContinuationTokens.Add(cosmosElementChangeFeedContinuationToken); } CosmosArray cosmosElementTokens = CosmosArray.Create(changeFeedContinuationTokens); ChangeFeedState state = ChangeFeedState.Continuation(cosmosElementTokens); ChangeFeedPage compositePage; if (backendPage is ChangeFeedSuccessPage successPage) { compositePage = new ChangeFeedSuccessPage( successPage.Content, successPage.RequestCharge, successPage.ActivityId, state); } else { compositePage = new ChangeFeedNotModifiedPage( backendPage.RequestCharge, backendPage.ActivityId, state); } this.Current = TryCatch <ChangeFeedPage> .FromResult(compositePage); return(true); }