예제 #1
0
        public static CrossPartitionChangeFeedAsyncEnumerator Create(
            IDocumentContainer documentContainer,
            ChangeFeedMode changeFeedMode,
            ChangeFeedRequestOptions changeFeedRequestOptions,
            CrossFeedRangeState <ChangeFeedState> state,
            CancellationToken cancellationToken)
        {
            changeFeedRequestOptions ??= new ChangeFeedRequestOptions();

            if (documentContainer == null)
            {
                throw new ArgumentNullException(nameof(documentContainer));
            }

            if (changeFeedMode == null)
            {
                throw new ArgumentNullException(nameof(changeFeedMode));
            }

            CrossPartitionRangePageAsyncEnumerator <ChangeFeedPage, ChangeFeedState> crossPartitionEnumerator = new CrossPartitionRangePageAsyncEnumerator <ChangeFeedPage, ChangeFeedState>(
                documentContainer,
                CrossPartitionChangeFeedAsyncEnumerator.MakeCreateFunction(
                    documentContainer,
                    changeFeedRequestOptions.PageSizeHint.GetValueOrDefault(int.MaxValue),
                    changeFeedMode,
                    changeFeedRequestOptions?.JsonSerializationFormatOptions?.JsonSerializationFormat,
                    cancellationToken),
                comparer: default /* this uses a regular queue instead of prioirty queue */,
예제 #2
0
        public async ValueTask <bool> MoveNextAsync()
        {
            if (!await this.enumerator.MoveNextAsync())
            {
                return(false);
            }

            TryCatch <CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> > monadicInnerReadFeedPage = this.enumerator.Current;

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

                return(true);
            }

            CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> innerReadFeedPage = monadicInnerReadFeedPage.Result;
            CrossFeedRangeState <ReadFeedState> crossFeedRangeState = innerReadFeedPage.State;
            ReadFeedCrossFeedRangeState?        state = crossFeedRangeState != null ? new ReadFeedCrossFeedRangeState(crossFeedRangeState.Value) : (ReadFeedCrossFeedRangeState?)null;

            CosmosArray documents = CosmosQueryClientCore.ParseElementsFromRestStream(
                innerReadFeedPage.Page.Content,
                Documents.ResourceType.Document,
                cosmosSerializationOptions: null);
            ReadFeedPage page = new ReadFeedPage(
                documents,
                innerReadFeedPage.Page.RequestCharge,
                innerReadFeedPage.Page.ActivityId,
                state);

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

            return(true);
        }
        public async ValueTask <bool> MoveNextAsync()
        {
            this.cancellationToken.ThrowIfCancellationRequested();

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

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

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

                return(true);
            }

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

            if (crossPartitionState != null)
            {
                List <CosmosElement> changeFeedContinuationTokens = new List <CosmosElement>();
                for (int i = 0; i < crossPartitionState.Value.Length; i++)
                {
                    this.cancellationToken.ThrowIfCancellationRequested();
                    FeedRangeState <ReadFeedState> feedRangeState            = crossPartitionState.Value.Span[i];
                    ReadFeedContinuationToken      readFeedContinuationToken = new ReadFeedContinuationToken(
                        feedRangeState.FeedRange,
                        feedRangeState.State);

                    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);
        }
        public IAsyncEnumerator <TryCatch <ChangeFeedPage> > GetAsyncEnumerator(CancellationToken cancellationToken = default)
        {
            CrossFeedRangeState <ChangeFeedState>   innerState      = new CrossFeedRangeState <ChangeFeedState>(this.state.FeedRangeStates);
            CrossPartitionChangeFeedAsyncEnumerator innerEnumerator = CrossPartitionChangeFeedAsyncEnumerator.Create(
                this.documentContainer,
                this.changeFeedRequestOptions,
                innerState,
                cancellationToken);

            return(new ChangeFeedCrossFeedRangeAsyncEnumerator(innerEnumerator));
        }
예제 #5
0
        public IAsyncEnumerator <TryCatch <ReadFeedPage> > GetAsyncEnumerator(CancellationToken cancellationToken = default)
        {
            CrossFeedRangeState <ReadFeedState>   innerState      = new CrossFeedRangeState <ReadFeedState>(this.state.FeedRangeStates);
            CrossPartitionReadFeedAsyncEnumerator innerEnumerator = CrossPartitionReadFeedAsyncEnumerator.Create(
                this.documentContainer,
                this.requestOptions,
                innerState,
                this.requestOptions?.MaxItemCount ?? int.MaxValue,
                cancellationToken);

            return(new ReadFeedCrossFeedRangeAsyncEnumerator(innerEnumerator));
        }
예제 #6
0
 public CrossPartitionRangePageAsyncEnumerable(
     IFeedRangeProvider feedRangeProvider,
     CreatePartitionRangePageAsyncEnumerator <TPage, TState> createPartitionRangeEnumerator,
     IComparer <PartitionRangePageAsyncEnumerator <TPage, TState> > comparer,
     int maxConcurrency,
     ITrace trace,
     CrossFeedRangeState <TState> state = default)
 {
     this.feedRangeProvider = feedRangeProvider ?? throw new ArgumentNullException(nameof(comparer));
     this.createPartitionRangeEnumerator = createPartitionRangeEnumerator ?? throw new ArgumentNullException(nameof(createPartitionRangeEnumerator));
     this.comparer       = comparer ?? throw new ArgumentNullException(nameof(comparer));
     this.state          = state;
     this.maxConcurrency = maxConcurrency < 0 ? throw new ArgumentOutOfRangeException(nameof(maxConcurrency)) : maxConcurrency;
     this.trace          = trace ?? throw new ArgumentNullException(nameof(trace));
 }
예제 #7
0
        public static TryCatch <IQueryPipelineStage> MonadicCreate(
            IDocumentContainer documentContainer,
            SqlQuerySpec sqlQuerySpec,
            IReadOnlyList <FeedRangeEpk> targetRanges,
            Cosmos.PartitionKey?partitionKey,
            int pageSize,
            int maxConcurrency,
            CosmosElement continuationToken,
            CancellationToken cancellationToken)
        {
            if (targetRanges == null)
            {
                throw new ArgumentNullException(nameof(targetRanges));
            }

            if (targetRanges.Count == 0)
            {
                throw new ArgumentException($"{nameof(targetRanges)} must have some elements");
            }

            if (pageSize <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(pageSize));
            }

            TryCatch <CrossFeedRangeState <QueryState> > monadicExtractState = MonadicExtractState(continuationToken, targetRanges);

            if (monadicExtractState.Failed)
            {
                return(TryCatch <IQueryPipelineStage> .FromException(monadicExtractState.Exception));
            }

            CrossFeedRangeState <QueryState> state = monadicExtractState.Result;

            CrossPartitionRangePageAsyncEnumerator <QueryPage, QueryState> crossPartitionPageEnumerator = new CrossPartitionRangePageAsyncEnumerator <QueryPage, QueryState>(
                documentContainer,
                ParallelCrossPartitionQueryPipelineStage.MakeCreateFunction(documentContainer, sqlQuerySpec, pageSize, partitionKey, cancellationToken),
                comparer: Comparer.Singleton,
                maxConcurrency,
                state: state,
                cancellationToken: cancellationToken);

            ParallelCrossPartitionQueryPipelineStage stage = new ParallelCrossPartitionQueryPipelineStage(crossPartitionPageEnumerator, cancellationToken);

            return(TryCatch <IQueryPipelineStage> .FromResult(stage));
        }
