//TODO: create additional varadic Merge methods for allocation free code paths. public static Memory <FeedRangeState <TState> > Merge <TState>(IReadOnlyList <ReadOnlyMemory <FeedRangeState <TState> > > ranges) where TState : State { if (ranges == null) { throw new ArgumentNullException(nameof(ranges)); } int totalLength = 0; for (int i = 0; i < ranges.Count; i++) { totalLength += ranges[i].Length; } FeedRangeState <TState>[] feedRanges = new FeedRangeState <TState> [totalLength]; Memory <FeedRangeState <TState> > feedRangesSpan = feedRanges.AsMemory(); for (int i = 0; i < ranges.Count; i++) { ReadOnlyMemory <FeedRangeState <TState> > range = ranges[i]; range.CopyTo(feedRangesSpan); feedRangesSpan = feedRangesSpan.Slice(start: range.Length); } return(feedRanges); }
public PartitionRangePageAsyncEnumerable( FeedRangeState <TState> feedRangeState, CreatePartitionRangePageAsyncEnumerator <TPage, TState> createPartitionRangeEnumerator) { this.feedRangeState = feedRangeState; this.createPartitionRangeEnumerator = createPartitionRangeEnumerator ?? throw new ArgumentNullException(nameof(createPartitionRangeEnumerator)); }
public Task <TryCatch <ChangeFeedPage> > MonadicChangeFeedAsync( FeedRangeState <ChangeFeedState> state, ChangeFeedPaginationOptions changeFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) => this.monadicDocumentContainer.MonadicChangeFeedAsync( state, changeFeedPaginationOptions, trace, cancellationToken);
public Task <TryCatch <ReadFeedPage> > MonadicReadFeedAsync( FeedRangeState <ReadFeedState> feedRangeState, ReadFeedPaginationOptions readFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) => this.monadicDocumentContainer.MonadicReadFeedAsync( feedRangeState, readFeedPaginationOptions, trace, cancellationToken);
public Task <ChangeFeedPage> ChangeFeedAsync( FeedRangeState <ChangeFeedState> feedRangeState, ChangeFeedPaginationOptions changeFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) => TryCatch <ChangeFeedPage> .UnsafeGetResultAsync( this.MonadicChangeFeedAsync( feedRangeState, changeFeedPaginationOptions, trace, cancellationToken), cancellationToken);
public Task <TryCatch <QueryPage> > MonadicQueryAsync( SqlQuerySpec sqlQuerySpec, FeedRangeState <QueryState> feedRangeState, QueryPaginationOptions queryPaginationOptions, ITrace trace, CancellationToken cancellationToken) => this.monadicDocumentContainer.MonadicQueryAsync( sqlQuerySpec, feedRangeState, queryPaginationOptions, trace, cancellationToken);
public Task <ReadFeedPage> ReadFeedAsync( FeedRangeState <ReadFeedState> feedRangeState, ReadFeedPaginationOptions readFeedPaginationOptions, ITrace trace, CancellationToken cancellationToken) => TryCatch <ReadFeedPage> .UnsafeGetResultAsync( this.MonadicReadFeedAsync( feedRangeState, readFeedPaginationOptions, trace, cancellationToken), cancellationToken);
public Task <QueryPage> QueryAsync( SqlQuerySpec sqlQuerySpec, FeedRangeState <QueryState> feedRangeState, QueryPaginationOptions queryPaginationOptions, ITrace trace, CancellationToken cancellationToken) => TryCatch <QueryPage> .UnsafeGetResultAsync( this.MonadicQueryAsync( sqlQuerySpec, feedRangeState, queryPaginationOptions, trace, cancellationToken), cancellationToken);
public static Memory <FeedRangeState <TState> > Merge <TState>( ReadOnlyMemory <FeedRangeState <TState> > first, ReadOnlyMemory <FeedRangeState <TState> > second, ReadOnlyMemory <FeedRangeState <TState> > third) where TState : State { FeedRangeState <TState>[] feedRanges = new FeedRangeState <TState> [first.Length + second.Length + third.Length]; Memory <FeedRangeState <TState> > feedRangesMemory = feedRanges.AsMemory(); first.CopyTo(feedRangesMemory); feedRangesMemory = feedRangesMemory.Slice(start: first.Length); second.CopyTo(feedRangesMemory); feedRangesMemory = feedRangesMemory.Slice(start: second.Length); third.CopyTo(feedRangesMemory); feedRangesMemory = feedRangesMemory.Slice(start: third.Length); return(feedRanges); }
protected PartitionRangePageAsyncEnumerator(FeedRangeState <TState> feedRangeState, CancellationToken cancellationToken) { this.FeedRangeState = feedRangeState; this.cancellationToken = cancellationToken; }
public CrossPartitionRangePageAsyncEnumerator( IFeedRangeProvider feedRangeProvider, CreatePartitionRangePageAsyncEnumerator <TPage, TState> createPartitionRangeEnumerator, IComparer <PartitionRangePageAsyncEnumerator <TPage, TState> > comparer, int?maxConcurrency, CancellationToken cancellationToken, CrossFeedRangeState <TState> state = default) { this.feedRangeProvider = feedRangeProvider ?? throw new ArgumentNullException(nameof(feedRangeProvider)); this.createPartitionRangeEnumerator = createPartitionRangeEnumerator ?? throw new ArgumentNullException(nameof(createPartitionRangeEnumerator)); this.cancellationToken = cancellationToken; this.lazyEnumerators = new AsyncLazy <IQueue <PartitionRangePageAsyncEnumerator <TPage, TState> > >(async(ITrace trace, CancellationToken token) => { ReadOnlyMemory <FeedRangeState <TState> > rangeAndStates; if (state != default) { rangeAndStates = state.Value; } else { // Fan out to all partitions with default state List <FeedRangeEpk> ranges = await feedRangeProvider.GetFeedRangesAsync(trace, token); List <FeedRangeState <TState> > rangesAndStatesBuilder = new List <FeedRangeState <TState> >(ranges.Count); foreach (FeedRangeInternal range in ranges) { rangesAndStatesBuilder.Add(new FeedRangeState <TState>(range, default)); } rangeAndStates = rangesAndStatesBuilder.ToArray(); } List <BufferedPartitionRangePageAsyncEnumerator <TPage, TState> > bufferedEnumerators = new List <BufferedPartitionRangePageAsyncEnumerator <TPage, TState> >(rangeAndStates.Length); for (int i = 0; i < rangeAndStates.Length; i++) { FeedRangeState <TState> feedRangeState = rangeAndStates.Span[i]; PartitionRangePageAsyncEnumerator <TPage, TState> enumerator = createPartitionRangeEnumerator(feedRangeState); BufferedPartitionRangePageAsyncEnumerator <TPage, TState> bufferedEnumerator = new BufferedPartitionRangePageAsyncEnumerator <TPage, TState>(enumerator, cancellationToken); bufferedEnumerators.Add(bufferedEnumerator); } if (maxConcurrency.HasValue) { await ParallelPrefetch.PrefetchInParallelAsync(bufferedEnumerators, maxConcurrency.Value, trace, token); } IQueue <PartitionRangePageAsyncEnumerator <TPage, TState> > queue; if (comparer == null) { queue = new QueueWrapper <PartitionRangePageAsyncEnumerator <TPage, TState> >( new Queue <PartitionRangePageAsyncEnumerator <TPage, TState> >(bufferedEnumerators)); } else { queue = new PriorityQueueWrapper <PartitionRangePageAsyncEnumerator <TPage, TState> >( new PriorityQueue <PartitionRangePageAsyncEnumerator <TPage, TState> >( bufferedEnumerators, comparer)); } return(queue); }); }
public async ValueTask <bool> MoveNextAsync(ITrace trace) { if (trace == null) { throw new ArgumentNullException(nameof(trace)); } using (ITrace childTrace = trace.StartChild(name: nameof(MoveNextAsync), component: TraceComponent.Pagination, level: TraceLevel.Info)) { IQueue <PartitionRangePageAsyncEnumerator <TPage, TState> > enumerators = await this.lazyEnumerators.GetValueAsync( childTrace, cancellationToken : this.cancellationToken); if (enumerators.Count == 0) { this.Current = default; this.CurrentRange = default; this.nextState = default; return(false); } PartitionRangePageAsyncEnumerator <TPage, TState> currentPaginator = enumerators.Dequeue(); currentPaginator.SetCancellationToken(this.cancellationToken); bool moveNextResult = false; try { moveNextResult = await currentPaginator.MoveNextAsync(childTrace); } catch { // Re-queue the enumerator to avoid emptying the queue enumerators.Enqueue(currentPaginator); throw; } if (!moveNextResult) { // Current enumerator is empty, // so recursively retry on the next enumerator. return(await this.MoveNextAsync(childTrace)); } if (currentPaginator.Current.Failed) { // Check if it's a retryable exception. Exception exception = currentPaginator.Current.Exception; while (exception.InnerException != null) { exception = exception.InnerException; } if (IsSplitException(exception)) { // Handle split List <FeedRangeEpk> childRanges = await this.feedRangeProvider.GetChildRangeAsync( currentPaginator.FeedRangeState.FeedRange, childTrace, this.cancellationToken); if (childRanges.Count <= 1) { // We optimistically assumed that the cache is not stale. // In the event that it is (where we only get back one child / the partition that we think got split) // Then we need to refresh the cache await this.feedRangeProvider.RefreshProviderAsync(childTrace, this.cancellationToken); childRanges = await this.feedRangeProvider.GetChildRangeAsync( currentPaginator.FeedRangeState.FeedRange, childTrace, this.cancellationToken); } if (childRanges.Count < 1) { string errorMessage = "SDK invariant violated 4795CC37: Must have at least one EPK range in a cross partition enumerator"; throw Resource.CosmosExceptions.CosmosExceptionFactory.CreateInternalServerErrorException( message: errorMessage, headers: null, stackTrace: null, trace: childTrace, error: new Microsoft.Azure.Documents.Error { Code = "SDK_invariant_violated_4795CC37", Message = errorMessage }); } if (childRanges.Count == 1) { // On a merge, the 410/1002 results in a single parent // We maintain the current enumerator's range and let the RequestInvokerHandler logic kick in enumerators.Enqueue(currentPaginator); } else { // Split foreach (FeedRangeInternal childRange in childRanges) { PartitionRangePageAsyncEnumerator <TPage, TState> childPaginator = this.createPartitionRangeEnumerator( new FeedRangeState <TState>(childRange, currentPaginator.FeedRangeState.State)); enumerators.Enqueue(childPaginator); } } // Recursively retry return(await this.MoveNextAsync(childTrace)); } // Just enqueue the paginator and the user can decide if they want to retry. enumerators.Enqueue(currentPaginator); this.Current = TryCatch <CrossFeedRangePage <TPage, TState> > .FromException(currentPaginator.Current.Exception); this.CurrentRange = currentPaginator.FeedRangeState.FeedRange; this.nextState = CrossPartitionRangePageAsyncEnumerator <TPage, TState> .GetNextRange(enumerators); return(true); } if (currentPaginator.FeedRangeState.State != default) { // Don't enqueue the paginator otherwise it's an infinite loop. enumerators.Enqueue(currentPaginator); } CrossFeedRangeState <TState> crossPartitionState; if (enumerators.Count == 0) { crossPartitionState = null; } else { FeedRangeState <TState>[] feedRangeAndStates = new FeedRangeState <TState> [enumerators.Count]; int i = 0; foreach (PartitionRangePageAsyncEnumerator <TPage, TState> enumerator in enumerators) { feedRangeAndStates[i++] = enumerator.FeedRangeState; } crossPartitionState = new CrossFeedRangeState <TState>(feedRangeAndStates); } this.Current = TryCatch <CrossFeedRangePage <TPage, TState> > .FromResult( new CrossFeedRangePage <TPage, TState>(currentPaginator.Current.Result, crossPartitionState)); this.CurrentRange = currentPaginator.FeedRangeState.FeedRange; this.nextState = CrossPartitionRangePageAsyncEnumerator <TPage, TState> .GetNextRange(enumerators); return(true); } }
protected BufferedPartitionRangePageAsyncEnumeratorBase(FeedRangeState <TState> feedRangeState, CancellationToken cancellationToken) : base(feedRangeState, cancellationToken) { }
public async ValueTask <bool> MoveNextAsync(ITrace trace) { if (trace == null) { throw new ArgumentNullException(nameof(trace)); } this.cancellationToken.ThrowIfCancellationRequested(); using (ITrace childTrace = trace.StartChild(name: nameof(MoveNextAsync), component: TraceComponent.Pagination, level: TraceLevel.Info)) { IQueue <PartitionRangePageAsyncEnumerator <TPage, TState> > enumerators = await this.lazyEnumerators.GetValueAsync( childTrace, cancellationToken : this.cancellationToken); if (enumerators.Count == 0) { this.Current = default; this.CurrentRange = default; return(false); } PartitionRangePageAsyncEnumerator <TPage, TState> currentPaginator = enumerators.Dequeue(); if (!await currentPaginator.MoveNextAsync(childTrace)) { // Current enumerator is empty, // so recursively retry on the next enumerator. return(await this.MoveNextAsync(childTrace)); } if (currentPaginator.Current.Failed) { // Check if it's a retryable exception. Exception exception = currentPaginator.Current.Exception; while (exception.InnerException != null) { exception = exception.InnerException; } if (IsSplitException(exception)) { // Handle split List <FeedRangeEpk> childRanges = await this.feedRangeProvider.GetChildRangeAsync( currentPaginator.FeedRangeState.FeedRange, childTrace, this.cancellationToken); if (childRanges.Count == 0) { throw new InvalidOperationException("Got back no children"); } if (childRanges.Count == 1) { // We optimistically assumed that the cache is not stale. // In the event that it is (where we only get back one child / the partition that we think got split) // Then we need to refresh the cache await this.feedRangeProvider.RefreshProviderAsync(childTrace, this.cancellationToken); childRanges = await this.feedRangeProvider.GetChildRangeAsync( currentPaginator.FeedRangeState.FeedRange, childTrace, this.cancellationToken); } if (childRanges.Count() <= 1) { throw new InvalidOperationException("Expected more than 1 child"); } foreach (FeedRangeInternal childRange in childRanges) { PartitionRangePageAsyncEnumerator <TPage, TState> childPaginator = this.createPartitionRangeEnumerator( new FeedRangeState <TState>(childRange, currentPaginator.FeedRangeState.State)); enumerators.Enqueue(childPaginator); } // Recursively retry return(await this.MoveNextAsync(childTrace)); } // Just enqueue the paginator and the user can decide if they want to retry. enumerators.Enqueue(currentPaginator); this.Current = TryCatch <CrossFeedRangePage <TPage, TState> > .FromException(currentPaginator.Current.Exception); this.CurrentRange = currentPaginator.FeedRangeState.FeedRange; return(true); } if (currentPaginator.FeedRangeState.State != default) { // Don't enqueue the paginator otherwise it's an infinite loop. enumerators.Enqueue(currentPaginator); } CrossFeedRangeState <TState> crossPartitionState; if (enumerators.Count == 0) { crossPartitionState = null; } else { FeedRangeState <TState>[] feedRangeAndStates = new FeedRangeState <TState> [enumerators.Count]; int i = 0; foreach (PartitionRangePageAsyncEnumerator <TPage, TState> enumerator in enumerators) { feedRangeAndStates[i++] = enumerator.FeedRangeState; } crossPartitionState = new CrossFeedRangeState <TState>(feedRangeAndStates); } this.Current = TryCatch <CrossFeedRangePage <TPage, TState> > .FromResult( new CrossFeedRangePage <TPage, TState>(currentPaginator.Current.Result, crossPartitionState)); this.CurrentRange = currentPaginator.FeedRangeState.FeedRange; return(true); } }
public async ValueTask <bool> MoveNextAsync() { this.cancellationToken.ThrowIfCancellationRequested(); IQueue <PartitionRangePageAsyncEnumerator <TPage, TState> > enumerators = await this.lazyEnumerators.GetValueAsync(cancellationToken : this.cancellationToken); if (enumerators.Count == 0) { this.Current = default; this.CurrentRange = default; return(false); } PartitionRangePageAsyncEnumerator <TPage, TState> currentPaginator = enumerators.Dequeue(); if (!await currentPaginator.MoveNextAsync()) { // Current enumerator is empty, // so recursively retry on the next enumerator. return(await this.MoveNextAsync()); } if (currentPaginator.Current.Failed) { // Check if it's a retryable exception. Exception exception = currentPaginator.Current.Exception; while (exception.InnerException != null) { exception = exception.InnerException; } if (IsSplitException(exception)) { // Handle split IEnumerable <FeedRangeInternal> childRanges = await this.feedRangeProvider.GetChildRangeAsync( currentPaginator.Range, cancellationToken : this.cancellationToken); foreach (FeedRangeInternal childRange in childRanges) { PartitionRangePageAsyncEnumerator <TPage, TState> childPaginator = this.createPartitionRangeEnumerator( childRange, currentPaginator.State); enumerators.Enqueue(childPaginator); } // Recursively retry return(await this.MoveNextAsync()); } if (IsMergeException(exception)) { throw new NotImplementedException(); } // Just enqueue the paginator and the user can decide if they want to retry. enumerators.Enqueue(currentPaginator); this.Current = TryCatch <CrossFeedRangePage <TPage, TState> > .FromException(currentPaginator.Current.Exception); this.CurrentRange = currentPaginator.Range; return(true); } if (currentPaginator.State != default) { // Don't enqueue the paginator otherwise it's an infinite loop. enumerators.Enqueue(currentPaginator); } CrossFeedRangeState <TState> crossPartitionState; if (enumerators.Count == 0) { crossPartitionState = null; } else { FeedRangeState <TState>[] feedRangeAndStates = new FeedRangeState <TState> [enumerators.Count]; int i = 0; foreach (PartitionRangePageAsyncEnumerator <TPage, TState> enumerator in enumerators) { feedRangeAndStates[i++] = new FeedRangeState <TState>(enumerator.Range, enumerator.State); } crossPartitionState = new CrossFeedRangeState <TState>(feedRangeAndStates); } this.Current = TryCatch <CrossFeedRangePage <TPage, TState> > .FromResult( new CrossFeedRangePage <TPage, TState>(currentPaginator.Current.Result, crossPartitionState)); this.CurrentRange = currentPaginator.Range; return(true); }