public static PartitionKeyHashRanges Create(IEnumerable <PartitionKeyHashRange> partitionKeyHashRanges)
        {
            CreateOutcome createStatus = PartitionKeyHashRanges.TryCreate(
                partitionKeyHashRanges,
                out PartitionKeyHashRanges partitionedSortedEffectiveRanges);

            switch (createStatus)
            {
            case CreateOutcome.DuplicatePartitionKeyRange:
                throw new ArgumentException($"{nameof(partitionKeyHashRanges)} must not have duplicate values.");

            case CreateOutcome.EmptyPartitionKeyRange:
                throw new ArgumentException($"{nameof(partitionKeyHashRanges)} must not have an empty range.");

            case CreateOutcome.NoPartitionKeyRanges:
                throw new ArgumentException($"{nameof(partitionKeyHashRanges)} must not be empty.");

            case CreateOutcome.NullPartitionKeyRanges:
                throw new ArgumentNullException(nameof(partitionKeyHashRanges));

            case CreateOutcome.RangesAreNotContiguous:
                throw new ArgumentException($"{nameof(partitionKeyHashRanges)} must have contiguous ranges.");

            case CreateOutcome.RangesOverlap:
                throw new ArgumentException($"{nameof(partitionKeyHashRanges)} must not overlapping ranges.");

            case CreateOutcome.Success:
                return(partitionedSortedEffectiveRanges);

            default:
                throw new ArgumentOutOfRangeException($"Unknown {nameof(CreateOutcome)}: {createStatus}.");
            }
        }
        public static PartitionKeyHashRange MergeRanges(PartitionKeyHashRanges partitionedSortedEffectiveRanges)
        {
            if (partitionedSortedEffectiveRanges == null)
            {
                throw new ArgumentNullException(nameof(partitionedSortedEffectiveRanges));
            }

            return(new PartitionKeyHashRange(
                       startInclusive: partitionedSortedEffectiveRanges.First().StartInclusive,
                       endExclusive: partitionedSortedEffectiveRanges.Last().EndExclusive));
        }
        public static SplitOutcome TrySplitRange(PartitionKeyHashRange partitionKeyHashRange, int rangeCount, out PartitionKeyHashRanges splitRanges)
        {
            if (rangeCount < 1)
            {
                splitRanges = default;
                return(SplitOutcome.NumRangesNeedsToBeGreaterThanZero);
            }

            UInt128 actualEnd   = partitionKeyHashRange.EndExclusive.HasValue ? partitionKeyHashRange.EndExclusive.Value.Value : UInt128.MaxValue;
            UInt128 actualStart = partitionKeyHashRange.StartInclusive.HasValue ? partitionKeyHashRange.StartInclusive.Value.Value : UInt128.MinValue;
            UInt128 rangeLength = actualEnd - actualStart;

            if (rangeLength < rangeCount)
            {
                splitRanges = default;
                return(SplitOutcome.RangeNotWideEnough);
            }

            if (rangeCount == 1)
            {
                // Just return the range as is:
                splitRanges = PartitionKeyHashRanges.Create(new PartitionKeyHashRange[] { partitionKeyHashRange });
                return(SplitOutcome.Success);
            }

            List <PartitionKeyHashRange> childRanges = new List <PartitionKeyHashRange>();
            UInt128 childRangeLength = rangeLength / rangeCount;

            // First range should start at the user supplied range (since the input might have an open range and we don't want to return 0)
            {
                PartitionKeyHash?start = partitionKeyHashRange.StartInclusive;
                PartitionKeyHash end   = new PartitionKeyHash(actualStart + childRangeLength);
                childRanges.Add(new PartitionKeyHashRange(start, end));
            }

            for (int i = 1; i < rangeCount - 1; i++)
            {
                PartitionKeyHash start = new PartitionKeyHash(actualStart + (childRangeLength * i));
                PartitionKeyHash end   = new PartitionKeyHash(start.Value + childRangeLength);
                childRanges.Add(new PartitionKeyHashRange(start, end));
            }

            // Last range will have remaining EPKs, since the range might not be divisible.
            {
                PartitionKeyHash start = new PartitionKeyHash(actualStart + (childRangeLength * (rangeCount - 1)));
                PartitionKeyHash?end   = partitionKeyHashRange.EndExclusive;
                childRanges.Add(new PartitionKeyHashRange(start, end));
            }

            splitRanges = PartitionKeyHashRanges.Create(childRanges);
            return(SplitOutcome.Success);
        }