예제 #8
0
        public static CrossPartitionReadFeedAsyncEnumerator Create(
            IDocumentContainer documentContainer,
            QueryRequestOptions queryRequestOptions,
            CrossFeedRangeState <ReadFeedState> crossFeedRangeState,
            int pageSize,
            CancellationToken cancellationToken)
        {
            if (documentContainer == null)
            {
                throw new ArgumentNullException(nameof(documentContainer));
            }

            if (crossFeedRangeState == null)
            {
                throw new ArgumentNullException(nameof(crossFeedRangeState));
            }

            RntbdConstants.RntdbEnumerationDirection rntdbEnumerationDirection = RntbdConstants.RntdbEnumerationDirection.Forward;
            if ((queryRequestOptions?.Properties != null) && queryRequestOptions.Properties.TryGetValue(HttpConstants.HttpHeaders.EnumerationDirection, out object direction))
            {
                rntdbEnumerationDirection = (byte)direction == (byte)RntbdConstants.RntdbEnumerationDirection.Reverse ? RntbdConstants.RntdbEnumerationDirection.Reverse : RntbdConstants.RntdbEnumerationDirection.Forward;
            }

            IComparer <PartitionRangePageAsyncEnumerator <ReadFeedPage, ReadFeedState> > comparer;

            if (rntdbEnumerationDirection == RntbdConstants.RntdbEnumerationDirection.Forward)
            {
                comparer = PartitionRangePageAsyncEnumeratorComparerForward.Singleton;
            }
            else
            {
                comparer = PartitionRangePageAsyncEnumeratorComparerReverse.Singleton;
            }

            CrossPartitionRangePageAsyncEnumerator <ReadFeedPage, ReadFeedState> crossPartitionEnumerator = new CrossPartitionRangePageAsyncEnumerator <ReadFeedPage, ReadFeedState>(
                documentContainer,
                CrossPartitionReadFeedAsyncEnumerator.MakeCreateFunction(
                    documentContainer,
                    queryRequestOptions,
                    pageSize,
                    cancellationToken),
                comparer: comparer,
                maxConcurrency: default,
예제 #9
0
        public static CrossPartitionChangeFeedAsyncEnumerator Create(
            IDocumentContainer documentContainer,
            CrossFeedRangeState <ChangeFeedState> state,
            ChangeFeedPaginationOptions changeFeedPaginationOptions,
            CancellationToken cancellationToken)
        {
            changeFeedPaginationOptions ??= ChangeFeedPaginationOptions.Default;

            if (documentContainer == null)
            {
                throw new ArgumentNullException(nameof(documentContainer));
            }

            CrossPartitionRangePageAsyncEnumerator <ChangeFeedPage, ChangeFeedState> crossPartitionEnumerator = new CrossPartitionRangePageAsyncEnumerator <ChangeFeedPage, ChangeFeedState>(
                documentContainer,
                CrossPartitionChangeFeedAsyncEnumerator.MakeCreateFunction(
                    documentContainer,
                    changeFeedPaginationOptions,
                    cancellationToken),
                comparer: default /* this uses a regular queue instead of prioirty queue */,
        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);
        }
예제 #11
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);
        }
        public static CrossPartitionReadFeedAsyncEnumerator Create(
            IDocumentContainer documentContainer,
            CrossFeedRangeState <ReadFeedState> crossFeedRangeState,
            ReadFeedPaginationOptions readFeedPaginationOptions,
            CancellationToken cancellationToken)
        {
            if (documentContainer == null)
            {
                throw new ArgumentNullException(nameof(documentContainer));
            }

            if (crossFeedRangeState == null)
            {
                throw new ArgumentNullException(nameof(crossFeedRangeState));
            }

            readFeedPaginationOptions ??= ReadFeedPaginationOptions.Default;

            ReadFeedPaginationOptions.PaginationDirection paginationDirection = readFeedPaginationOptions.Direction.GetValueOrDefault(ReadFeedPaginationOptions.PaginationDirection.Forward);

            IComparer <PartitionRangePageAsyncEnumerator <ReadFeedPage, ReadFeedState> > comparer;

            if (paginationDirection == ReadFeedPaginationOptions.PaginationDirection.Forward)
            {
                comparer = PartitionRangePageAsyncEnumeratorComparerForward.Singleton;
            }
            else
            {
                comparer = PartitionRangePageAsyncEnumeratorComparerReverse.Singleton;
            }

            CrossPartitionRangePageAsyncEnumerator <ReadFeedPage, ReadFeedState> crossPartitionEnumerator = new CrossPartitionRangePageAsyncEnumerator <ReadFeedPage, ReadFeedState>(
                documentContainer,
                CrossPartitionReadFeedAsyncEnumerator.MakeCreateFunction(
                    documentContainer,
                    readFeedPaginationOptions,
                    cancellationToken),
                comparer: comparer,
                maxConcurrency: default,
        public async Task ShouldReturnNotModifiedAfterCyclingOnAllRanges(int partitions)
        {
            ReadOnlyMemory<FeedRangeState<ChangeFeedState>> rangeStates = null;

            if (partitions == 1)
            {
                rangeStates = new FeedRangeState<ChangeFeedState>[]{
                    new FeedRangeState<ChangeFeedState>(FeedRangeEpk.FullRange, ChangeFeedState.Now())
                };
            }
            if (partitions == 2)
            {
                rangeStates = new FeedRangeState<ChangeFeedState>[]{
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("", "AA", true, false)), ChangeFeedState.Now()),
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("AA", "FF", true, false)), ChangeFeedState.Now()),
                };
            }
            if (partitions == 3)
            {
                rangeStates = new FeedRangeState<ChangeFeedState>[]{
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("", "AA", true, false)), ChangeFeedState.Now()),
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("AA", "BB", true, false)), ChangeFeedState.Now()),
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("BB", "FF", true, false)), ChangeFeedState.Now()),
                };
            }

            Assert.IsNotNull(rangeStates, $"Range states not initialized for {partitions} partitions");

            CrossFeedRangeState<ChangeFeedState> state = new CrossFeedRangeState<ChangeFeedState>(rangeStates);
            Mock<IDocumentContainer> documentContainer = new Mock<IDocumentContainer>();

            // Returns a 304 with 1RU charge
            documentContainer.Setup(c => c.MonadicChangeFeedAsync(
                It.IsAny<FeedRangeState<ChangeFeedState>>(),
                It.IsAny<ChangeFeedPaginationOptions>(),
                It.IsAny<ITrace>(),
                It.IsAny<CancellationToken>())).ReturnsAsync(
                (FeedRangeState<ChangeFeedState> state, ChangeFeedPaginationOptions options, ITrace trace, CancellationToken token) 
                    => TryCatch<ChangeFeedPage>.FromResult(new ChangeFeedNotModifiedPage(requestCharge: 1, activityId: string.Empty, additionalHeaders: default, state.State)));
