protected override async Task <TryCatch <OrderByQueryPage> > GetNextPageAsync(ITrace trace, CancellationToken cancellationToken)
            {
                // Unfortunately we need to keep both the epk range and partition key for queries
                // Since the continuation token format uses epk range even though we only need the partition key to route the request.
                FeedRangeInternal feedRange = this.PartitionKey.HasValue ? new FeedRangePartitionKey(this.PartitionKey.Value) : this.FeedRangeState.FeedRange;

                TryCatch <QueryPage> monadicQueryPage = await this.queryDataSource
                                                        .MonadicQueryAsync(
                    sqlQuerySpec : this.SqlQuerySpec,
                    feedRangeState : new FeedRangeState <QueryState>(feedRange, this.FeedRangeState.State),
                    queryPaginationOptions : this.queryPaginationOptions,
                    trace : trace,
                    cancellationToken);

                if (monadicQueryPage.Failed)
                {
                    return(TryCatch <OrderByQueryPage> .FromException(monadicQueryPage.Exception));
                }
                QueryPage queryPage = monadicQueryPage.Result;

                return(TryCatch <OrderByQueryPage> .FromResult(new OrderByQueryPage(queryPage)));
            }
 public ReadFeedContinuationToken(FeedRangeInternal feedRange, ReadFeedState readFeedState)
 {
     this.Range = feedRange ?? throw new ArgumentNullException(nameof(feedRange));
     this.State = readFeedState ?? throw new ArgumentNullException(nameof(readFeedState));
 }
Ejemplo n.º 3
0
        public async Task ReadFeedIteratorCore_ReadNextAsync()
        {
            string          continuation    = "TBD";
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK);

            responseMessage.Headers.ContinuationToken = continuation;
            responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1";
            responseMessage.Content = new MemoryStream(Encoding.UTF8.GetBytes("{}"));

            Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>();

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <string>(),
                       It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Document),
                       It.IsAny <Documents.OperationType>(),
                       It.IsAny <RequestOptions>(),
                       It.IsAny <ContainerInternal>(),
                       It.IsAny <PartitionKey?>(),
                       It.IsAny <Stream>(),
                       It.IsAny <Action <RequestMessage> >(),
                       It.IsAny <CosmosDiagnosticsContext>(),
                       It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(responseMessage));

            ContainerInternal containerCore = Mock.Of <ContainerInternal>();

            Mock.Get(containerCore)
            .Setup(c => c.ClientContext)
            .Returns(cosmosClientContext.Object);
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.GetContinuation())
            .Returns(continuation);
            Mock.Get(feedToken)
            .Setup(f => f.IsDone)
            .Returns(true);

            FeedRangeIteratorCore feedTokenIterator = new FeedRangeIteratorCore(containerCore, feedToken, new QueryRequestOptions(), Documents.ResourceType.Document, queryDefinition: null);
            ResponseMessage       response          = await feedTokenIterator.ReadNextAsync();

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
        }
Ejemplo n.º 4
0
 public override Task <List <PartitionKeyRange> > GetTargetPartitionKeyRangeByFeedRangeAsync(string resourceLink, string collectionResourceId, PartitionKeyDefinition partitionKeyDefinition, FeedRangeInternal feedRangeInternal, bool forceRefresh, ITrace trace)
 {
     throw new NotImplementedException();
 }
 public Task <TryCatch> MonadicSplitAsync(
     FeedRangeInternal feedRange,
     CancellationToken cancellationToken) => Task.FromResult(TryCatch.FromException(new NotSupportedException()));
 public abstract Task <List <Documents.PartitionKeyRange> > GetTargetPartitionKeyRangeByFeedRangeAsync(
     string resourceLink,
     string collectionResourceId,
     Documents.PartitionKeyDefinition partitionKeyDefinition,
     FeedRangeInternal feedRangeInternal);
Ejemplo n.º 7
0
 public FeedRangeState(FeedRangeInternal feedRange, TState state)
 {
     this.FeedRange = feedRange ?? throw new ArgumentNullException(nameof(feedRange));
     this.State     = state;
 }
