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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
            }
        }
Exemple #4
0
        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);
        }