Ejemplo n.º 1
0
        public async Task FeedRangePKRangeId_GetEffectiveRangesAsync()
        {
            Documents.PartitionKeyRange partitionKeyRange = new Documents.PartitionKeyRange()
            {
                Id = Guid.NewGuid().ToString(), MinInclusive = "AA", MaxExclusive = "BB"
            };
            FeedRangePartitionKeyRange feedRangePartitionKeyRange = new FeedRangePartitionKeyRange(partitionKeyRange.Id);

            Routing.IRoutingMapProvider routingProvider = Mock.Of <Routing.IRoutingMapProvider>();
            Mock.Get(routingProvider)
            .Setup(f => f.TryGetPartitionKeyRangeByIdAsync(It.IsAny <string>(), It.Is <string>(s => s == partitionKeyRange.Id), It.IsAny <bool>()))
            .ReturnsAsync(partitionKeyRange);
            List <Documents.Routing.Range <string> > ranges = await feedRangePartitionKeyRange.GetEffectiveRangesAsync(routingProvider, null, null);

            Assert.AreEqual(1, ranges.Count);
            Assert.AreEqual(partitionKeyRange.ToRange().Min, ranges[0].Min);
            Assert.AreEqual(partitionKeyRange.ToRange().Max, ranges[0].Max);
            Mock.Get(routingProvider)
            .Verify(f => f.TryGetPartitionKeyRangeByIdAsync(It.IsAny <string>(), It.Is <string>(s => s == partitionKeyRange.Id), It.IsAny <bool>()), Times.Once);
        }
Ejemplo n.º 2
0
        private void HandleSplit(IReadOnlyList <Documents.PartitionKeyRange> keyRanges)
        {
            if (keyRanges == null)
            {
                throw new ArgumentNullException(nameof(keyRanges));
            }

            // Update current
            Documents.PartitionKeyRange firstRange = keyRanges[0];
            this.currentToken.Range = new Documents.Routing.Range <string>(firstRange.MinInclusive, firstRange.MaxExclusive, true, false);
            // Add children
            foreach (Documents.PartitionKeyRange keyRange in keyRanges.Skip(1))
            {
                this.compositeContinuationTokens.Enqueue(new CompositeContinuationToken()
                {
                    Range = new Documents.Routing.Range <string>(keyRange.MinInclusive, keyRange.MaxExclusive, true, false),
                    Token = this.currentToken.Token
                });
            }
        }
        internal override async Task <List <Documents.Routing.Range <string> > > GetEffectiveRangesAsync(
            IRoutingMapProvider routingMapProvider,
            string containerRid,
            Documents.PartitionKeyDefinition partitionKeyDefinition,
            ITrace trace)
        {
            Documents.PartitionKeyRange pkRange = await routingMapProvider.TryGetPartitionKeyRangeByIdAsync(
                collectionResourceId : containerRid,
                partitionKeyRangeId : this.PartitionKeyRangeId,
                trace : trace,
                forceRefresh : false);

            if (pkRange == null)
            {
                // Try with a refresh
                pkRange = await routingMapProvider.TryGetPartitionKeyRangeByIdAsync(
                    collectionResourceId : containerRid,
                    partitionKeyRangeId : this.PartitionKeyRangeId,
                    trace : trace,
                    forceRefresh : true);
            }

            if (pkRange == null)
            {
                throw CosmosExceptionFactory.Create(
                          statusCode: HttpStatusCode.Gone,
                          message: $"The PartitionKeyRangeId: \"{this.PartitionKeyRangeId}\" is not valid for the current container {containerRid} .",
                          stackTrace: string.Empty,
                          headers: new Headers()
                {
                    SubStatusCode = SubStatusCodes.PartitionKeyRangeGone,
                },
                          error: null,
                          innerException: null,
                          trace: NoOpTrace.Singleton);
            }

            return(new List <Documents.Routing.Range <string> > {
                pkRange.ToRange()
            });
        }
