/// <summary> /// Gets <see cref="PartitionKeyRange"/> instance which corresponds to <paramref name="rangeFromContinuationToken"/> /// </summary> /// <param name="providedPartitionKeyRanges"></param> /// <param name="routingMapProvider"></param> /// <param name="collectionRid"></param> /// <param name="rangeFromContinuationToken"></param> /// <param name="suppliedTokens"></param> /// <param name="direction"></param> /// <returns>null if collection with specified <paramref name="collectionRid"/> doesn't exist, which potentially means /// that collection was resolved to outdated Rid by name. Also null can be returned if <paramref name="rangeFromContinuationToken"/> /// is not found - this means it was split. /// </returns> public virtual async Task <ResolvedRangeInfo> TryGetTargetRangeFromContinuationTokenRangeAsync( IReadOnlyList <Range <string> > providedPartitionKeyRanges, IRoutingMapProvider routingMapProvider, string collectionRid, Range <string> rangeFromContinuationToken, List <CompositeContinuationToken> suppliedTokens, RntdbEnumerationDirection direction = RntdbEnumerationDirection.Forward) { // For queries such as "SELECT * FROM root WHERE false", // we will have empty ranges and just forward the request to the first partition if (providedPartitionKeyRanges.Count == 0) { return(new ResolvedRangeInfo( await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync( collectionRid, PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey), suppliedTokens)); } // Initially currentRange will be empty if (rangeFromContinuationToken.IsEmpty) { if (direction == RntdbEnumerationDirection.Reverse) { PartitionKeyRange lastPartitionKeyRange = (await routingMapProvider.TryGetOverlappingRangesAsync(collectionRid, providedPartitionKeyRanges.Single())).Last(); return(new ResolvedRangeInfo( lastPartitionKeyRange, suppliedTokens)); } Range <string> minimumRange = PartitionRoutingHelper.Min( providedPartitionKeyRanges, Range <string> .MinComparer.Instance); return(new ResolvedRangeInfo( await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(collectionRid, minimumRange.Min), suppliedTokens)); } PartitionKeyRange targetPartitionKeyRange = await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(collectionRid, rangeFromContinuationToken.Min); if (targetPartitionKeyRange == null) { return(new ResolvedRangeInfo(null, suppliedTokens)); } if (!rangeFromContinuationToken.Equals(targetPartitionKeyRange.ToRange())) { // Cannot find target range. Either collection was resolved incorrectly or the range was split List <PartitionKeyRange> replacedRanges = (await routingMapProvider.TryGetOverlappingRangesAsync(collectionRid, rangeFromContinuationToken, true)).ToList(); if (replacedRanges == null || replacedRanges.Count < 1) { return(new ResolvedRangeInfo(null, null)); } else { if (!(replacedRanges[0].MinInclusive.Equals(rangeFromContinuationToken.Min) && replacedRanges[replacedRanges.Count - 1].MaxExclusive.Equals(rangeFromContinuationToken.Max))) { return(new ResolvedRangeInfo(null, null)); } } if (direction == RntdbEnumerationDirection.Reverse) { replacedRanges.Reverse(); } List <CompositeContinuationToken> continuationTokensToBePersisted = null; if (suppliedTokens != null && suppliedTokens.Count > 0) { continuationTokensToBePersisted = new List <CompositeContinuationToken>(replacedRanges.Count + suppliedTokens.Count - 1); foreach (PartitionKeyRange partitionKeyRange in replacedRanges) { CompositeContinuationToken token = (CompositeContinuationToken)suppliedTokens[0].ShallowCopy(); token.Range = partitionKeyRange.ToRange(); continuationTokensToBePersisted.Add(token); } continuationTokensToBePersisted.AddRange(suppliedTokens.Skip(1)); } return(new ResolvedRangeInfo(replacedRanges[0], continuationTokensToBePersisted)); } return(new ResolvedRangeInfo(targetPartitionKeyRange, suppliedTokens)); }
public virtual Range <string> ExtractPartitionKeyRangeFromContinuationToken(INameValueCollection headers, out List <CompositeContinuationToken> compositeContinuationTokens) { if (headers == null) { throw new ArgumentNullException("headers"); } compositeContinuationTokens = null; Range <string> range = Range <string> .GetEmptyRange(PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey); if (string.IsNullOrEmpty(headers[HttpConstants.HttpHeaders.Continuation])) { return(range); } string providedContinuation = headers[HttpConstants.HttpHeaders.Continuation]; CompositeContinuationToken initialContinuationToken = null; if (!string.IsNullOrEmpty(providedContinuation)) { try { if (providedContinuation.Trim().StartsWith("[", StringComparison.Ordinal)) { compositeContinuationTokens = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(providedContinuation); if (compositeContinuationTokens != null && compositeContinuationTokens.Count > 0) { headers[HttpConstants.HttpHeaders.Continuation] = compositeContinuationTokens[0].Token; initialContinuationToken = compositeContinuationTokens[0]; } else { headers.Remove(HttpConstants.HttpHeaders.Continuation); } } else { // TODO: Remove the else logic after the gateway deployment is complete initialContinuationToken = JsonConvert.DeserializeObject <CompositeContinuationToken>(providedContinuation); if (initialContinuationToken != null) { compositeContinuationTokens = new List <CompositeContinuationToken> { initialContinuationToken }; } else { throw new BadRequestException(RMResources.InvalidContinuationToken); } } if (initialContinuationToken != null && initialContinuationToken.Range != null) { range = initialContinuationToken.Range; } if (initialContinuationToken != null && !string.IsNullOrEmpty(initialContinuationToken.Token)) { headers[HttpConstants.HttpHeaders.Continuation] = initialContinuationToken.Token; } else { headers.Remove(HttpConstants.HttpHeaders.Continuation); } } catch (JsonException ex) { DefaultTrace.TraceWarning( string.Format( CultureInfo.InvariantCulture, "{0} Invalid JSON in the continuation token {1}", DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture), providedContinuation)); throw new BadRequestException(RMResources.InvalidContinuationToken, ex); } } else { headers.Remove(HttpConstants.HttpHeaders.Continuation); } return(range); }
private void AddFormattedContinuationHeaderHelper(AddFormattedContinuationToHeaderTestUnit positiveTestData, out INameValueCollection headers, out List <PartitionKeyRange> resolvedRanges, out List <CompositeContinuationToken> resolvedContinuationTokens) { Func <string, INameValueCollection> getHeadersWithContinuation = (string continuationToken) => { INameValueCollection localHeaders = new DictionaryNameValueCollection(); if (continuationToken != null) { localHeaders[HttpConstants.HttpHeaders.Continuation] = continuationToken; } return(localHeaders); }; resolvedRanges = positiveTestData.ResolvedRanges.Select(x => new PartitionKeyRange() { MinInclusive = x.Min, MaxExclusive = x.Max }).ToList(); resolvedContinuationTokens = new List <CompositeContinuationToken>(); CompositeContinuationToken[] initialContinuationTokens = null; if (!string.IsNullOrEmpty(positiveTestData.InputCompositeContinuationToken)) { if (positiveTestData.InputCompositeContinuationToken.Trim().StartsWith("[", StringComparison.Ordinal)) { initialContinuationTokens = JsonConvert.DeserializeObject <CompositeContinuationToken[]>(positiveTestData.InputCompositeContinuationToken); } else { initialContinuationTokens = new CompositeContinuationToken[] { JsonConvert.DeserializeObject <CompositeContinuationToken>(positiveTestData.InputCompositeContinuationToken) }; } } if (resolvedRanges.Count > 1) { CompositeContinuationToken continuationToBeCopied; if (initialContinuationTokens != null && initialContinuationTokens.Length > 0) { continuationToBeCopied = (CompositeContinuationToken)initialContinuationTokens[0].ShallowCopy(); } else { continuationToBeCopied = new CompositeContinuationToken(); continuationToBeCopied.Token = string.Empty; } headers = getHeadersWithContinuation(continuationToBeCopied.Token); foreach (PartitionKeyRange pkrange in resolvedRanges) { CompositeContinuationToken token = (CompositeContinuationToken)continuationToBeCopied.ShallowCopy(); token.Range = pkrange.ToRange(); resolvedContinuationTokens.Add(token); } if (initialContinuationTokens != null) { resolvedContinuationTokens.AddRange(initialContinuationTokens.Skip(1)); } } else { headers = getHeadersWithContinuation(null); } }