예제 #14
0
        public async ValueTask <bool> MoveNextAsync(ITrace trace)
        {
            this.cancellationToken.ThrowIfCancellationRequested();

            if (trace == null)
            {
                throw new ArgumentNullException(nameof(trace));
            }

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

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

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

                return(true);
            }

            CrossFeedRangePage <QueryPage, QueryState> crossPartitionPageResult = currentCrossPartitionPage.Result;
            QueryPage backendQueryPage = crossPartitionPageResult.Page;
            CrossFeedRangeState <QueryState> crossPartitionState = crossPartitionPageResult.State;

            QueryState queryState;

            if (crossPartitionState == null)
            {
                queryState = null;
            }
            else
            {
                // left most and any non null continuations
                IOrderedEnumerable <FeedRangeState <QueryState> > feedRangeStates = crossPartitionState
                                                                                    .Value
                                                                                    .ToArray()
                                                                                    .OrderBy(tuple => ((FeedRangeEpk)tuple.FeedRange).Range.Min);

                List <ParallelContinuationToken> activeParallelContinuationTokens = new List <ParallelContinuationToken>();
                {
                    FeedRangeState <QueryState> firstState = feedRangeStates.First();
                    ParallelContinuationToken   firstParallelContinuationToken = new ParallelContinuationToken(
                        token: firstState.State != null ? ((CosmosString)firstState.State.Value).Value : null,
                        range: ((FeedRangeEpk)firstState.FeedRange).Range);

                    activeParallelContinuationTokens.Add(firstParallelContinuationToken);
                }

                foreach (FeedRangeState <QueryState> feedRangeState in feedRangeStates.Skip(1))
                {
                    this.cancellationToken.ThrowIfCancellationRequested();

                    if (feedRangeState.State != null)
                    {
                        ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken(
                            token: feedRangeState.State != null ? ((CosmosString)feedRangeState.State.Value).Value : null,
                            range: ((FeedRangeEpk)feedRangeState.FeedRange).Range);

                        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);
        }
예제 #15
0
        private static TryCatch <CrossFeedRangeState <QueryState> > MonadicExtractState(
            CosmosElement continuationToken,
            IReadOnlyList <FeedRangeEpk> ranges)
        {
            if (continuationToken == null)
            {
                // Full fan out to the ranges with null continuations
                CrossFeedRangeState <QueryState> fullFanOutState = new CrossFeedRangeState <QueryState>(ranges.Select(range => new FeedRangeState <QueryState>(range, (QueryState)null)).ToArray());
                return(TryCatch <CrossFeedRangeState <QueryState> > .FromResult(fullFanOutState));
            }

            if (!(continuationToken is CosmosArray parallelContinuationTokenListRaw))
            {
                return(TryCatch <CrossFeedRangeState <QueryState> > .FromException(
                           new MalformedContinuationTokenException(
                               $"Invalid format for continuation token {continuationToken} for {nameof(ParallelCrossPartitionQueryPipelineStage)}")));
            }

            if (parallelContinuationTokenListRaw.Count == 0)
            {
                return(TryCatch <CrossFeedRangeState <QueryState> > .FromException(
                           new MalformedContinuationTokenException(
                               $"Invalid format for continuation token {continuationToken} for {nameof(ParallelCrossPartitionQueryPipelineStage)}")));
            }

            List <ParallelContinuationToken> parallelContinuationTokens = new List <ParallelContinuationToken>();

            foreach (CosmosElement parallelContinuationTokenRaw in parallelContinuationTokenListRaw)
            {
                TryCatch <ParallelContinuationToken> tryCreateParallelContinuationToken = ParallelContinuationToken.TryCreateFromCosmosElement(parallelContinuationTokenRaw);
                if (tryCreateParallelContinuationToken.Failed)
                {
                    return(TryCatch <CrossFeedRangeState <QueryState> > .FromException(
                               tryCreateParallelContinuationToken.Exception));
                }

                parallelContinuationTokens.Add(tryCreateParallelContinuationToken.Result);
            }

            TryCatch <PartitionMapping <ParallelContinuationToken> > partitionMappingMonad = PartitionMapper.MonadicGetPartitionMapping(
                ranges,
                parallelContinuationTokens);

            if (partitionMappingMonad.Failed)
            {
                return(TryCatch <CrossFeedRangeState <QueryState> > .FromException(
                           partitionMappingMonad.Exception));
            }

            PartitionMapping <ParallelContinuationToken> partitionMapping = partitionMappingMonad.Result;
            List <FeedRangeState <QueryState> >          feedRangeStates  = new List <FeedRangeState <QueryState> >();

            List <IReadOnlyDictionary <FeedRangeEpk, ParallelContinuationToken> > rangesToInitialize = new List <IReadOnlyDictionary <FeedRangeEpk, ParallelContinuationToken> >()
            {
                // Skip all the partitions left of the target range, since they have already been drained fully.
                partitionMapping.TargetMapping,
                partitionMapping.MappingRightOfTarget,
            };

            foreach (IReadOnlyDictionary <FeedRangeEpk, ParallelContinuationToken> rangeToInitalize in rangesToInitialize)
            {
                foreach (KeyValuePair <FeedRangeEpk, ParallelContinuationToken> kvp in rangeToInitalize)
                {
                    FeedRangeState <QueryState> feedRangeState = new FeedRangeState <QueryState>(kvp.Key, kvp.Value?.Token != null ? new QueryState(CosmosString.Create(kvp.Value.Token)) : null);
                    feedRangeStates.Add(feedRangeState);
                }
            }

            CrossFeedRangeState <QueryState> crossPartitionState = new CrossFeedRangeState <QueryState>(feedRangeStates.ToArray());

            return(TryCatch <CrossFeedRangeState <QueryState> > .FromResult(crossPartitionState));
        }
예제 #16
0
 public CrossFeedRangePage(TBackendPage backendEndPage, CrossFeedRangeState <TBackendState> state)
     : base(state)
 {
     this.Page = backendEndPage;
 }
        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(
            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,
예제 #19
0
        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);
        }