Пример #1
0
        //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);
        }
Пример #2
0
 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);
Пример #9
0
        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)
 {
 }
Пример #14
0
        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);
        }