예제 #4
0
            public static TryCatch <PartitionKeyHashRanges> Create(IEnumerable <PartitionKeyHashRange> partitionKeyHashRanges)
            {
                CreateOutcome createStatus = PartitionKeyHashRanges.TryCreate(
                    partitionKeyHashRanges,
                    out PartitionKeyHashRanges partitionedSortedEffectiveRanges);

                return(createStatus switch
                {
                    CreateOutcome.DuplicatePartitionKeyRange => TryCatch <PartitionKeyHashRanges> .FromException(
                        new ArgumentException($"{nameof(partitionKeyHashRanges)} must not have duplicate values.")),
                    CreateOutcome.EmptyPartitionKeyRange => TryCatch <PartitionKeyHashRanges> .FromException(
                        new ArgumentException($"{nameof(partitionKeyHashRanges)} must not have an empty range.")),
                    CreateOutcome.NoPartitionKeyRanges => TryCatch <PartitionKeyHashRanges> .FromException(
                        new ArgumentException($"{nameof(partitionKeyHashRanges)} must not be empty.")),
                    CreateOutcome.NullPartitionKeyRanges => TryCatch <PartitionKeyHashRanges> .FromException(
                        new ArgumentNullException(nameof(partitionKeyHashRanges))),
                    CreateOutcome.RangesAreNotContiguous => TryCatch <PartitionKeyHashRanges> .FromException(
                        new ArgumentException($"{nameof(partitionKeyHashRanges)} must have contiguous ranges.")),
                    CreateOutcome.RangesOverlap => TryCatch <PartitionKeyHashRanges> .FromException(
                        new ArgumentException($"{nameof(partitionKeyHashRanges)} must not overlapping ranges.")),
                    CreateOutcome.Success => TryCatch <PartitionKeyHashRanges> .FromResult(partitionedSortedEffectiveRanges),
                    _ => throw new ArgumentOutOfRangeException($"Unknown {nameof(CreateOutcome)}: {createStatus}."),
                });
        public static CreateOutcome TryCreate(
            IEnumerable <PartitionKeyHashRange> partitionKeyHashRanges,
            out PartitionKeyHashRanges partitionedSortedEffectiveRanges)
        {
            if (partitionKeyHashRanges == null)
            {
                partitionedSortedEffectiveRanges = default;
                return(CreateOutcome.NullPartitionKeyRanges);
            }

            if (partitionKeyHashRanges.Count() == 0)
            {
                partitionedSortedEffectiveRanges = default;
                return(CreateOutcome.NoPartitionKeyRanges);
            }

            SortedSet <PartitionKeyHashRange> sortedSet = new SortedSet <PartitionKeyHashRange>();

            foreach (PartitionKeyHashRange partitionKeyHashRange in partitionKeyHashRanges)
            {
                if (partitionKeyHashRange.StartInclusive.Equals(partitionKeyHashRange.EndExclusive))
                {
                    if (partitionKeyHashRange.StartInclusive.HasValue && partitionKeyHashRange.EndExclusive.HasValue)
                    {
                        partitionedSortedEffectiveRanges = default;
                        return(CreateOutcome.EmptyPartitionKeyRange);
                    }
                }

                if (!sortedSet.Add(partitionKeyHashRange))
                {
                    partitionedSortedEffectiveRanges = default;
                    return(CreateOutcome.DuplicatePartitionKeyRange);
                }
            }

            // Need to check if the ranges overlap
            // This can be done by checking the overall range that the ranges cover
            // and comparing it to the width of all the ranges.
            // https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-two-integer-ranges-for-overlap
            UInt128 minStart = UInt128.MaxValue;
            UInt128 maxEnd   = UInt128.MinValue;

            (UInt128 sumOfWidth, bool overflowed) = (0, false);

            foreach (PartitionKeyHashRange partitionKeyHashRange in sortedSet)
            {
                if (partitionKeyHashRange.StartInclusive.HasValue)
                {
                    if (partitionKeyHashRange.StartInclusive.Value.Value < minStart)
                    {
                        minStart = partitionKeyHashRange.StartInclusive.Value.Value;
                    }
                }
                else
                {
                    minStart = UInt128.MinValue;
                }

                if (partitionKeyHashRange.EndExclusive.HasValue)
                {
                    if (partitionKeyHashRange.EndExclusive.Value.Value > maxEnd)
                    {
                        maxEnd = partitionKeyHashRange.EndExclusive.Value.Value;
                    }
                }
                else
                {
                    maxEnd = UInt128.MaxValue;
                }

                UInt128 width = partitionKeyHashRange.EndExclusive.GetValueOrDefault(new PartitionKeyHash(UInt128.MaxValue)).Value
                                - partitionKeyHashRange.StartInclusive.GetValueOrDefault(new PartitionKeyHash(UInt128.MinValue)).Value;
                sumOfWidth += width;
                if (sumOfWidth < width)
                {
                    overflowed = true;
                }
            }

            UInt128 rangeCoverage = maxEnd - minStart;

            if ((rangeCoverage < sumOfWidth) || overflowed)
            {
                partitionedSortedEffectiveRanges = default;
                return(CreateOutcome.RangesOverlap);
            }
            else if (rangeCoverage > sumOfWidth)
            {
                partitionedSortedEffectiveRanges = default;
                return(CreateOutcome.RangesAreNotContiguous);
            }
            else
            {
                partitionedSortedEffectiveRanges = new PartitionKeyHashRanges(sortedSet);
                return(CreateOutcome.Success);
            }
        }