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 */,
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)); }
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)); }
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)); }
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)); }
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,
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); }
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)));
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); }
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)); }
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,
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); }