Ejemplo n.º 4
0
 private void CreateChildRanges(IReadOnlyList <Documents.PartitionKeyRange> keyRanges)
 {
     if (keyRanges == null)
     {
         throw new ArgumentNullException(nameof(keyRanges));
     }
     // Update current
     Documents.PartitionKeyRange firstRange = keyRanges[0];
     this.CurrentToken.Range = new Documents.Routing.Range <string>(firstRange.MinInclusive, firstRange.MaxExclusive, true, false);
     if (FeedRangeCompositeContinuation.TryParseAsCompositeContinuationToken(
             this.CurrentToken.Token,
             out CompositeContinuationToken continuationAsComposite))
     {
         // Update the internal composite continuation
         continuationAsComposite.Range = this.CurrentToken.Range;
         this.CurrentToken.Token       = JsonConvert.SerializeObject(continuationAsComposite);
         // Add children
         foreach (Documents.PartitionKeyRange keyRange in keyRanges.Skip(1))
         {
             continuationAsComposite.Range = keyRange.ToRange();
             this.CompositeContinuationTokens.Enqueue(
                 FeedRangeCompositeContinuation.CreateCompositeContinuationTokenForRange(
                     keyRange.MinInclusive,
                     keyRange.MaxExclusive,
                     JsonConvert.SerializeObject(continuationAsComposite)));
         }
     }
     else
     {
         // Add children
         foreach (Documents.PartitionKeyRange keyRange in keyRanges.Skip(1))
         {
             this.CompositeContinuationTokens.Enqueue(
                 FeedRangeCompositeContinuation.CreateCompositeContinuationTokenForRange(
                     keyRange.MinInclusive,
                     keyRange.MaxExclusive,
                     this.CurrentToken.Token));
         }
     }
 }
        public override async Task <List <Documents.Routing.Range <string> > > GetEffectiveRangesAsync(
            IRoutingMapProvider routingMapProvider,
            string containerRid,
            Documents.PartitionKeyDefinition partitionKeyDefinition)
        {
            Documents.PartitionKeyRange pkRange = await routingMapProvider.TryGetPartitionKeyRangeByIdAsync(
                collectionResourceId : containerRid,
                partitionKeyRangeId : this.PartitionKeyRangeId,
                forceRefresh : false);

            if (pkRange == null)
            {
                // Try with a refresh
                pkRange = await routingMapProvider.TryGetPartitionKeyRangeByIdAsync(
                    collectionResourceId : containerRid,
                    partitionKeyRangeId : this.PartitionKeyRangeId,
                    forceRefresh : true);
            }

            if (pkRange == null)
            {
                throw CosmosExceptionFactory.Create(
                          statusCode: HttpStatusCode.Gone,
                          subStatusCode: (int)SubStatusCodes.PartitionKeyRangeGone,
                          message: $"The PartitionKeyRangeId: \"{this.PartitionKeyRangeId}\" is not valid for the current container {containerRid} .",
                          stackTrace: string.Empty,
                          activityId: string.Empty,
                          requestCharge: 0,
                          retryAfter: null,
                          headers: null,
                          diagnosticsContext: null,
                          error: null,
                          innerException: null);
            }

            return(new List <Documents.Routing.Range <string> > {
                pkRange.ToRange()
            });
        }
Ejemplo n.º 6
0
        public async Task FeedRangeEPK_GetPartitionKeyRangesAsync()
        {
            Documents.Routing.Range <string> range             = new Documents.Routing.Range <string>("AA", "BB", true, false);
            Documents.PartitionKeyRange      partitionKeyRange = new Documents.PartitionKeyRange()
            {
                Id = Guid.NewGuid().ToString(), MinInclusive = range.Min, MaxExclusive = range.Max
            };
            FeedRangePartitionKeyRange feedRangePartitionKeyRange = new FeedRangePartitionKeyRange(partitionKeyRange.Id);
            IRoutingMapProvider        routingProvider            = Mock.Of <IRoutingMapProvider>();

            Mock.Get(routingProvider)
            .Setup(f => f.TryGetOverlappingRangesAsync(It.IsAny <string>(), It.Is <Documents.Routing.Range <string> >(s => s == range), It.IsAny <bool>()))
            .ReturnsAsync(new List <Documents.PartitionKeyRange>()
            {
                partitionKeyRange
            });

            FeedRangeEpk         feedRangeEPK = new FeedRangeEpk(range);
            IEnumerable <string> pkRanges     = await feedRangeEPK.GetPartitionKeyRangesAsync(routingProvider, null, null, default(CancellationToken));

            Assert.AreEqual(1, pkRanges.Count());
            Assert.AreEqual(partitionKeyRange.Id, pkRanges.First());
        }
