Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition
{
using System;
using System.Collections.Generic;
using Microsoft.Azure.Cosmos.Query.Core.QueryClient;
using Microsoft.Azure.Documents.Routing;

internal static class HierarchicalPartitionUtils
{
private static readonly bool IsLengthAwareComparisonEnabled = ConfigurationManager.IsLengthAwareRangeComparatorEnabled();
/// <summary>
/// Updates the FeedRange to limit the scope of incoming feedRange to logical partition within a single physical partition.
/// Generally speaking, a subpartitioned container can experience split partition at any level of hierarchical partition key.
Expand Down Expand Up @@ -50,7 +53,16 @@ public static FeedRangeInternal LimitFeedRangeToSinglePartition(PartitionKey? pa
String overlappingMax;
bool maxInclusive;

if (Documents.Routing.Range<String>.MinComparer.Instance.Compare(
//LengthAwareComparer is the default Range comparer and flag <see cref="ConfigurationManager.UseLengthAwareRangeComparator"/> is used to ovverride the default comparer to legacy Min/Max comparer.
IComparer<Range<string>> minComparer = IsLengthAwareComparisonEnabled
? Documents.Routing.Range<string>.LengthAwareMinComparer.Instance
: Documents.Routing.Range<string>.MinComparer.Instance;

IComparer<Range<string>> maxComparer = IsLengthAwareComparisonEnabled
? Documents.Routing.Range<string>.LengthAwareMaxComparer.Instance
: Documents.Routing.Range<string>.MaxComparer.Instance;

if (minComparer.Compare(
epkForPartitionKey,
feedRangeEpk.Range) < 0)
{
Expand All @@ -63,7 +75,7 @@ public static FeedRangeInternal LimitFeedRangeToSinglePartition(PartitionKey? pa
minInclusive = epkForPartitionKey.IsMinInclusive;
}

if (Documents.Routing.Range<String>.MaxComparer.Instance.Compare(
if (maxComparer.Compare(
epkForPartitionKey,
feedRangeEpk.Range) > 0)
{
Expand Down
24 changes: 10 additions & 14 deletions Microsoft.Azure.Cosmos/src/Routing/CollectionRoutingMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,7 @@ internal sealed class CollectionRoutingMap

internal int HighestNonOfflinePkRangeId { get; private set; }

public CollectionRoutingMap(
CollectionRoutingMap collectionRoutingMap,
string changeFeedNextIfNoneMatch)
{
this.rangeById = new Dictionary<string, Tuple<PartitionKeyRange, ServiceIdentity>>(collectionRoutingMap.rangeById);
this.orderedPartitionKeyRanges = new List<PartitionKeyRange>(collectionRoutingMap.orderedPartitionKeyRanges);
this.orderedRanges = new List<Range<string>>(collectionRoutingMap.orderedRanges);
this.goneRanges = new HashSet<string>(collectionRoutingMap.goneRanges);
this.HighestNonOfflinePkRangeId = collectionRoutingMap.HighestNonOfflinePkRangeId;
this.CollectionUniqueId = collectionRoutingMap.CollectionUniqueId;
this.ChangeFeedNextIfNoneMatch = changeFeedNextIfNoneMatch;
}
private readonly (IComparer<Range<string>> MinComparer, IComparer<Range<string>> MaxComparer) comparers;

private CollectionRoutingMap(
Dictionary<string, Tuple<PartitionKeyRange, ServiceIdentity>> rangeById,
Expand Down Expand Up @@ -82,6 +71,13 @@ private CollectionRoutingMap(
}
return range.Status == PartitionKeyRangeStatus.Offline ? CollectionRoutingMap.InvalidPkRangeId : pkId;
});

//LengthAwareComparer is the default Range comparer and flag <see cref="ConfigurationManager.UseLengthAwareRangeComparator"/> is used to ovverride the default comparer to legacy Min/Max comparer.
bool useLengthAwareComparer = ConfigurationManager.IsLengthAwareRangeComparatorEnabled();
Comment thread
ananth7592 marked this conversation as resolved.

this.comparers = useLengthAwareComparer
? (Range<string>.LengthAwareMinComparer.Instance, Range<string>.LengthAwareMaxComparer.Instance)
: (Range<string>.MinComparer.Instance, Range<string>.MaxComparer.Instance);
}

public static CollectionRoutingMap TryCreateCompleteRoutingMap(
Expand Down Expand Up @@ -142,13 +138,13 @@ public IReadOnlyList<PartitionKeyRange> GetOverlappingRanges(IReadOnlyList<Range
// Then within that two positions, check for overlapping partition key ranges
foreach (Range<string> providedRange in providedPartitionKeyRanges)
{
int minIndex = this.orderedRanges.BinarySearch(providedRange, Range<string>.MinComparer.Instance);
int minIndex = this.orderedRanges.BinarySearch(providedRange, this.comparers.MinComparer);
if (minIndex < 0)
{
minIndex = Math.Max(0, (~minIndex) - 1);
}

int maxIndex = this.orderedRanges.BinarySearch(providedRange, Range<string>.MaxComparer.Instance);
int maxIndex = this.orderedRanges.BinarySearch(providedRange, this.comparers.MaxComparer);
if (maxIndex < 0)
{
maxIndex = Math.Min(this.OrderedPartitionKeyRanges.Count - 1, ~maxIndex);
Expand Down
25 changes: 20 additions & 5 deletions Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace Microsoft.Azure.Cosmos.Routing

internal class PartitionRoutingHelper
{
private static readonly bool IsLengthAwareComparisonEnabled = ConfigurationManager.IsLengthAwareRangeComparatorEnabled();
public static IReadOnlyList<Range<string>> GetProvidedPartitionKeyRanges(
string querySpecJsonString,
bool enableCrossPartitionQuery,
Expand Down Expand Up @@ -231,9 +232,11 @@ await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(
return new ResolvedRangeInfo(lastPartitionKeyRange, suppliedTokens);
}

(IComparer<Range<string>> minComparer, _) = this.GetComparers(IsLengthAwareComparisonEnabled);

Range<string> minimumRange = PartitionRoutingHelper.Min(
providedPartitionKeyRanges,
Range<string>.MinComparer.Instance);
minComparer);

return new ResolvedRangeInfo(
await routingMapProvider.TryGetRangeByEffectivePartitionKeyAsync(collectionRid, minimumRange.Min, trace),
Expand Down Expand Up @@ -347,21 +350,24 @@ public virtual async Task<bool> TryAddPartitionKeyRangeToContinuationTokenAsync(
// We only need to get the next range if we have to
if (string.IsNullOrEmpty(backendResponseHeaders[HttpConstants.HttpHeaders.Continuation]))
{
(IComparer<Range<string>> minComparer, IComparer<Range<string>> maxComparer) = this.GetComparers(IsLengthAwareComparisonEnabled);

if (direction == RntdbEnumerationDirection.Reverse)
{
rangeToUse = PartitionRoutingHelper.MinBefore(
(await routingMapProvider.TryGetOverlappingRangesAsync(
collectionRid,
providedPartitionKeyRanges.Single(),
trace)).ToList(),
currentRange);
currentRange,
minComparer);
}
else
{
Range<string> nextProvidedRange = PartitionRoutingHelper.MinAfter(
providedPartitionKeyRanges,
currentRange.ToRange(),
Range<string>.MaxComparer.Instance);
maxComparer);

if (nextProvidedRange == null)
{
Expand Down Expand Up @@ -547,14 +553,14 @@ private static T MinAfter<T>(IReadOnlyList<T> values, T minValue, IComparer<T> c
return min;
}

private static PartitionKeyRange MinBefore(IReadOnlyList<PartitionKeyRange> values, PartitionKeyRange minValue)
private static PartitionKeyRange MinBefore(IReadOnlyList<PartitionKeyRange> values, PartitionKeyRange minValue,
IComparer<Range<string>> comparer)
{
if (values.Count == 0)
{
throw new ArgumentException(nameof(values));
}

IComparer<Range<string>> comparer = Range<string>.MinComparer.Instance;
PartitionKeyRange min = null;
foreach (PartitionKeyRange value in values)
{
Expand All @@ -566,6 +572,15 @@ private static PartitionKeyRange MinBefore(IReadOnlyList<PartitionKeyRange> valu

return min;
}

//LengthAwareComparer is the default Range comparer and flag <see cref="ConfigurationManager.UseLengthAwareRangeComparator"/> is used to ovverride the default comparer to legacy Min/Max comparer.
private (IComparer<Range<string>> minComparer, IComparer<Range<string>> maxComparer) GetComparers(bool useLengthAwareComparison)
{
return (
useLengthAwareComparison ? Range<string>.LengthAwareMinComparer.Instance : Range<string>.MinComparer.Instance,
useLengthAwareComparison ? Range<string>.LengthAwareMaxComparer.Instance : Range<string>.MaxComparer.Instance
);
}

public readonly struct ResolvedRangeInfo
{
Expand Down
25 changes: 25 additions & 0 deletions Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ internal static class ConfigurationManager
/// </summary>
internal static readonly string BypassQueryParsing = "AZURE_COSMOS_BYPASS_QUERY_PARSING";

/// <summary>
/// A read-only string containing the environment variable name for disabling length aware range comparator.
/// Length aware range comparators were intorduced in Range class to handle EPK range comparisons correctly in the case of a container's physical partition set consisting of fully and partially specified EPK values.
/// By default length aware range comparator is enabled. Refer to Range.cs in Msdata project for more details. Range.LengthAwareMinComparer/LengthAwareMaxComparer.
/// Setting the value to false will disable length aware range comparator and switch to using the regular Range.MinComparer/MaxComparer.
/// </summary>
internal static readonly string UseLengthAwareRangeComparator = "AZURE_COSMOS_USE_LENGTH_AWARE_RANGE_COMPARATOR";

public static T GetEnvironmentVariable<T>(string variable, T defaultValue)
{
string value = Environment.GetEnvironmentVariable(variable);
Expand Down Expand Up @@ -376,5 +384,22 @@ public static bool ForceBypassQueryParsing()
variable: ConfigurationManager.BypassQueryParsing,
defaultValue: false);
}

/// <summary>
/// Gets the boolean value indicating if length-aware range comparator is enabled.
/// Default: true for preview , false for GA.
/// </summary>
/// <returns>A boolean flag indicating if length-aware range comparator is enabled.</returns>
public static bool IsLengthAwareRangeComparatorEnabled()
{
bool defaultValue = false;
#if PREVIEW && !INTERNAL
defaultValue = true;
#endif
return ConfigurationManager
.GetEnvironmentVariable(
variable: ConfigurationManager.UseLengthAwareRangeComparator,
defaultValue: defaultValue);
}
}
}
Loading
Loading