Ejemplo n.º 8
0
 public Task <TryCatch <List <FeedRangeEpk> > > MonadicGetChildRangeAsync(
     FeedRangeInternal feedRange,
     CancellationToken cancellationToken) => this.monadicDocumentContainer.MonadicGetChildRangeAsync(
     feedRange,
     cancellationToken);
Ejemplo n.º 9
0
 /// <summary>
 /// Intializes an instance of the <see cref="ChangeFeedStartFromNow"/> class.
 /// </summary>
 /// <param name="feedRange">The (optional) feed range to start from.</param>
 public ChangeFeedStartFromNow(FeedRangeInternal feedRange)
     : base(feedRange)
 {
 }
Ejemplo n.º 10
0
        public static QueryIterator Create(
            CosmosQueryClient client,
            CosmosClientContext clientContext,
            SqlQuerySpec sqlQuerySpec,
            string continuationToken,
            FeedRangeInternal feedRangeInternal,
            QueryRequestOptions queryRequestOptions,
            Uri resourceLink,
            bool isContinuationExpected,
            bool allowNonValueAggregateQuery,
            PartitionedQueryExecutionInfo partitionedQueryExecutionInfo)
        {
            if (queryRequestOptions == null)
            {
                queryRequestOptions = new QueryRequestOptions();
            }

            CosmosDiagnosticsContext queryPipelineCreationDiagnostics = CosmosDiagnosticsContext.Create(queryRequestOptions);

            CosmosQueryContextCore cosmosQueryContext = new CosmosQueryContextCore(
                client: client,
                queryRequestOptions: queryRequestOptions,
                resourceTypeEnum: Documents.ResourceType.Document,
                operationType: Documents.OperationType.Query,
                resourceType: typeof(QueryResponseCore),
                resourceLink: resourceLink,
                isContinuationExpected: isContinuationExpected,
                allowNonValueAggregateQuery: allowNonValueAggregateQuery,
                diagnosticsContext: queryPipelineCreationDiagnostics,
                correlatedActivityId: Guid.NewGuid());

            CosmosElement requestContinuationToken;

            switch (queryRequestOptions.ExecutionEnvironment.GetValueOrDefault(ExecutionEnvironment.Client))
            {
            case ExecutionEnvironment.Client:
                if (continuationToken != null)
                {
                    if (!CosmosElement.TryParse(continuationToken, out requestContinuationToken))
                    {
                        return(new QueryIterator(
                                   cosmosQueryContext,
                                   new QueryExecutionContextWithException(
                                       new MalformedContinuationTokenException(
                                           $"Malformed Continuation Token: {continuationToken}")),
                                   queryRequestOptions.CosmosSerializationFormatOptions,
                                   queryRequestOptions,
                                   clientContext));
                    }
                }
                else
                {
                    requestContinuationToken = null;
                }
                break;

            case ExecutionEnvironment.Compute:
                requestContinuationToken = queryRequestOptions.CosmosElementContinuationToken;
                break;

            default:
                throw new ArgumentOutOfRangeException($"Unknown {nameof(ExecutionEnvironment)}: {queryRequestOptions.ExecutionEnvironment.Value}.");
            }

            CosmosQueryExecutionContextFactory.InputParameters inputParameters = new CosmosQueryExecutionContextFactory.InputParameters(
                sqlQuerySpec: sqlQuerySpec,
                initialUserContinuationToken: requestContinuationToken,
                initialFeedRange: feedRangeInternal,
                maxConcurrency: queryRequestOptions.MaxConcurrency,
                maxItemCount: queryRequestOptions.MaxItemCount,
                maxBufferedItemCount: queryRequestOptions.MaxBufferedItemCount,
                partitionKey: queryRequestOptions.PartitionKey,
                properties: queryRequestOptions.Properties,
                partitionedQueryExecutionInfo: partitionedQueryExecutionInfo,
                executionEnvironment: queryRequestOptions.ExecutionEnvironment,
                returnResultsInDeterministicOrder: queryRequestOptions.ReturnResultsInDeterministicOrder,
                testInjections: queryRequestOptions.TestSettings);

            return(new QueryIterator(
                       cosmosQueryContext,
                       CosmosQueryExecutionContextFactory.Create(cosmosQueryContext, inputParameters),
                       queryRequestOptions.CosmosSerializationFormatOptions,
                       queryRequestOptions,
                       clientContext));
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Initializes an instance of the <see cref="ChangeFeedStartFromBeginning"/> class.
 /// </summary>
 /// <param name="feedRange">The (optional) range to start from.</param>
 public ChangeFeedStartFromBeginning(FeedRangeInternal feedRange)
     : base(feedRange)
 {
 }
Ejemplo n.º 12
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 <CrossPartitionPage <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);
            }

            CrossPartitionState <TState> crossPartitionState;

            if (enumerators.Count == 0)
            {
                crossPartitionState = null;
            }
            else
            {
                List <(FeedRangeInternal, TState)> feedRangeAndStates = new List <(FeedRangeInternal, TState)>(enumerators.Count);
                foreach (PartitionRangePageAsyncEnumerator <TPage, TState> enumerator in enumerators)
                {
                    feedRangeAndStates.Add((enumerator.Range, enumerator.State));
                }

                crossPartitionState = new CrossPartitionState <TState>(feedRangeAndStates);
            }

            this.Current = TryCatch <CrossPartitionPage <TPage, TState> > .FromResult(
                new CrossPartitionPage <TPage, TState>(currentPaginator.Current.Result, crossPartitionState));

            this.CurrentRange = currentPaginator.Range;
            return(true);
        }
