public static FeedRangeInternal ReadJObject( JObject jObject, JsonSerializer serializer) { if (jObject.TryGetValue(FeedRangeInternalConverter.RangePropertyName, out JToken rangeJToken)) { try { Documents.Routing.Range <string> completeRange = (Documents.Routing.Range <string>)rangeJsonConverter.ReadJson(rangeJToken.CreateReader(), typeof(Documents.Routing.Range <string>), null, serializer); return(new FeedRangeEpk(completeRange)); } catch (JsonSerializationException) { throw new JsonReaderException(); } } if (jObject.TryGetValue(FeedRangeInternalConverter.PartitionKeyPropertyName, out JToken pkJToken)) { if (!PartitionKey.TryParseJsonString(pkJToken.Value <string>(), out PartitionKey partitionKey)) { throw new JsonReaderException(); } return(new FeedRangePartitionKey(partitionKey)); } if (jObject.TryGetValue(FeedRangeInternalConverter.PartitionKeyRangeIdPropertyName, out JToken pkRangeJToken)) { return(new FeedRangePartitionKeyRange(pkRangeJToken.Value <string>())); } throw new JsonReaderException(); }
public FeedTokenEPKRange( string containerRid, Documents.Routing.Range <string> completeRange, IReadOnlyList <CompositeContinuationToken> deserializedTokens) : this(containerRid) { if (deserializedTokens == null) { throw new ArgumentNullException(nameof(deserializedTokens)); } if (completeRange == null) { throw new ArgumentNullException(nameof(completeRange)); } if (deserializedTokens.Count == 0) { throw new ArgumentOutOfRangeException(nameof(deserializedTokens)); } this.CompleteRange = completeRange; foreach (CompositeContinuationToken token in deserializedTokens) { this.CompositeContinuationTokens.Enqueue(token); } this.currentToken = this.CompositeContinuationTokens.Peek(); }
public async Task FeedRangePK_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 }; Documents.PartitionKeyDefinition partitionKeyDefinition = new Documents.PartitionKeyDefinition(); partitionKeyDefinition.Paths.Add("/id"); PartitionKey partitionKey = new PartitionKey("test"); IRoutingMapProvider routingProvider = Mock.Of <IRoutingMapProvider>(); Mock.Get(routingProvider) .Setup(f => f.TryGetOverlappingRangesAsync(It.IsAny <string>(), It.IsAny <Documents.Routing.Range <string> >(), It.IsAny <bool>())) .ReturnsAsync(new List <Documents.PartitionKeyRange>() { partitionKeyRange }); FeedRangePartitionKey feedRangePartitionKey = new FeedRangePartitionKey(partitionKey); IEnumerable <string> pkRanges = await feedRangePartitionKey.GetPartitionKeyRangesAsync(routingProvider, null, partitionKeyDefinition, default(CancellationToken)); Assert.AreEqual(1, pkRanges.Count()); Assert.AreEqual(partitionKeyRange.Id, pkRanges.First()); }
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 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 async Task HandlePartitionGoneAsync_EpkBasedLease_Merge() { string continuation = Guid.NewGuid().ToString(); Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("AA", "EE", true, false); DocumentServiceLeaseCoreEpk lease = new DocumentServiceLeaseCoreEpk() { LeaseToken = "AA-BB", ContinuationToken = continuation, Owner = Guid.NewGuid().ToString(), FeedRange = new FeedRangeEpk(range) }; Mock <Routing.PartitionKeyRangeCache> pkRangeCache = new Mock <Routing.PartitionKeyRangeCache>( Mock.Of <Documents.IAuthorizationTokenProvider>(), Mock.Of <Documents.IStoreModel>(), Mock.Of <Common.CollectionCache>()); List <Documents.PartitionKeyRange> resultingRanges = new List <Documents.PartitionKeyRange>() { new Documents.PartitionKeyRange() { Id = "1", MinInclusive = "", MaxExclusive = "FF" }, }; pkRangeCache.Setup(p => p.TryGetOverlappingRangesAsync( It.IsAny <string>(), It.Is <Documents.Routing.Range <string> >(r => r.Min == range.Min && r.Max == range.Max), It.IsAny <ITrace>(), true)) .ReturnsAsync(resultingRanges); Mock <DocumentServiceLeaseManager> leaseManager = new Mock <DocumentServiceLeaseManager>(); PartitionSynchronizerCore partitionSynchronizerCore = new PartitionSynchronizerCore( Mock.Of <ContainerInternal>(), Mock.Of <DocumentServiceLeaseContainer>(), leaseManager.Object, 1, pkRangeCache.Object, Guid.NewGuid().ToString()); (IEnumerable <DocumentServiceLease> addedLeases, bool shouldDelete) = await partitionSynchronizerCore.HandlePartitionGoneAsync(lease); Assert.IsFalse(shouldDelete); Assert.AreEqual(lease, addedLeases.First()); leaseManager.Verify(l => l.CreateLeaseIfNotExistAsync( It.IsAny <Documents.PartitionKeyRange>(), It.IsAny <string>()), Times.Never); leaseManager.Verify(l => l.CreateLeaseIfNotExistAsync( It.IsAny <FeedRangeEpk>(), It.IsAny <string>()), Times.Never); }
public static TryCatch <CompositeContinuationToken> TryCreateFromCosmosElement(CosmosElement cosmosElement) { if (!(cosmosElement is CosmosObject cosmosObject)) { return(TryCatch <CompositeContinuationToken> .FromException( new MalformedContinuationTokenException($"{nameof(CompositeContinuationToken)} is not an object: {cosmosElement}"))); } if (!cosmosObject.TryGetValue(PropertyNames.Token, out CosmosElement rawToken)) { return(TryCatch <CompositeContinuationToken> .FromException( new MalformedContinuationTokenException($"{nameof(CompositeContinuationToken)} is missing field: '{PropertyNames.Token}': {cosmosElement}"))); } string token; if (rawToken is CosmosString rawTokenString) { token = rawTokenString.Value; } else { token = null; } if (!cosmosObject.TryGetValue(PropertyNames.Range, out CosmosObject rawRange)) { return(TryCatch <CompositeContinuationToken> .FromException( new MalformedContinuationTokenException($"{nameof(CompositeContinuationToken)} is missing field: '{PropertyNames.Range}': {cosmosElement}"))); } if (!rawRange.TryGetValue(PropertyNames.Min, out CosmosString rawMin)) { return(TryCatch <CompositeContinuationToken> .FromException( new MalformedContinuationTokenException($"{nameof(CompositeContinuationToken)} is missing field: '{PropertyNames.Min}': {cosmosElement}"))); } string min = rawMin.Value; if (!rawRange.TryGetValue(PropertyNames.Max, out CosmosString rawMax)) { return(TryCatch <CompositeContinuationToken> .FromException( new MalformedContinuationTokenException($"{nameof(CompositeContinuationToken)} is missing field: '{PropertyNames.Max}': {cosmosElement}"))); } string max = rawMax.Value; Documents.Routing.Range <string> range = new Documents.Routing.Range <string>(min, max, true, false); CompositeContinuationToken compositeContinuationToken = new CompositeContinuationToken() { Token = token, Range = range, }; return(TryCatch <CompositeContinuationToken> .FromResult(compositeContinuationToken)); }
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 FeedRangeEpk(Documents.Routing.Range <string> range) { if (range == null) { throw new ArgumentNullException(nameof(range)); } this.Range = range; }
public async Task HandlePartitionGoneAsync_PKRangeBasedLease_Merge() { string continuation = Guid.NewGuid().ToString(); Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("", "BB", true, false); DocumentServiceLeaseCore lease = new DocumentServiceLeaseCore() { LeaseToken = "0", ContinuationToken = continuation, Owner = Guid.NewGuid().ToString(), FeedRange = new FeedRangeEpk(range) }; Mock <Routing.PartitionKeyRangeCache> pkRangeCache = new Mock <Routing.PartitionKeyRangeCache>( Mock.Of <Documents.IAuthorizationTokenProvider>(), Mock.Of <Documents.IStoreModel>(), Mock.Of <Common.CollectionCache>()); List <Documents.PartitionKeyRange> resultingRanges = new List <Documents.PartitionKeyRange>() { new Documents.PartitionKeyRange() { Id = "2", MinInclusive = "", MaxExclusive = "FF" } }; pkRangeCache.Setup(p => p.TryGetOverlappingRangesAsync( It.IsAny <string>(), It.Is <Documents.Routing.Range <string> >(r => r.Min == range.Min && r.Max == range.Max), It.IsAny <ITrace>(), It.Is <bool>(b => b == true))) .ReturnsAsync(resultingRanges); Mock <DocumentServiceLeaseManager> leaseManager = new Mock <DocumentServiceLeaseManager>(); PartitionSynchronizerCore partitionSynchronizerCore = new PartitionSynchronizerCore( Mock.Of <ContainerInternal>(), Mock.Of <DocumentServiceLeaseContainer>(), leaseManager.Object, 1, pkRangeCache.Object, Guid.NewGuid().ToString()); await partitionSynchronizerCore.HandlePartitionGoneAsync(lease); leaseManager.Verify(l => l.CreateLeaseIfNotExistAsync( It.IsAny <Documents.PartitionKeyRange>(), It.IsAny <string>()), Times.Never); leaseManager.Verify(l => l.CreateLeaseIfNotExistAsync( It.IsAny <FeedRangeEpk>(), It.IsAny <string>()), Times.Once); leaseManager.Verify(l => l.CreateLeaseIfNotExistAsync( It.Is <FeedRangeEpk>(epKRange => epKRange.Range.Min == range.Min && epKRange.Range.Max == range.Max), It.Is <string>(c => c == continuation)), Times.Once); }
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(); FeedRangeVisitor feedRangeVisitor = new FeedRangeVisitor(requestMessage); feedRange.Accept(feedRangeVisitor); Assert.AreEqual(0, requestMessage.Properties.Count); }
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 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 async Task StandByFeedIterator_EmptyBeginning() { int totalCount = 0; int expectedDocuments = 5; string lastcontinuation = string.Empty; Documents.Routing.Range <string> previousRange = null; Documents.Routing.Range <string> currentRange = null; int pkRangesCount = (await this.Container.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(this.Container.LinkUri)).Count; int visitedPkRanges = 0; ContainerCore itemsCore = (ContainerCore)this.Container; FeedIterator feedIterator = itemsCore.GetStandByFeedIterator(); while (feedIterator.HasMoreResults) { using (ResponseMessage responseMessage = await feedIterator.ReadNextAsync(this.cancellationToken)) { lastcontinuation = responseMessage.Headers.ContinuationToken; Assert.AreEqual(responseMessage.ContinuationToken, responseMessage.Headers.ContinuationToken); List <CompositeContinuationToken> deserializedToken = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(lastcontinuation); currentRange = deserializedToken[0].Range; if (responseMessage.IsSuccessStatusCode) { Collection <ToDoActivity> response = TestCommon.Serializer.FromStream <CosmosFeedResponseUtil <ToDoActivity> >(responseMessage.Content).Data; totalCount += response.Count; } else { if (visitedPkRanges == 0) { await this.CreateRandomItems(this.Container, expectedDocuments, randomPartitionKey : true); } } if (visitedPkRanges == pkRangesCount && responseMessage.StatusCode == System.Net.HttpStatusCode.NotModified) { break; } if (!currentRange.Equals(previousRange)) { visitedPkRanges++; } previousRange = currentRange; } } Assert.AreEqual(expectedDocuments, totalCount); }
public async Task FeedRangePK_GetEffectiveRangesAsync() { Documents.PartitionKeyDefinition partitionKeyDefinition = new Documents.PartitionKeyDefinition(); partitionKeyDefinition.Paths.Add("/id"); PartitionKey partitionKey = new PartitionKey("test"); FeedRangePartitionKey feedRangePartitionKey = new FeedRangePartitionKey(partitionKey); Documents.Routing.Range <string> range = Documents.Routing.Range <string> .GetPointRange(partitionKey.InternalKey.GetEffectivePartitionKeyString(partitionKeyDefinition)); List <Documents.Routing.Range <string> > ranges = await feedRangePartitionKey.GetEffectiveRangesAsync(Mock.Of <IRoutingMapProvider>(), null, partitionKeyDefinition); Assert.AreEqual(1, ranges.Count); Assert.AreEqual(range, ranges[0]); }
private FeedTokenEPKRange( string containerRid, CompositeContinuationToken compositeContinuationTokenByPartitionKeyRangeId) : this(containerRid) { if (compositeContinuationTokenByPartitionKeyRangeId == null) { throw new ArgumentNullException(nameof(compositeContinuationTokenByPartitionKeyRangeId)); } this.CompleteRange = compositeContinuationTokenByPartitionKeyRangeId.Range; this.CompositeContinuationTokens.Enqueue(compositeContinuationTokenByPartitionKeyRangeId); this.currentToken = this.CompositeContinuationTokens.Peek(); }
public FeedTokenEPKRange( string containerRid, Documents.PartitionKeyRange keyRange) : this(containerRid) { if (keyRange == null) { throw new ArgumentNullException(nameof(keyRange)); } this.CompleteRange = new Documents.Routing.Range <string>(keyRange.MinInclusive, keyRange.MaxExclusive, true, false); this.CompositeContinuationTokens.Enqueue(FeedTokenEPKRange.CreateCompositeContinuationTokenForRange(keyRange.MinInclusive, keyRange.MaxExclusive, null)); this.currentToken = this.CompositeContinuationTokens.Peek(); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { try { Documents.Routing.Range <string> range = (Documents.Routing.Range <string>)value; writer.WriteStartObject(); writer.WritePropertyName(MinProperty); serializer.Serialize(writer, range.Min); writer.WritePropertyName(MaxProperty); serializer.Serialize(writer, range.Max); writer.WriteEndObject(); } catch (Exception ex) { throw new JsonSerializationException(string.Empty, ex); } }
public static FeedRangeEPK ReadJObject( JObject jObject, JsonSerializer serializer) { if (!jObject.TryGetValue(FeedRangeEPKConverter.RangePropertyName, out JToken rangeJToken)) { throw new JsonReaderException(); } try { Documents.Routing.Range <string> completeRange = (Documents.Routing.Range <string>)rangeJsonConverter.ReadJson(rangeJToken.CreateReader(), typeof(Documents.Routing.Range <string>), null, serializer); return(new FeedRangeEPK(completeRange)); } catch (JsonSerializationException) { throw new JsonReaderException(); } }
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 void FeedRangeCompositeContinuation_RequestVisitor_IfEPKAlreadyExists() { const string containerRid = "containerRid"; const string continuation = "continuation"; string epkString = Guid.NewGuid().ToString(); Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("A", "B", true, false); FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(containerRid, Mock.Of <FeedRangeInternal>(), new List <Documents.Routing.Range <string> >() { range }, continuation); RequestMessage requestMessage = new RequestMessage(); FeedRangeVisitor feedRangeVisitor = new FeedRangeVisitor(requestMessage); requestMessage.Properties[HandlerConstants.StartEpkString] = epkString; requestMessage.Properties[HandlerConstants.EndEpkString] = epkString; token.Accept(feedRangeVisitor, ChangeFeedRequestOptions.FillContinuationToken); Assert.AreEqual(epkString, requestMessage.Properties[HandlerConstants.StartEpkString]); Assert.AreEqual(epkString, requestMessage.Properties[HandlerConstants.EndEpkString]); }
private async Task <IReadOnlyList <Documents.PartitionKeyRange> > TryGetOverlappingRangesAsync( Documents.Routing.Range <string> targetRange, bool forceRefresh = false) { Debug.Assert(targetRange != null); IReadOnlyList <Documents.PartitionKeyRange> keyRanges = await this.pkRangeCacheDelegate( this.containerRid, new Documents.Routing.Range <string>( targetRange.Min, targetRange.Max, isMaxInclusive: true, isMinInclusive: false), forceRefresh); if (keyRanges.Count == 0) { throw new ArgumentOutOfRangeException("RequestContinuation", $"Token contains invalid range {targetRange.Min}-{targetRange.Max}"); } return(keyRanges); }
public void FeedRangeCompositeContinuation_RequestVisitor() { const string containerRid = "containerRid"; const string continuation = "continuation"; Documents.Routing.Range <string> range = new Documents.Routing.Range <string>("A", "B", true, false); FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(containerRid, Mock.Of <FeedRangeInternal>(), new List <Documents.Routing.Range <string> >() { range }, continuation); RequestMessage requestMessage = new RequestMessage(); requestMessage.OperationType = Documents.OperationType.ReadFeed; requestMessage.ResourceType = Documents.ResourceType.Document; FeedRangeVisitor feedRangeVisitor = new FeedRangeVisitor(requestMessage); token.Accept(feedRangeVisitor, ChangeFeedRequestOptions.FillContinuationToken); Assert.AreEqual(range.Min, requestMessage.Properties[HandlerConstants.StartEpkString]); Assert.AreEqual(range.Max, requestMessage.Properties[HandlerConstants.EndEpkString]); Assert.AreEqual(continuation, requestMessage.Headers.IfNoneMatch); Assert.IsTrue(requestMessage.IsPartitionKeyRangeHandlerRequired); }
public FeedTokenEPKRange( string containerRid, IReadOnlyList <Documents.PartitionKeyRange> keyRanges) : this(containerRid) { if (keyRanges == null) { throw new ArgumentNullException(nameof(keyRanges)); } if (keyRanges.Count == 0) { throw new ArgumentOutOfRangeException(nameof(keyRanges)); } this.CompleteRange = new Documents.Routing.Range <string>(keyRanges[0].MinInclusive, keyRanges[keyRanges.Count - 1].MaxExclusive, true, false); foreach (Documents.PartitionKeyRange keyRange in keyRanges) { this.CompositeContinuationTokens.Enqueue(FeedTokenEPKRange.CreateCompositeContinuationTokenForRange(keyRange.MinInclusive, keyRange.MaxExclusive, null)); } this.currentToken = this.CompositeContinuationTokens.Peek(); }
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); Routing.IRoutingMapProvider routingProvider = Mock.Of <Routing.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()); }
/// <summary> /// Returns list of effective partition key ranges for a collection. /// </summary> /// <param name="collectionResourceId">Collection for which to retrieve routing map.</param> /// <param name="range">This method will return all ranges which overlap this range.</param> /// <param name="forceRefresh">Whether forcefully refreshing the routing map is necessary</param> /// <returns>List of effective partition key ranges for a collection or null if collection doesn't exist.</returns> public abstract Task <IReadOnlyList <Documents.PartitionKeyRange> > TryGetOverlappingRangesAsync( string collectionResourceId, Documents.Routing.Range <string> range, bool forceRefresh = false);
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); }
/// <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); }
internal override IReadOnlyList <Documents.PartitionKeyRange> ResolveOverlapingPartitionKeyRanges(string collectionRid, Documents.Routing.Range <string> range, bool forceRefresh) { if (range.Min == Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey && range.Max == Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey) { return(this.availablePartitionKeyRanges); } if (range.Min == this.availablePartitionKeyRanges[0].MinInclusive) { return(new List <Documents.PartitionKeyRange>() { this.availablePartitionKeyRanges[0] }); } if (range.Min == this.availablePartitionKeyRanges[1].MinInclusive) { return(new List <Documents.PartitionKeyRange>() { this.availablePartitionKeyRanges[1] }); } return(new List <Documents.PartitionKeyRange>() { this.availablePartitionKeyRanges[2] }); }
internal virtual IReadOnlyList <PartitionKeyRange> ResolveOverlapingPartitionKeyRanges(string collectionRid, Documents.Routing.Range <string> range, bool forceRefresh) { return((IReadOnlyList <PartitionKeyRange>) new List <Documents.PartitionKeyRange>() { new Documents.PartitionKeyRange() { MinInclusive = "", MaxExclusive = "FF", Id = "0" } }); }