Ejemplo n.º 7
0
        public async Task FeedRange_PKRangeId_Serialization()
        {
            string continuationToken = "TBD";
            string containerRid      = Guid.NewGuid().ToString();
            DocumentFeedResponse <Documents.PartitionKeyRange> ranges = await this.Container.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(this.Container.LinkUri);

            Documents.PartitionKeyRange oneRange = ranges.First();

            FeedRangePartitionKeyRange     original = new FeedRangePartitionKeyRange(oneRange.Id);
            FeedRangeCompositeContinuation feedRangeSimpleContinuation = new FeedRangeCompositeContinuation(containerRid, original, new List <Documents.Routing.Range <string> >()
            {
                oneRange.ToRange()
            }, continuationToken);
            string serialized = feedRangeSimpleContinuation.ToString();

            Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation feedRangeContinuation));
            FeedRangeCompositeContinuation deserialized          = feedRangeContinuation as FeedRangeCompositeContinuation;
            FeedRangePartitionKeyRange     deserializedFeedRange = deserialized.FeedRange as FeedRangePartitionKeyRange;

            Assert.IsNotNull(deserialized, "Error deserializing to FeedRangePartitionKeyRange");
            Assert.AreEqual(original.PartitionKeyRangeId, deserializedFeedRange.PartitionKeyRangeId);
            Assert.AreEqual(continuationToken, deserialized.GetContinuation());
        }
        /// <summary>
        /// Initializes a new instance of the ItemProducer class.
        /// </summary>
        /// <param name="queryContext">request context</param>
        /// <param name="querySpecForInit">query spec for initialization</param>
        /// <param name="partitionKeyRange">The partition key range.</param>
        /// <param name="produceAsyncCompleteCallback">The callback to call once you are done fetching.</param>
        /// <param name="equalityComparer">The comparer to use to determine whether the producer has seen a new document.</param>
        /// <param name="testFlags">Flags used to help faciliate testing.</param>
        /// <param name="initialPageSize">The initial page size.</param>
        /// <param name="initialContinuationToken">The initial continuation token.</param>
        public ItemProducer(
            CosmosQueryContext queryContext,
            SqlQuerySpec querySpecForInit,
            PartitionKeyRange partitionKeyRange,
            ProduceAsyncCompleteDelegate produceAsyncCompleteCallback,
            IEqualityComparer <CosmosElement> equalityComparer,
            TestInjections testFlags,
            long initialPageSize            = 50,
            string initialContinuationToken = null)
        {
            this.bufferedPages = new AsyncCollection <QueryResponseCore>();

            // We use a binary semaphore to get the behavior of a mutex,
            // since fetching documents from the backend using a continuation token is a critical section.
            this.fetchSemaphore               = new SemaphoreSlim(1, 1);
            this.queryContext                 = queryContext;
            this.querySpecForInit             = querySpecForInit;
            this.PartitionKeyRange            = partitionKeyRange ?? throw new ArgumentNullException(nameof(partitionKeyRange));
            this.produceAsyncCompleteCallback = produceAsyncCompleteCallback ?? throw new ArgumentNullException(nameof(produceAsyncCompleteCallback));
            this.equalityComparer             = equalityComparer ?? throw new ArgumentNullException(nameof(equalityComparer));
            this.pageSize = initialPageSize;
            this.CurrentContinuationToken  = initialContinuationToken;
            this.BackendContinuationToken  = initialContinuationToken;
            this.PreviousContinuationToken = initialContinuationToken;
            if (!string.IsNullOrEmpty(initialContinuationToken))
            {
                this.hasStartedFetching = true;
                this.IsActive           = true;
            }

            this.fetchSchedulingMetrics = new SchedulingStopwatch();
            this.fetchSchedulingMetrics.Ready();

            this.testFlags = testFlags;

            this.HasMoreResults = true;
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Matches ranges to their corresponding continuation token.
        /// Note that most ranges don't have a corresponding continuation token, so their value will be set to null.
        /// Also note that in the event of a split two or more ranges will match to the same continuation token.
        /// </summary>
        /// <typeparam name="PartitionedToken">The type of token we are matching with.</typeparam>
        /// <param name="partitionKeyRanges">The partition key ranges to match.</param>
        /// <param name="partitionedContinuationTokens">The continuation tokens to match with.</param>
        /// <returns>A dictionary of ranges matched with their continuation tokens.</returns>
        public static IReadOnlyDictionary <PartitionKeyRange, PartitionedToken> MatchRangesToContinuationTokens <PartitionedToken>(
            ReadOnlyMemory <PartitionKeyRange> partitionKeyRanges,
            IReadOnlyList <PartitionedToken> partitionedContinuationTokens)
            where PartitionedToken : IPartitionedToken
        {
            if (partitionedContinuationTokens == null)
            {
                throw new ArgumentNullException(nameof(partitionedContinuationTokens));
            }

            Dictionary <PartitionKeyRange, PartitionedToken> partitionKeyRangeToToken = new Dictionary <PartitionKeyRange, PartitionedToken>();
            ReadOnlySpan <PartitionKeyRange> partitionKeyRangeSpan = partitionKeyRanges.Span;

            for (int i = 0; i < partitionKeyRangeSpan.Length; i++)
            {
                PartitionKeyRange partitionKeyRange = partitionKeyRangeSpan[i];
                foreach (PartitionedToken partitionedToken in partitionedContinuationTokens)
                {
                    // See if continuation token includes the range
                    if ((partitionKeyRange.MinInclusive.CompareTo(partitionedToken.PartitionRange.Min) >= 0) &&
                        (partitionKeyRange.MaxExclusive.CompareTo(partitionedToken.PartitionRange.Max) <= 0))
                    {
                        partitionKeyRangeToToken[partitionKeyRange] = partitionedToken;
                        break;
                    }
                }

                if (!partitionKeyRangeToToken.ContainsKey(partitionKeyRange))
                {
                    // Could not find a matching token so just set it to null
                    partitionKeyRangeToToken[partitionKeyRange] = default;
                }
            }

            return(partitionKeyRangeToToken);
        }
        /// <summary>
        /// <para>
        /// If a query encounters split up resuming using continuation, we need to regenerate the continuation tokens.
        /// Specifically, since after split we will have new set of ranges, we need to remove continuation token for the
        /// parent partition and introduce continuation token for the child partitions.
        /// </para>
        /// <para>
        /// This function does that. Also in that process, we also check validity of the input continuation tokens. For example,
        /// even after split the boundary ranges of the child partitions should match with the parent partitions. If the Min and Max
        /// range of a target partition in the continuation token was Min1 and Max1. Then the Min and Max range info for the two
        /// corresponding child partitions C1Min, C1Max, C2Min, and C2Max should follow the constrain below:
        ///  PMax = C2Max > C2Min > C1Max > C1Min = PMin.
        /// </para>
        /// </summary>
        /// <param name="partitionKeyRanges">The partition key ranges to extract continuation tokens for.</param>
        /// <param name="suppliedContinuationTokens">The continuation token that the user supplied.</param>
        /// <param name="targetRangeToContinuationTokenMap">The output dictionary of partition key range to continuation token.</param>
        /// <typeparam name="TContinuationToken">The type of continuation token to generate.</typeparam>
        /// <Remarks>
        /// The code assumes that merge doesn't happen and
        /// </Remarks>
        /// <returns>The index of the partition whose MinInclusive is equal to the suppliedContinuationTokens</returns>
        protected int FindTargetRangeAndExtractContinuationTokens <TContinuationToken>(
            List <PartitionKeyRange> partitionKeyRanges,
            IEnumerable <Tuple <TContinuationToken, Documents.Routing.Range <string> > > suppliedContinuationTokens,
            out Dictionary <string, TContinuationToken> targetRangeToContinuationTokenMap)
        {
            if (partitionKeyRanges == null)
            {
                throw new ArgumentNullException(nameof(partitionKeyRanges));
            }

            if (partitionKeyRanges.Count < 1)
            {
                throw new ArgumentException(nameof(partitionKeyRanges));
            }

            foreach (PartitionKeyRange partitionKeyRange in partitionKeyRanges)
            {
                if (partitionKeyRange == null)
                {
                    throw new ArgumentException(nameof(partitionKeyRanges));
                }
            }

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

            if (suppliedContinuationTokens.Count() < 1)
            {
                throw new ArgumentException(nameof(suppliedContinuationTokens));
            }

            if (suppliedContinuationTokens.Count() > partitionKeyRanges.Count)
            {
                throw new ArgumentException($"{nameof(suppliedContinuationTokens)} can not have more elements than {nameof(partitionKeyRanges)}.");
            }

            targetRangeToContinuationTokenMap = new Dictionary <string, TContinuationToken>();

            // Find the minimum index.
            Tuple <TContinuationToken, Documents.Routing.Range <string> > firstContinuationTokenAndRange = suppliedContinuationTokens
                                                                                                           .OrderBy((tuple) => tuple.Item2.Min)
                                                                                                           .First();
            TContinuationToken firstContinuationToken = firstContinuationTokenAndRange.Item1;
            PartitionKeyRange  firstContinuationRange = new PartitionKeyRange
            {
                MinInclusive = firstContinuationTokenAndRange.Item2.Min,
                MaxExclusive = firstContinuationTokenAndRange.Item2.Max
            };

            int minIndex = partitionKeyRanges.BinarySearch(
                firstContinuationRange,
                Comparer <PartitionKeyRange> .Create((range1, range2) => string.CompareOrdinal(range1.MinInclusive, range2.MinInclusive)));

            if (minIndex < 0)
            {
                throw new ArgumentException($"{RMResources.InvalidContinuationToken} - Could not find continuation token: {firstContinuationToken}");
            }

            foreach (Tuple <TContinuationToken, Documents.Routing.Range <string> > suppledContinuationToken in suppliedContinuationTokens)
            {
                // find what ranges make up the supplied continuation token
                TContinuationToken continuationToken   = suppledContinuationToken.Item1;
                Documents.Routing.Range <string> range = suppledContinuationToken.Item2;

                IEnumerable <PartitionKeyRange> replacementRanges = partitionKeyRanges
                                                                    .Where((partitionKeyRange) =>
                                                                           string.CompareOrdinal(range.Min, partitionKeyRange.MinInclusive) <= 0 &&
                                                                           string.CompareOrdinal(range.Max, partitionKeyRange.MaxExclusive) >= 0)
                                                                    .OrderBy((partitionKeyRange) => partitionKeyRange.MinInclusive);

                // Could not find the child ranges
                if (replacementRanges.Count() == 0)
                {
                    throw this.queryClient.CreateBadRequestException(
                              $"{RMResources.InvalidContinuationToken} - Could not find continuation token: {continuationToken}");
                }

                // PMax = C2Max > C2Min > C1Max > C1Min = PMin.
                string parentMax = range.Max;
                string child2Max = replacementRanges.Last().MaxExclusive;
                string child2Min = replacementRanges.Last().MinInclusive;
                string child1Max = replacementRanges.First().MaxExclusive;
                string child1Min = replacementRanges.First().MinInclusive;
                string parentMin = range.Min;

                if (!(parentMax == child2Max &&
                      string.CompareOrdinal(child2Max, child2Min) >= 0 &&
                      (replacementRanges.Count() == 1 ? true : string.CompareOrdinal(child2Min, child1Max) >= 0) &&
                      string.CompareOrdinal(child1Max, child1Min) >= 0 &&
                      child1Min == parentMin))
                {
                    throw this.queryClient.CreateBadRequestException(
                              $"{RMResources.InvalidContinuationToken} - PMax = C2Max > C2Min > C1Max > C1Min = PMin: {continuationToken}");
                }

                foreach (PartitionKeyRange partitionKeyRange in replacementRanges)
                {
                    targetRangeToContinuationTokenMap.Add(partitionKeyRange.Id, continuationToken);
                }
            }

            return(minIndex);
        }
Ejemplo n.º 11
0
        public static TryCatch <PartitionMapping <PartitionedToken> > TryGetInitializationInfo <PartitionedToken>(
            IReadOnlyList <PartitionKeyRange> partitionKeyRanges,
            IReadOnlyList <PartitionedToken> partitionedContinuationTokens)
            where PartitionedToken : IPartitionedToken
        {
            if (partitionKeyRanges == null)
            {
                throw new ArgumentNullException(nameof(partitionKeyRanges));
            }

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

            if (partitionKeyRanges.Count < 1)
            {
                throw new ArgumentException(nameof(partitionKeyRanges));
            }

            if (partitionedContinuationTokens.Count < 1)
            {
                throw new ArgumentException(nameof(partitionKeyRanges));
            }

            if (partitionedContinuationTokens.Count > partitionKeyRanges.Count)
            {
                throw new ArgumentException($"{nameof(partitionedContinuationTokens)} can not have more elements than {nameof(partitionKeyRanges)}.");
            }

            // Find the continuation token for the partition we left off on:
            PartitionedToken firstContinuationToken = partitionedContinuationTokens
                                                      .OrderBy((partitionedToken) => partitionedToken.PartitionRange.Min)
                                                      .First();

            // Segment the ranges based off that:
            ReadOnlyMemory <PartitionKeyRange> sortedRanges = partitionKeyRanges
                                                              .OrderBy((partitionKeyRange) => partitionKeyRange.MinInclusive)
                                                              .ToArray();

            PartitionKeyRange firstContinuationRange = new PartitionKeyRange
            {
                MinInclusive = firstContinuationToken.PartitionRange.Min,
                MaxExclusive = firstContinuationToken.PartitionRange.Max
            };

            int matchedIndex = sortedRanges.Span.BinarySearch(
                firstContinuationRange,
                Comparer <PartitionKeyRange> .Create((range1, range2) => string.CompareOrdinal(range1.MinInclusive, range2.MinInclusive)));

            if (matchedIndex < 0)
            {
                return(TryCatch <PartitionMapping <PartitionedToken> > .FromException(
                           new MalformedContinuationTokenException(
                               $"{RMResources.InvalidContinuationToken} - Could not find continuation token: {firstContinuationToken}")));
            }

            ReadOnlyMemory <PartitionKeyRange> partitionsLeftOfTarget  = matchedIndex == 0 ? ReadOnlyMemory <PartitionKeyRange> .Empty : sortedRanges.Slice(start: 0, length: matchedIndex);
            ReadOnlyMemory <PartitionKeyRange> targetPartition         = sortedRanges.Slice(start: matchedIndex, length: 1);
            ReadOnlyMemory <PartitionKeyRange> partitionsRightOfTarget = matchedIndex == sortedRanges.Length - 1 ? ReadOnlyMemory <PartitionKeyRange> .Empty : sortedRanges.Slice(start: matchedIndex + 1);

            // Create the continuation token mapping for each region.
            IReadOnlyDictionary <PartitionKeyRange, PartitionedToken> mappingForPartitionsLeftOfTarget = MatchRangesToContinuationTokens(
                partitionsLeftOfTarget,
                partitionedContinuationTokens);
            IReadOnlyDictionary <PartitionKeyRange, PartitionedToken> mappingForTargetPartition = MatchRangesToContinuationTokens(
                targetPartition,
                partitionedContinuationTokens);
            IReadOnlyDictionary <PartitionKeyRange, PartitionedToken> mappingForPartitionsRightOfTarget = MatchRangesToContinuationTokens(
                partitionsRightOfTarget,
                partitionedContinuationTokens);

            return(TryCatch <PartitionMapping <PartitionedToken> > .FromResult(
                       new PartitionMapping <PartitionedToken>(
                           partitionsLeftOfTarget : mappingForPartitionsLeftOfTarget,
                           targetPartition : mappingForTargetPartition,
                           partitionsRightOfTarget : mappingForPartitionsRightOfTarget)));
        }
Ejemplo n.º 12
0
        protected async Task <TryCatch> TryInitializeAsync(
            string collectionRid,
            int initialPageSize,
            SqlQuerySpec querySpecForInit,
            IReadOnlyDictionary <PartitionKeyRange, string> targetRangeToContinuationMap,
            bool deferFirstPage,
            string filter,
            Func <ItemProducerTree, Task <TryCatch> > tryFilterAsync,
            CancellationToken cancellationToken)
        {
            if (collectionRid == null)
            {
                throw new ArgumentNullException(nameof(collectionRid));
            }

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

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

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

            cancellationToken.ThrowIfCancellationRequested();

            List <ItemProducerTree> itemProducerTrees = new List <ItemProducerTree>();

            foreach (KeyValuePair <PartitionKeyRange, string> rangeAndContinuationToken in targetRangeToContinuationMap)
            {
                PartitionKeyRange partitionKeyRange = rangeAndContinuationToken.Key;
                string            continuationToken = rangeAndContinuationToken.Value;
                ItemProducerTree  itemProducerTree  = new ItemProducerTree(
                    this.queryContext,
                    querySpecForInit,
                    partitionKeyRange,
                    this.OnItemProducerTreeCompleteFetching,
                    this.itemProducerForest.Comparer,
                    this.equalityComparer,
                    this.testSettings,
                    deferFirstPage,
                    collectionRid,
                    initialPageSize,
                    continuationToken)
                {
                    Filter = filter
                };

                // Prefetch if necessary, and populate consume queue.
                if (this.CanPrefetch)
                {
                    this.TryScheduleFetch(itemProducerTree);
                }

                itemProducerTrees.Add(itemProducerTree);
            }

            // Using loop fission so that we can load the document producers in parallel
            foreach (ItemProducerTree itemProducerTree in itemProducerTrees)
            {
                if (!deferFirstPage)
                {
                    while (true)
                    {
                        (bool movedToNextPage, QueryResponseCore? failureResponse) = await itemProducerTree.TryMoveNextPageAsync(cancellationToken);

                        if (failureResponse.HasValue)
                        {
                            return(TryCatch.FromException(
                                       failureResponse.Value.CosmosException));
                        }

                        if (!movedToNextPage)
                        {
                            break;
                        }

                        if (itemProducerTree.IsAtBeginningOfPage)
                        {
                            break;
                        }

                        if (itemProducerTree.TryMoveNextDocumentWithinPage())
                        {
                            break;
                        }
                    }
                }

                if (tryFilterAsync != null)
                {
                    TryCatch tryFilter = await tryFilterAsync(itemProducerTree);

                    if (!tryFilter.Succeeded)
                    {
                        return(tryFilter);
                    }
                }

                this.itemProducerForest.Enqueue(itemProducerTree);
            }

            return(TryCatch.FromResult());
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Initializes a new instance of the ItemProducerTree class.
        /// </summary>
        /// <param name="queryContext">query context.</param>
        /// <param name="querySpecForInit">query spec init.</param>
        /// <param name="partitionKeyRange">The partition key range.</param>
        /// <param name="produceAsyncCompleteCallback">Callback to invoke once a fetch finishes.</param>
        /// <param name="itemProducerTreeComparer">Comparer to determine, which tree to produce from.</param>
        /// <param name="equalityComparer">Comparer to see if we need to return the continuation token for a partition.</param>
        /// <param name="testSettings">Test flags.</param>
        /// <param name="deferFirstPage">Whether or not to defer fetching the first page.</param>
        /// <param name="collectionRid">The collection to drain from.</param>
        /// <param name="initialPageSize">The initial page size.</param>
        /// <param name="initialContinuationToken">The initial continuation token.</param>
        public ItemProducerTree(
            CosmosQueryContext queryContext,
            SqlQuerySpec querySpecForInit,
            Documents.PartitionKeyRange partitionKeyRange,
            ProduceAsyncCompleteDelegate produceAsyncCompleteCallback,
            IComparer <ItemProducerTree> itemProducerTreeComparer,
            IEqualityComparer <CosmosElement> equalityComparer,
            TestInjections testSettings,
            bool deferFirstPage,
            string collectionRid,
            long initialPageSize            = 50,
            string initialContinuationToken = null)
        {
            if (queryContext == null)
            {
                throw new ArgumentNullException($"{nameof(queryContext)}");
            }

            if (itemProducerTreeComparer == null)
            {
                throw new ArgumentNullException($"{nameof(itemProducerTreeComparer)}");
            }

            if (produceAsyncCompleteCallback == null)
            {
                throw new ArgumentNullException($"{nameof(produceAsyncCompleteCallback)}");
            }

            if (itemProducerTreeComparer == null)
            {
                throw new ArgumentNullException($"{nameof(itemProducerTreeComparer)}");
            }

            if (equalityComparer == null)
            {
                throw new ArgumentNullException($"{nameof(equalityComparer)}");
            }

            if (string.IsNullOrEmpty(collectionRid))
            {
                throw new ArgumentException($"{nameof(collectionRid)} can not be null or empty.");
            }

            this.Root = new ItemProducer(
                queryContext,
                querySpecForInit,
                partitionKeyRange,
                (itemsBuffered, resourceUnitUsage, diagnostics, requestLength, token) => produceAsyncCompleteCallback(this, itemsBuffered, resourceUnitUsage, diagnostics, requestLength, token),
                equalityComparer,
                testSettings,
                initialPageSize,
                initialContinuationToken);

            this.queryClient    = queryContext.QueryClient;
            this.children       = new PriorityQueue <ItemProducerTree>(itemProducerTreeComparer, true);
            this.deferFirstPage = deferFirstPage;
            this.collectionRid  = collectionRid;
            this.createItemProducerTreeCallback = ItemProducerTree.CreateItemProducerTreeCallback(
                queryContext,
                querySpecForInit,
                produceAsyncCompleteCallback,
                itemProducerTreeComparer,
                equalityComparer,
                testSettings,
                deferFirstPage,
                collectionRid,
                initialPageSize);
            this.executeWithSplitProofingSemaphore = new SemaphoreSlim(1, 1);
        }