Ejemplo n.º 14
0
 public ChangeFeedStartFromContinuationAndFeedRange(string etag, FeedRangeInternal feedRange)
     : base(feedRange)
 {
     this.Etag = etag;
 }
Ejemplo n.º 15
0
        public async ValueTask <bool> MoveNextAsync()
        {
            this.cancellationToken.ThrowIfCancellationRequested();
            if (this.bufferedException.HasValue)
            {
                this.Current           = this.bufferedException.Value;
                this.bufferedException = null;
                return(true);
            }

            if (!await this.crossPartitionEnumerator.MoveNextAsync())
            {
                throw new InvalidOperationException("ChangeFeed should always have a next page.");
            }

            TryCatch <CrossPartitionPage <ChangeFeedPage, ChangeFeedState> > monadicCrossPartitionPage = this.crossPartitionEnumerator.Current;

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

                return(true);
            }

            CrossPartitionPage <ChangeFeedPage, ChangeFeedState> crossPartitionPage = monadicCrossPartitionPage.Result;
            ChangeFeedPage backendPage = crossPartitionPage.Page;

            if (backendPage is ChangeFeedNotModifiedPage)
            {
                // Keep draining the cross partition enumerator until
                // We get a non 304 page or we loop back to the same range or run into an exception
                FeedRangeInternal originalRange      = this.crossPartitionEnumerator.CurrentRange;
                double            totalRequestCharge = backendPage.RequestCharge;
                do
                {
                    if (!await this.crossPartitionEnumerator.MoveNextAsync())
                    {
                        throw new InvalidOperationException("ChangeFeed should always have a next page.");
                    }

                    monadicCrossPartitionPage = this.crossPartitionEnumerator.Current;
                    if (monadicCrossPartitionPage.Failed)
                    {
                        // Buffer the exception, since we need to return the request charge so far.
                        this.bufferedException = TryCatch <ChangeFeedPage> .FromException(monadicCrossPartitionPage.Exception);
                    }
                    else
                    {
                        crossPartitionPage  = monadicCrossPartitionPage.Result;
                        backendPage         = crossPartitionPage.Page;
                        totalRequestCharge += backendPage.RequestCharge;
                    }
                }while (!(backendPage is ChangeFeedSuccessPage ||
                          this.crossPartitionEnumerator.CurrentRange.Equals(originalRange) ||
                          this.bufferedException.HasValue));

                // Create a page with the aggregated request charge
                if (backendPage is ChangeFeedSuccessPage changeFeedSuccessPage)
                {
                    backendPage = new ChangeFeedSuccessPage(
                        changeFeedSuccessPage.Content,
                        totalRequestCharge,
                        changeFeedSuccessPage.ActivityId,
                        changeFeedSuccessPage.State);
                }
                else
                {
                    backendPage = new ChangeFeedNotModifiedPage(
                        totalRequestCharge,
                        backendPage.ActivityId,
                        backendPage.State);
                }
            }

            CrossPartitionState <ChangeFeedState> crossPartitionState = crossPartitionPage.State;

            List <CosmosElement> changeFeedContinuationTokens = new List <CosmosElement>();

            foreach ((FeedRangeInternal range, ChangeFeedState state)rangeAndState in crossPartitionState.Value)
            {
                ChangeFeedContinuationToken changeFeedContinuationToken = new ChangeFeedContinuationToken(
                    rangeAndState.range,
                    rangeAndState.state);
                CosmosElement cosmosElementChangeFeedContinuationToken = ChangeFeedContinuationToken.ToCosmosElement(changeFeedContinuationToken);
                changeFeedContinuationTokens.Add(cosmosElementChangeFeedContinuationToken);
            }

            CosmosArray     cosmosElementTokens = CosmosArray.Create(changeFeedContinuationTokens);
            ChangeFeedState state = ChangeFeedState.Continuation(cosmosElementTokens);
            ChangeFeedPage  compositePage;

            if (backendPage is ChangeFeedSuccessPage successPage)
            {
                compositePage = new ChangeFeedSuccessPage(
                    successPage.Content,
                    successPage.RequestCharge,
                    successPage.ActivityId,
                    state);
            }
            else
            {
                compositePage = new ChangeFeedNotModifiedPage(
                    backendPage.RequestCharge,
                    backendPage.ActivityId,
                    state);
            }

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

            return(true);
        }
