Ejemplo n.º 1
0
        public async ValueTask <bool> MoveNextAsync()
        {
            this.cancellationToken.ThrowIfCancellationRequested();

            if (!await this.crossPartitionEnumerator.MoveNextAsync())
            {
                this.Current = default;
                return(false);
            }

            TryCatch <CrossPartitionPage <ReadFeedPage, ReadFeedState> > monadicCrossPartitionPage = this.crossPartitionEnumerator.Current;

            if (monadicCrossPartitionPage.Failed)
            {
                this.Current = TryCatch <ReadFeedPage> .FromException(monadicCrossPartitionPage.Exception);

                return(true);
            }

            CrossPartitionPage <ReadFeedPage, ReadFeedState> crossPartitionPage = monadicCrossPartitionPage.Result;
            ReadFeedPage backendPage = crossPartitionPage.Page;
            CrossPartitionState <ReadFeedState> crossPartitionState = crossPartitionPage.State;
            ReadFeedState state;

            if (crossPartitionState != null)
            {
                IReadOnlyList <(FeedRangeInternal, ReadFeedState)> rangesAndStates = crossPartitionState.Value;
                List <CosmosElement> changeFeedContinuationTokens = new List <CosmosElement>();
                foreach ((FeedRangeInternal range, ReadFeedState readFeedState) in rangesAndStates)
                {
                    this.cancellationToken.ThrowIfCancellationRequested();
                    ReadFeedContinuationToken readFeedContinuationToken = new ReadFeedContinuationToken(
                        range,
                        readFeedState);

                    CosmosElement cosmosElementChangeFeedContinuationToken = ReadFeedContinuationToken.ToCosmosElement(readFeedContinuationToken);

                    changeFeedContinuationTokens.Add(cosmosElementChangeFeedContinuationToken);
                }

                CosmosArray cosmosElementTokens = CosmosArray.Create(changeFeedContinuationTokens);
                state = new ReadFeedState(cosmosElementTokens);
            }
            else
            {
                state = null;
            }

            ReadFeedPage compositePage = new ReadFeedPage(backendPage.Content, backendPage.RequestCharge, backendPage.ActivityId, backendPage.Diagnostics, state);

            this.Current = TryCatch <ReadFeedPage> .FromResult(compositePage);

            return(true);
        }
Ejemplo n.º 2
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);
        }
        // In order to maintain the continuation token for the user we must drain with a few constraints
        // 1) We fully drain from the left most partition before moving on to the next partition
        // 2) We drain only full pages from the document producer so we aren't left with a partial page
        //  otherwise we would need to add to the continuation token how many items to skip over on that page.
        public async ValueTask <bool> MoveNextAsync()
        {
            this.cancellationToken.ThrowIfCancellationRequested();

            if (!await this.crossPartitionRangePageAsyncEnumerator.MoveNextAsync())
            {
                this.Current = default;
                return(false);
            }

            TryCatch <CrossPartitionPage <QueryPage, QueryState> > currentCrossPartitionPage = this.crossPartitionRangePageAsyncEnumerator.Current;

            if (currentCrossPartitionPage.Failed)
            {
                this.Current = TryCatch <QueryPage> .FromException(currentCrossPartitionPage.Exception);

                return(true);
            }

            CrossPartitionPage <QueryPage, QueryState> crossPartitionPageResult = currentCrossPartitionPage.Result;
            QueryPage backendQueryPage = crossPartitionPageResult.Page;
            CrossPartitionState <QueryState> crossPartitionState = crossPartitionPageResult.State;

            QueryState queryState;

            if (crossPartitionState == null)
            {
                queryState = null;
            }
            else
            {
                // Left most and any non null continuations
                List <(PartitionKeyRange, QueryState)> rangesAndStates = crossPartitionState.Value.OrderBy(tuple => tuple.Item1, PartitionKeyRangeComparer.Singleton).ToList();
                List <ParallelContinuationToken>       activeParallelContinuationTokens = new List <ParallelContinuationToken>();
                for (int i = 0; i < rangesAndStates.Count; i++)
                {
                    this.cancellationToken.ThrowIfCancellationRequested();

                    (PartitionKeyRange range, QueryState state) = rangesAndStates[i];
                    if ((i == 0) || (state != null))
                    {
                        ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken(
                            token: state != null ? ((CosmosString)state.Value).Value : null,
                            range: range.ToRange());

                        activeParallelContinuationTokens.Add(parallelContinuationToken);
                    }
                }

                IEnumerable <CosmosElement> cosmosElementContinuationTokens = activeParallelContinuationTokens
                                                                              .Select(token => ParallelContinuationToken.ToCosmosElement(token));
                CosmosArray cosmosElementParallelContinuationTokens = CosmosArray.Create(cosmosElementContinuationTokens);

                queryState = new QueryState(cosmosElementParallelContinuationTokens);
            }

            QueryPage crossPartitionQueryPage = new QueryPage(
                backendQueryPage.Documents,
                backendQueryPage.RequestCharge,
                backendQueryPage.ActivityId,
                backendQueryPage.ResponseLengthInBytes,
                backendQueryPage.CosmosQueryExecutionInfo,
                backendQueryPage.DisallowContinuationTokenMessage,
                queryState);

            this.Current = TryCatch <QueryPage> .FromResult(crossPartitionQueryPage);

            return(true);
        }