public static TryCatch <PartitionMapping <PartitionedToken> > MonadicGetPartitionMapping <PartitionedToken>( IReadOnlyList <FeedRangeEpk> feedRanges, IReadOnlyList <PartitionedToken> tokens) where PartitionedToken : IPartitionedToken { if (feedRanges == null) { throw new ArgumentNullException(nameof(feedRanges)); } if (tokens == null) { throw new ArgumentNullException(nameof(tokens)); } if (feedRanges.Count < 1) { throw new ArgumentException(nameof(feedRanges)); } if (tokens.Count < 1) { throw new ArgumentException(nameof(feedRanges)); } List <FeedRangeEpk> mergedFeedRanges = MergeRangesWherePossible(feedRanges); List <(FeedRangeEpk, PartitionedToken)> splitRangesAndTokens = SplitRangesBasedOffContinuationToken(mergedFeedRanges, tokens); FeedRangeEpk targetFeedRange = GetTargetFeedRange(tokens); return(MonadicConstructPartitionMapping( splitRangesAndTokens, tokens, targetFeedRange)); }
public void FeedRangeEPK_Range() { Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("AA", "BB", true, false); FeedRangeEpk feedRangeEPK = new FeedRangeEpk(range); Assert.AreEqual(range, feedRangeEPK.Range); }
public void TestMatchRangesTocontinuationTokens_OneToNone() { FeedRangeEpk partitionKeyRange = new FeedRangeEpk( new Documents.Routing.Range <string>( min: string.Empty, max: "A", isMinInclusive: true, isMaxInclusive: false)); ParallelContinuationToken token = new ParallelContinuationToken( token: "asdf", range: new Documents.Routing.Range <string>( min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)); IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMapping = new Dictionary <FeedRangeEpk, IPartitionedToken>() { { partitionKeyRange, null }, }; ContinuationResumeLogicTests.RunMatchRangesToContinuationTokens( expectedMapping, new FeedRangeEpk[] { partitionKeyRange }, new ParallelContinuationToken[] { token }); }
public async Task <TryCatch <QueryPage> > VisitAsync( FeedRangeEpk feedRange, Arguments argument, CancellationToken cancellationToken) { // Check to see if it lines up exactly with one physical partition TryCatch <List <PartitionKeyRange> > tryGetFeedRanges = await this.documentContainer.MonadicGetChildRangeAsync( new PartitionKeyRange() { MinInclusive = feedRange.Range.Min, MaxExclusive = feedRange.Range.Max, }, cancellationToken); if (tryGetFeedRanges.Failed) { return(TryCatch <QueryPage> .FromException(tryGetFeedRanges.Exception)); } List <PartitionKeyRange> feedRanges = tryGetFeedRanges.Result; if (feedRanges.Count != 1) { // Simulate a split exception, since we don't have a partition key range id to route to. CosmosException goneException = new CosmosException( message: $"Epk Range: {feedRange.Range} is gone.", statusCode: System.Net.HttpStatusCode.Gone, subStatusCode: (int)SubStatusCodes.PartitionKeyRangeGone, activityId: Guid.NewGuid().ToString(), requestCharge: default);
public void ReadFeedIteratorCore_Create_Default() { FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(Mock.Of <ContainerInternal>(), null, null, null); FeedRangeEpk defaultRange = feedTokenIterator.FeedRangeInternal as FeedRangeEpk; Assert.AreEqual(FeedRangeEpk.FullRange.Range.Min, defaultRange.Range.Min); Assert.AreEqual(FeedRangeEpk.FullRange.Range.Max, defaultRange.Range.Max); Assert.IsNull(feedTokenIterator.FeedRangeContinuation); }
public void Visit(FeedRangeEpk feedRange, RequestMessage requestMessage) { // In case EPK has already been set by compute if (!requestMessage.Properties.ContainsKey(HandlerConstants.StartEpkString)) { requestMessage.Properties[HandlerConstants.StartEpkString] = feedRange.Range.Min; requestMessage.Properties[HandlerConstants.EndEpkString] = feedRange.Range.Max; } }
public async Task FeedRangeEPK_GetEffectiveRangesAsync() { Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("AA", "BB", true, false); FeedRangeEpk feedRangeEPK = new FeedRangeEpk(range); List <Documents.Routing.Range <string> > ranges = await feedRangeEPK.GetEffectiveRangesAsync(Mock.Of <IRoutingMapProvider>(), null, null); Assert.AreEqual(1, ranges.Count); Assert.AreEqual(range, ranges[0]); }
public void ReadFeedIteratorCore_Create_WithRange() { Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("A", "B", true, false); FeedRangeEpk feedRangeEPK = new FeedRangeEpk(range); FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(Mock.Of <ContainerInternal>(), feedRangeEPK, null, null); Assert.AreEqual(feedRangeEPK, feedTokenIterator.FeedRangeInternal); Assert.IsNull(feedTokenIterator.FeedRangeContinuation); }
public void FeedRangeEPK_ToJsonFromJson() { Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("AA", "BB", true, false); FeedRangeEpk feedRangeEPK = new FeedRangeEpk(range); string representation = feedRangeEPK.ToJsonString(); FeedRangeEpk feedRangeEPKDeserialized = Cosmos.FeedRange.FromJsonString(representation) as FeedRangeEpk; Assert.IsNotNull(feedRangeEPKDeserialized); Assert.AreEqual(feedRangeEPK.Range.Min, feedRangeEPKDeserialized.Range.Min); Assert.AreEqual(feedRangeEPK.Range.Max, feedRangeEPKDeserialized.Range.Max); }
public void FeedRangeEPK_RequestVisitor() { Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("AA", "BB", true, false); FeedRangeEpk feedRange = new FeedRangeEpk(range); RequestMessage requestMessage = new RequestMessage(); feedRange.Accept(FeedRangeRequestMessagePopulatorVisitor.Singleton, requestMessage); Assert.AreEqual(2, requestMessage.Properties.Count); Assert.AreEqual("AA", requestMessage.Properties[HandlerConstants.StartEpkString]); Assert.AreEqual("BB", requestMessage.Properties[HandlerConstants.EndEpkString]); }
public void ReadFeedIteratorCore_Create_WithContinuation() { string continuation = Guid.NewGuid().ToString(); FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(Mock.Of <ContainerInternal>(), null, continuation, null); FeedRangeEpk defaultRange = feedTokenIterator.FeedRangeInternal as FeedRangeEpk; Assert.AreEqual(FeedRangeEpk.FullRange.Range.Min, defaultRange.Range.Min); Assert.AreEqual(FeedRangeEpk.FullRange.Range.Max, defaultRange.Range.Max); Assert.IsNotNull(feedTokenIterator.FeedRangeContinuation); Assert.AreEqual(continuation, feedTokenIterator.FeedRangeContinuation.GetContinuation()); }
public void TestTryGetInitializationInfo_ResumeRightPartition() { FeedRangeEpk pkRange1 = new FeedRangeEpk( new Documents.Routing.Range <string>( min: string.Empty, max: "A", isMinInclusive: true, isMaxInclusive: false)); FeedRangeEpk pkRange2 = new FeedRangeEpk( new Documents.Routing.Range <string>( min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false)); FeedRangeEpk pkRange3 = new FeedRangeEpk( new Documents.Routing.Range <string>( min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)); ParallelContinuationToken token = new ParallelContinuationToken( token: "asdf", range: new Documents.Routing.Range <string>( min: "B", max: "C", isMinInclusive: true, isMaxInclusive: false)); IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingLeftPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>() { { pkRange1, null }, { pkRange2, null }, }; IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingTargetPartition = new Dictionary <FeedRangeEpk, IPartitionedToken>() { { pkRange3, token }, }; IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingRightPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>() { }; RunTryGetInitializationInfo( expectedMappingLeftPartitions, expectedMappingTargetPartition, expectedMappingRightPartitions, new FeedRangeEpk[] { pkRange1, pkRange2, pkRange3 }, new IPartitionedToken[] { token }); }
public void EffectivePartitionKeyRange() { Microsoft.Azure.Documents.Routing.Range <string> range = new Microsoft.Azure.Documents.Routing.Range <string>( min: "A", max: "B", isMinInclusive: true, isMaxInclusive: false); FeedRangeInternal feedRange = new FeedRangeEpk(range); AssertRoundTrip(feedRange); }
public void ResumeOnMultipleTokens() { FeedRangeEpk range = Range(min: "A", max: "F"); ParallelContinuationToken token1 = Token(min: "A", max: "C"); ParallelContinuationToken token2 = Token(min: "C", max: "E"); RunTryGetInitializationInfo( Mapping(), Mapping((Range(min: "A", max: "C"), token1)), Mapping((Range(min: "C", max: "E"), token2), (Range(min: "E", max: "F"), null)), new FeedRangeEpk[] { range, }, new IPartitionedToken[] { token1, token2 }); }
public async Task ReadFeedIteratorCore_WithNoInitialState_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("{}")); MultiRangeMockDocumentClient documentClient = new MultiRangeMockDocumentClient(); Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>(); cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions()); cosmosClientContext.Setup(c => c.DocumentClient).Returns(documentClient); 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); Mock.Get(containerCore) .Setup(c => c.GetRIDAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(Guid.NewGuid().ToString()); FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(containerCore, null, null, new QueryRequestOptions()); ResponseMessage response = await feedTokenIterator.ReadNextAsync(); Assert.IsTrue(FeedRangeContinuation.TryParse(response.ContinuationToken, out FeedRangeContinuation parsedToken)); FeedRangeCompositeContinuation feedRangeCompositeContinuation = parsedToken as FeedRangeCompositeContinuation; FeedRangeEpk feedTokenEPKRange = feedRangeCompositeContinuation.FeedRange as FeedRangeEpk; // Assert that a FeedToken for the entire range is used Assert.AreEqual(Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, feedTokenEPKRange.Range.Min); Assert.AreEqual(Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, feedTokenEPKRange.Range.Max); Assert.AreEqual(continuation, feedRangeCompositeContinuation.CompositeContinuationTokens.Peek().Token); Assert.IsFalse(feedRangeCompositeContinuation.IsDone); }
public void ResumeRightPartition() { FeedRangeEpk range1 = Range(min: string.Empty, max: "A"); FeedRangeEpk range2 = Range(min: "A", max: "B"); FeedRangeEpk range3 = Range(min: "B", max: "C"); ParallelContinuationToken token = Token(min: "B", max: "C"); RunTryGetInitializationInfo( Mapping((CombineRanges(range1, range2), null)), Mapping((range3, token)), Mapping(), new FeedRangeEpk[] { range1, range2, range3 }, new IPartitionedToken[] { token }); }
public void FeedRangeCompositeContinuation_TryParse() { const string containerRid = "containerRid"; List <Documents.Routing.Range <string> > keyRanges = new List <Documents.Routing.Range <string> >() { new Documents.Routing.Range <string>("A", "B", true, false), new Documents.Routing.Range <string>("D", "E", true, false), }; FeedRangeInternal feedRangeInternal = new FeedRangeEpk(new Documents.Routing.Range <string>("A", "E", true, false)); FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(containerRid, feedRangeInternal, keyRanges); Assert.IsTrue(FeedRangeContinuation.TryParse(token.ToString(), out _)); Assert.IsFalse(FeedRangeContinuation.TryParse("whatever", out _)); }
public void ReadFeedIteratorCore_Create_WithFeedContinuation() { string continuation = Guid.NewGuid().ToString(); FeedRangeEpk feedRangeEPK = FeedRangeEpk.FullRange; FeedRangeCompositeContinuation feedRangeSimpleContinuation = new FeedRangeCompositeContinuation(Guid.NewGuid().ToString(), feedRangeEPK, new List <Documents.Routing.Range <string> >() { feedRangeEPK.Range }, continuation); FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(Mock.Of <ContainerInternal>(), null, feedRangeSimpleContinuation.ToString(), null); FeedRangeEpk defaultRange = feedTokenIterator.FeedRangeInternal as FeedRangeEpk; Assert.AreEqual(FeedRangeEpk.FullRange.Range.Min, defaultRange.Range.Min); Assert.AreEqual(FeedRangeEpk.FullRange.Range.Max, defaultRange.Range.Max); Assert.IsNotNull(feedTokenIterator.FeedRangeContinuation); Assert.AreEqual(continuation, feedTokenIterator.FeedRangeContinuation.GetContinuation()); }
public void ResumeOnSplit_LogicalParition() { // Suppose the partition spans epk range A to E // And the user send a query with partition key that hashes to C // The the token will look like: ParallelContinuationToken token = Token(min: "A", "E"); // Now suppose there is a split that creates two partitions A to B and B to E // Now C will map to the partition that goes from B to E FeedRangeEpk range = Range(min: "B", max: "E"); RunTryGetInitializationInfo( Mapping(), Mapping((range, token)), Mapping(), new FeedRangeEpk[] { range }, new IPartitionedToken[] { token }); }
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 <ITrace>(), It.IsAny <bool>())) .ReturnsAsync(new List <Documents.PartitionKeyRange>() { partitionKeyRange }); FeedRangeEpk FeedRangeEpk = new FeedRangeEpk(range); IEnumerable <string> pkRanges = await FeedRangeEpk.GetPartitionKeyRangesAsync(routingProvider, null, null, default, NoOpTrace.Singleton);
public async Task FeedRange_EPK_Serialization() { string continuation = "TBD"; string containerRid = Guid.NewGuid().ToString(); List <FeedRange> ranges = (await this.Container.GetFeedRangesAsync()).ToList(); List <string> serializations = new List <string>(); List <FeedRangeCompositeContinuation> tokens = new List <FeedRangeCompositeContinuation>(); foreach (FeedRange range in ranges) { FeedRangeEpk feedRangeEPK = range as FeedRangeEpk; FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(containerRid, feedRangeEPK, new List <Documents.Routing.Range <string> >() { feedRangeEPK.Range }, continuation); tokens.Add(feedRangeCompositeContinuation); serializations.Add(feedRangeCompositeContinuation.ToString()); } List <FeedRangeContinuation> deserialized = new List <FeedRangeContinuation>(); foreach (string serialized in serializations) { Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation token)); deserialized.Add(token); } Assert.AreEqual(tokens.Count, deserialized.Count); for (int i = 0; i < tokens.Count; i++) { FeedRangeCompositeContinuation originalToken = tokens[i] as FeedRangeCompositeContinuation; FeedRangeCompositeContinuation deserializedToken = deserialized[i] as FeedRangeCompositeContinuation; Assert.AreEqual(originalToken.GetContinuation(), deserializedToken.GetContinuation()); Assert.AreEqual(originalToken.ContainerRid, deserializedToken.ContainerRid); Assert.AreEqual(originalToken.CompositeContinuationTokens.Count, deserializedToken.CompositeContinuationTokens.Count); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Token, deserializedToken.CompositeContinuationTokens.Peek().Token); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.Min, deserializedToken.CompositeContinuationTokens.Peek().Range.Min); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.Max, deserializedToken.CompositeContinuationTokens.Peek().Range.Max); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.IsMinInclusive, deserializedToken.CompositeContinuationTokens.Peek().Range.IsMinInclusive); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.IsMaxInclusive, deserializedToken.CompositeContinuationTokens.Peek().Range.IsMaxInclusive); } }
public void TestTryGetInitializationInfo_ResumeLogicalParition() { // Suppose the partition spans epk range A to E // And the user send a query with partition key that hashes to C // The the token will look like: ParallelContinuationToken token = new ParallelContinuationToken( token: "asdf", range: new Documents.Routing.Range <string>( min: "A", max: "E", isMinInclusive: true, isMaxInclusive: false)); // Now suppose there is a split that creates two partitions A to B and B to E // Now C will map to the partition that goes from B to E FeedRangeEpk pkRange = new FeedRangeEpk( new Documents.Routing.Range <string>( min: "B", max: "E", isMinInclusive: true, isMaxInclusive: false)); IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingLeftPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>() { }; IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingTargetPartition = new Dictionary <FeedRangeEpk, IPartitionedToken>() { { pkRange, token }, }; IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingRightPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>() { }; RunTryGetInitializationInfo( expectedMappingLeftPartitions, expectedMappingTargetPartition, expectedMappingRightPartitions, new FeedRangeEpk[] { pkRange }, new IPartitionedToken[] { token }); }
public override Task <DocumentServiceLease> CreateLeaseIfNotExistAsync( FeedRangeEpk feedRange, string continuationToken) { if (feedRange == null) { throw new ArgumentNullException(nameof(feedRange)); } string leaseToken = $"{feedRange.Range.Min}-{feedRange.Range.Max}"; DocumentServiceLeaseCoreEpk documentServiceLease = new DocumentServiceLeaseCoreEpk { LeaseId = leaseToken, LeaseToken = leaseToken, ContinuationToken = continuationToken, FeedRange = feedRange }; return(this.TryCreateDocumentServiceLeaseAsync(documentServiceLease)); }
public void ResumeOnAMerge() { // Suppose that we read from range 1 FeedRangeEpk range1 = Range(min: string.Empty, max: "A"); // Then Range 1 Merged with Range 2 FeedRangeEpk range2 = Range(min: "A", max: "B"); // And we have a continuation token for range 1 ParallelContinuationToken token = Token(min: string.Empty, max: "A"); // Then we should resume on range 1 with epk range filtering // and still have range 2 with null continuation. RunTryGetInitializationInfo( Mapping(), Mapping((range1, token)), Mapping((range2, null)), new FeedRangeEpk[] { CombineRanges(range1, range2) }, new IPartitionedToken[] { token }); }
public async Task CreatesEPKBasedLease(int factoryType) { RequestOptionsFactory requestOptionsFactory = GetRequestOptionsFactory(factoryType); string continuation = Guid.NewGuid().ToString(); DocumentServiceLeaseStoreManagerOptions options = new DocumentServiceLeaseStoreManagerOptions { HostName = Guid.NewGuid().ToString() }; FeedRangeEpk feedRangeEpk = new FeedRangeEpk(new Documents.Routing.Range <string>("AA", "BB", true, false)); Mock <DocumentServiceLeaseUpdater> mockUpdater = new Mock <DocumentServiceLeaseUpdater>(); Mock <ContainerInternal> mockedContainer = new Mock <ContainerInternal>(); mockedContainer.Setup(c => c.CreateItemStreamAsync( It.IsAny <Stream>(), It.IsAny <PartitionKey>(), It.IsAny <ItemRequestOptions>(), It.IsAny <CancellationToken>())).ReturnsAsync((Stream stream, PartitionKey partitionKey, ItemRequestOptions options, CancellationToken token) => new ResponseMessage(System.Net.HttpStatusCode.OK) { Content = stream }); DocumentServiceLeaseManagerCosmos documentServiceLeaseManagerCosmos = new DocumentServiceLeaseManagerCosmos( Mock.Of <ContainerInternal>(), mockedContainer.Object, mockUpdater.Object, options, requestOptionsFactory); DocumentServiceLease afterAcquire = await documentServiceLeaseManagerCosmos.CreateLeaseIfNotExistAsync(feedRangeEpk, continuation); DocumentServiceLeaseCoreEpk epkBasedLease = (DocumentServiceLeaseCoreEpk)afterAcquire; Assert.IsNotNull(epkBasedLease); Assert.AreEqual(continuation, afterAcquire.ContinuationToken); Assert.AreEqual(feedRangeEpk.Range.Min, ((FeedRangeEpk)epkBasedLease.FeedRange).Range.Min); Assert.AreEqual(feedRangeEpk.Range.Max, ((FeedRangeEpk)epkBasedLease.FeedRange).Range.Max); ValidateRequestOptionsFactory(requestOptionsFactory, epkBasedLease); }
/// <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 <FeedRangeEpk, PartitionedToken> MatchRangesToContinuationTokens <PartitionedToken>( ReadOnlyMemory <FeedRangeEpk> partitionKeyRanges, IReadOnlyList <PartitionedToken> partitionedContinuationTokens) where PartitionedToken : IPartitionedToken { if (partitionedContinuationTokens == null) { throw new ArgumentNullException(nameof(partitionedContinuationTokens)); } Dictionary <FeedRangeEpk, PartitionedToken> partitionKeyRangeToToken = new Dictionary <FeedRangeEpk, PartitionedToken>(); ReadOnlySpan <FeedRangeEpk> partitionKeyRangeSpan = partitionKeyRanges.Span; for (int i = 0; i < partitionKeyRangeSpan.Length; i++) { FeedRangeEpk feedRange = partitionKeyRangeSpan[i]; foreach (PartitionedToken partitionedToken in partitionedContinuationTokens) { bool rightOfStart = (partitionedToken.Range.Min == string.Empty) || ((feedRange.Range.Min != string.Empty) && (feedRange.Range.Min.CompareTo(partitionedToken.Range.Min) >= 0)); bool leftOfEnd = (partitionedToken.Range.Max == string.Empty) || ((feedRange.Range.Max != string.Empty) && (feedRange.Range.Max.CompareTo(partitionedToken.Range.Max) <= 0)); // See if continuation token includes the range if (rightOfStart && leftOfEnd) { partitionKeyRangeToToken[feedRange] = partitionedToken; break; } } if (!partitionKeyRangeToToken.ContainsKey(feedRange)) { // Could not find a matching token so just set it to null partitionKeyRangeToToken[feedRange] = default; } } return(partitionKeyRangeToToken); }
public void ResumeOnAMerge_LogicalPartition() { // Suppose that we read from range 2 with a logical partiton key that hashes to D FeedRangeEpk range2 = Range(min: "C", max: "E"); // Then Range 1 FeedRangeEpk range1 = Range(min: "A", max: "C"); // and Range 3 merge with range 2 FeedRangeEpk range3 = Range(min: "E", max: "G"); // And we have a continuation token for range 2 ParallelContinuationToken token = Token(min: "C", max: "E"); // Then we should resume on range 2 with epk range filtering // and still have range 1 and 3 with null continuation (but, since there is a logical partition key it won't match any results). RunTryGetInitializationInfo( Mapping((range1, null)), Mapping((range2, token)), Mapping((range3, null)), new FeedRangeEpk[] { CombineRanges(CombineRanges(range1, range2), range3) }, new IPartitionedToken[] { token }); }
public override Task <DocumentServiceLease> CreateLeaseIfNotExistAsync( FeedRangeEpk feedRange, string continuationToken) { if (feedRange == null) { throw new ArgumentNullException(nameof(feedRange)); } string leaseToken = $"{feedRange.Range.Min}-{feedRange.Range.Max}"; string leaseDocId = this.GetDocumentId(leaseToken); DocumentServiceLeaseCoreEpk documentServiceLease = new DocumentServiceLeaseCoreEpk { LeaseId = leaseDocId, LeaseToken = leaseToken, ContinuationToken = continuationToken, FeedRange = feedRange }; this.requestOptionsFactory.AddPartitionKeyIfNeeded((string pk) => documentServiceLease.LeasePartitionKey = pk, Guid.NewGuid().ToString()); return(this.TryCreateDocumentServiceLeaseAsync(documentServiceLease)); }
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()); }
public async Task ShouldUseFeedRangeEpk() { int itemCount = 5; string pkRangeId = "0"; DateTime startTime = DateTime.UtcNow; Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("AA", "BB", true, false); FeedRangeEpk feedRange = new FeedRangeEpk(range); DocumentServiceLeaseCoreEpk documentServiceLeaseCore = new DocumentServiceLeaseCoreEpk() { LeaseToken = pkRangeId, FeedRange = feedRange }; Mock <ContainerInternal> containerMock = new Mock <ContainerInternal>(); Mock <CosmosClientContext> mockContext = new Mock <CosmosClientContext>(); mockContext.Setup(c => c.ProcessResourceOperationStreamAsync( It.IsAny <string>(), It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Document), It.Is <Documents.OperationType>(rt => rt == Documents.OperationType.ReadFeed), It.Is <ChangeFeedRequestOptions>(cfo => cfo.PageSizeHint == itemCount), It.Is <ContainerInternal>(o => o == containerMock.Object), It.Is <FeedRange>(fr => fr is FeedRangeEpk), It.IsAny <Stream>(), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <ITrace>(), It.IsAny <CancellationToken>() ) ).ReturnsAsync(new ResponseMessage(System.Net.HttpStatusCode.OK)); containerMock.Setup(c => c.ClientContext).Returns(mockContext.Object); containerMock.Setup(c => c.LinkUri).Returns("http://localhot"); MockDocumentClient mockDocumentClient = new MockDocumentClient(); mockContext.Setup(c => c.DocumentClient).Returns(mockDocumentClient); ChangeFeedPartitionKeyResultSetIteratorCore iterator = ChangeFeedPartitionKeyResultSetIteratorCore.Create( lease: documentServiceLeaseCore, continuationToken: null, maxItemCount: itemCount, container: containerMock.Object, startTime: startTime, startFromBeginning: false); ResponseMessage response = await iterator.ReadNextAsync(); Assert.AreEqual(System.Net.HttpStatusCode.OK, response.StatusCode); mockContext.Verify(c => c.ProcessResourceOperationStreamAsync( It.IsAny <string>(), It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Document), It.Is <Documents.OperationType>(rt => rt == Documents.OperationType.ReadFeed), It.Is <ChangeFeedRequestOptions>(cfo => cfo.PageSizeHint == itemCount), It.Is <ContainerInternal>(o => o == containerMock.Object), It.Is <FeedRange>(fr => fr is FeedRangeEpk), It.IsAny <Stream>(), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <ITrace>(), It.IsAny <CancellationToken>() ), Times.Once); }