Ejemplo n.º 16
0
        public async ValueTask <bool> MoveNextAsync(ITrace trace)
        {
            this.cancellationToken.ThrowIfCancellationRequested();

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

            using (ITrace changeFeedMoveNextTrace = trace.StartChild("ChangeFeed MoveNextAsync", TraceComponent.ChangeFeed, TraceLevel.Info))
            {
                if (this.bufferedException.HasValue)
                {
                    this.Current           = this.bufferedException.Value;
                    this.bufferedException = null;
                    return(true);
                }

                if (!await this.crossPartitionEnumerator.MoveNextAsync(changeFeedMoveNextTrace))
                {
                    throw new InvalidOperationException("ChangeFeed should always have a next page.");
                }

                TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > monadicCrossPartitionPage = this.crossPartitionEnumerator.Current;
                if (monadicCrossPartitionPage.Failed)
                {
                    this.Current = TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > .FromException(monadicCrossPartitionPage.Exception);

                    return(true);
                }

                CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> crossFeedRangePage = monadicCrossPartitionPage.Result;
                ChangeFeedPage backendPage = crossFeedRangePage.Page;
                if (backendPage is ChangeFeedNotModifiedPage)
                {
                    // Keep draining the cross partition enumerator until
                    // We get a non 304 page or we loop back to the same range or run into an exception
                    FeedRangeInternal originalRange = this.crossPartitionEnumerator.CurrentRange;
                    // No point on draining when the state has 1 range
                    if (!IsNextRangeEqualToOriginal(this.crossPartitionEnumerator, originalRange))
                    {
                        using (ITrace drainNotModifedPages = changeFeedMoveNextTrace.StartChild("Drain NotModified Pages", TraceComponent.ChangeFeed, TraceLevel.Info))
                        {
                            double totalRequestCharge = backendPage.RequestCharge;
                            do
                            {
                                if (!await this.crossPartitionEnumerator.MoveNextAsync(drainNotModifedPages))
                                {
                                    throw new InvalidOperationException("ChangeFeed should always have a next page.");
                                }

                                monadicCrossPartitionPage = this.crossPartitionEnumerator.Current;
                                if (monadicCrossPartitionPage.Failed)
                                {
                                    // Buffer the exception, since we need to return the request charge so far.
                                    this.bufferedException = TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > .FromException(monadicCrossPartitionPage.Exception);
                                }
                                else
                                {
                                    crossFeedRangePage  = monadicCrossPartitionPage.Result;
                                    backendPage         = crossFeedRangePage.Page;
                                    totalRequestCharge += backendPage.RequestCharge;
                                }
                            }while (!(backendPage is ChangeFeedSuccessPage ||
                                      IsNextRangeEqualToOriginal(this.crossPartitionEnumerator, originalRange) ||
                                      this.bufferedException.HasValue));

                            // Create a page with the aggregated request charge
                            if (backendPage is ChangeFeedSuccessPage changeFeedSuccessPage)
                            {
                                backendPage = new ChangeFeedSuccessPage(
                                    changeFeedSuccessPage.Content,
                                    totalRequestCharge,
                                    changeFeedSuccessPage.ActivityId,
                                    changeFeedSuccessPage.AdditionalHeaders,
                                    changeFeedSuccessPage.State);
                            }
                            else
                            {
                                backendPage = new ChangeFeedNotModifiedPage(
                                    totalRequestCharge,
                                    backendPage.ActivityId,
                                    backendPage.AdditionalHeaders,
                                    backendPage.State);
                            }
                        }
                    }
                }

                crossFeedRangePage = new CrossFeedRangePage <ChangeFeedPage, ChangeFeedState>(
                    backendPage,
                    crossFeedRangePage.State);

                this.Current = TryCatch <CrossFeedRangePage <ChangeFeedPage, ChangeFeedState> > .FromResult(crossFeedRangePage);

                return(true);
            }
        }
        public async Task ChangeFeedIteratorCore_OfT_ReadNextAsync()
        {
            string          continuation    = "TBD";
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK);

            responseMessage.Headers.ETag = continuation;
            responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1";

            Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>();

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <string>(),
                       It.IsAny <Documents.ResourceType>(),
                       It.IsAny <Documents.OperationType>(),
                       It.IsAny <RequestOptions>(),
                       It.IsAny <ContainerInternal>(),
                       It.IsAny <PartitionKey?>(),
                       It.IsAny <Stream>(),
                       It.IsAny <Action <RequestMessage> >(),
                       It.IsAny <CosmosDiagnosticsContext>(),
                       It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(responseMessage));

            ContainerInternal containerCore = Mock.Of <ContainerInternal>();

            Mock.Get(containerCore)
            .Setup(c => c.ClientContext)
            .Returns(cosmosClientContext.Object);
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);
            Mock.Get(feedToken)
            .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()))
            .Returns(Documents.ShouldRetryResult.NoRetry());

            ChangeFeedIteratorCore changeFeedIteratorCore = CreateWithCustomFeedToken(containerCore, feedToken);

            bool creatorCalled = false;
            Func <ResponseMessage, FeedResponse <dynamic> > creator = (ResponseMessage r) =>
            {
                creatorCalled = true;
                return(Mock.Of <FeedResponse <dynamic> >());
            };

            FeedIteratorCore <dynamic> changeFeedIteratorCoreOfT = new FeedIteratorCore <dynamic>(changeFeedIteratorCore, creator);
            FeedResponse <dynamic>     response = await changeFeedIteratorCoreOfT.ReadNextAsync();

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once);
            Mock.Get(feedToken)
            .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once);

            Assert.IsTrue(creatorCalled, "Response creator not called");
        }
        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);
            }
        }
        public async Task ChangeFeedIteratorCore_Retries()
        {
            string          continuation    = "TBD";
            ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK);

            responseMessage.Headers.ETag = continuation;
            responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1";

            Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>();

            cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions());
            cosmosClientContext
            .Setup(c => c.ProcessResourceOperationStreamAsync(
                       It.IsAny <string>(),
                       It.IsAny <Documents.ResourceType>(),
                       It.IsAny <Documents.OperationType>(),
                       It.IsAny <RequestOptions>(),
                       It.IsAny <ContainerInternal>(),
                       It.IsAny <PartitionKey?>(),
                       It.IsAny <Stream>(),
                       It.IsAny <Action <RequestMessage> >(),
                       It.IsAny <CosmosDiagnosticsContext>(),
                       It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(responseMessage));

            ContainerInternal containerCore = Mock.Of <ContainerInternal>();

            Mock.Get(containerCore)
            .Setup(c => c.ClientContext)
            .Returns(cosmosClientContext.Object);
            FeedRangeInternal range = Mock.Of <FeedRangeInternal>();

            Mock.Get(range)
            .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>()));
            FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>();

            Mock.Get(feedToken)
            .Setup(f => f.FeedRange)
            .Returns(range);

            Mock.Get(feedToken)
            .SetupSequence(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.RetryAfter(TimeSpan.Zero)))
            .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry()));
            Mock.Get(feedToken)
            .SetupSequence(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()))
            .Returns(Documents.ShouldRetryResult.RetryAfter(TimeSpan.Zero))
            .Returns(Documents.ShouldRetryResult.NoRetry())
            .Returns(Documents.ShouldRetryResult.NoRetry());

            ChangeFeedIteratorCore changeFeedIteratorCore = CreateWithCustomFeedToken(containerCore, feedToken);
            ResponseMessage        response = await changeFeedIteratorCore.ReadNextAsync();

            Mock.Get(feedToken)
            .Verify(f => f.ReplaceContinuation(It.IsAny <string>()), Times.Once);

            Mock.Get(feedToken)
            .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Exactly(2));
            Mock.Get(feedToken)
            .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Exactly(3));

            Mock.Get(cosmosClientContext.Object)
            .Verify(c => c.ProcessResourceOperationStreamAsync(
                        It.IsAny <string>(),
                        It.IsAny <Documents.ResourceType>(),
                        It.IsAny <Documents.OperationType>(),
                        It.IsAny <RequestOptions>(),
                        It.IsAny <ContainerInternal>(),
                        It.IsAny <PartitionKey?>(),
                        It.IsAny <Stream>(),
                        It.IsAny <Action <RequestMessage> >(),
                        It.IsAny <CosmosDiagnosticsContext>(),
                        It.IsAny <CancellationToken>()), Times.Exactly(3));
        }
Ejemplo n.º 20
0
 protected PartitionRangePageAsyncEnumerator(FeedRangeInternal range, CancellationToken cancellationToken, TState state = default)
 {
     this.Range             = range ?? throw new ArgumentNullException(nameof(range));
     this.State             = state;
     this.cancellationToken = cancellationToken;
 }
 public ChangeFeedStartFromContinuationAndFeedRange(string etag, FeedRangeInternal feedRange)
 {
     this.Etag      = etag;
     this.FeedRange = feedRange ?? throw new ArgumentNullException(nameof(feedRange));
 }
        public static QueryIterator Create(
            ContainerCore containerCore,
            CosmosQueryClient client,
            CosmosClientContext clientContext,
            SqlQuerySpec sqlQuerySpec,
            string continuationToken,
            FeedRangeInternal feedRangeInternal,
            QueryRequestOptions queryRequestOptions,
            string resourceLink,
            bool isContinuationExpected,
            bool allowNonValueAggregateQuery,
            bool forcePassthrough,
            PartitionedQueryExecutionInfo partitionedQueryExecutionInfo)
        {
            if (queryRequestOptions == null)
            {
                queryRequestOptions = new QueryRequestOptions();
            }

            CosmosQueryContextCore cosmosQueryContext = new CosmosQueryContextCore(
                client: client,
                resourceTypeEnum: Documents.ResourceType.Document,
                operationType: Documents.OperationType.Query,
                resourceType: typeof(QueryResponseCore),
                resourceLink: resourceLink,
                isContinuationExpected: isContinuationExpected,
                allowNonValueAggregateQuery: allowNonValueAggregateQuery,
                correlatedActivityId: Guid.NewGuid());

            NetworkAttachedDocumentContainer networkAttachedDocumentContainer = new NetworkAttachedDocumentContainer(
                containerCore,
                client,
                queryRequestOptions);
            DocumentContainer documentContainer = new DocumentContainer(networkAttachedDocumentContainer);

            CosmosElement requestContinuationToken;

            switch (queryRequestOptions.ExecutionEnvironment.GetValueOrDefault(ExecutionEnvironment.Client))
            {
            case ExecutionEnvironment.Client:
                if (continuationToken != null)
                {
                    TryCatch <CosmosElement> tryParse = CosmosElement.Monadic.Parse(continuationToken);
                    if (tryParse.Failed)
                    {
                        return(new QueryIterator(
                                   cosmosQueryContext,
                                   new FaultedQueryPipelineStage(
                                       new MalformedContinuationTokenException(
                                           message: $"Malformed Continuation Token: {continuationToken}",
                                           innerException: tryParse.Exception)),
                                   queryRequestOptions.CosmosSerializationFormatOptions,
                                   queryRequestOptions,
                                   clientContext));
                    }

                    requestContinuationToken = tryParse.Result;
                }
                else
                {
                    requestContinuationToken = null;
                }
                break;

            case ExecutionEnvironment.Compute:
                requestContinuationToken = queryRequestOptions.CosmosElementContinuationToken;
                break;

            default:
                throw new ArgumentOutOfRangeException($"Unknown {nameof(ExecutionEnvironment)}: {queryRequestOptions.ExecutionEnvironment.Value}.");
            }

            CosmosQueryExecutionContextFactory.InputParameters inputParameters = new CosmosQueryExecutionContextFactory.InputParameters(
                sqlQuerySpec: sqlQuerySpec,
                initialUserContinuationToken: requestContinuationToken,
                initialFeedRange: feedRangeInternal,
                maxConcurrency: queryRequestOptions.MaxConcurrency,
                maxItemCount: queryRequestOptions.MaxItemCount,
                maxBufferedItemCount: queryRequestOptions.MaxBufferedItemCount,
                partitionKey: queryRequestOptions.PartitionKey,
                properties: queryRequestOptions.Properties,
                partitionedQueryExecutionInfo: partitionedQueryExecutionInfo,
                executionEnvironment: queryRequestOptions.ExecutionEnvironment,
                returnResultsInDeterministicOrder: queryRequestOptions.ReturnResultsInDeterministicOrder,
                forcePassthrough: forcePassthrough,
                testInjections: queryRequestOptions.TestSettings);

            return(new QueryIterator(
                       cosmosQueryContext,
                       CosmosQueryExecutionContextFactory.Create(documentContainer, cosmosQueryContext, inputParameters, NoOpTrace.Singleton),
                       queryRequestOptions.CosmosSerializationFormatOptions,
                       queryRequestOptions,
                       clientContext));
        }
 /// <summary>
 /// Initializes an instance of the <see cref="ChangeFeedStartFromBeginning"/> class.
 /// </summary>
 /// <param name="feedRange">The (optional) range to start from.</param>
 public ChangeFeedStartFromBeginning(FeedRangeInternal feedRange)
     : base()
 {
     this.FeedRange = feedRange ?? throw new ArgumentNullException(nameof(feedRange));
 }
Ejemplo n.º 24
0
 public Task <TryCatch> MonadicSplitAsync(
     FeedRangeInternal feedRange,
     CancellationToken cancellationToken) => this.monadicDocumentContainer.MonadicSplitAsync(
     feedRange,
     cancellationToken);
Ejemplo n.º 25
0
 public ChangeFeedContinuationToken(FeedRangeInternal feedRange, ChangeFeedState changeFeedState)
 {
     this.Range = feedRange ?? throw new ArgumentNullException(nameof(feedRange));
     this.State = changeFeedState ?? throw new ArgumentNullException(nameof(changeFeedState));
 }