This is an automated email from the ASF dual-hosted git repository. konstantinov pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit 909ed8b949403abcd72995f5dedcbcd0f5f6531d Merge: f05b27502f 1a12cbb349 Author: Dmitry Konstantinov <[email protected]> AuthorDate: Sat Nov 22 11:47:05 2025 +0000 Merge branch 'cassandra-5.0' into trunk * cassandra-5.0: Fix cleanup of old incremental repair sessions in case of owned token range changes or a table deleting CHANGES.txt | 1 + src/java/org/apache/cassandra/dht/Range.java | 15 +++ .../cassandra/repair/consistent/LocalSessions.java | 30 ++++- ...ncrementalRepairCleanupAfterNodeAddingTest.java | 147 +++++++++++++++++++++ test/unit/org/apache/cassandra/dht/RangeTest.java | 44 ++++++ .../repair/consistent/LocalSessionTest.java | 16 +++ 6 files changed, 251 insertions(+), 2 deletions(-) diff --cc CHANGES.txt index b0b278a751,f6dfe4045a..e2262d4f38 --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -333,21 -99,7 +333,22 @@@ Merged from 4.1 * Optionally skip exception logging on invalid legacy protocol magic exception (CASSANDRA-19483) * Fix SimpleClient ability to release acquired capacity (CASSANDRA-20202) * Fix WaitQueue.Signal.awaitUninterruptibly may block forever if invoking thread is interrupted (CASSANDRA-20084) + * Run audit_logging_options through santiation and validation on startup (CASSANDRA-20208) + * Enforce CQL message size limit on multiframe messages (CASSANDRA-20052) + * Fix race condition in DecayingEstimatedHistogramReservoir during rescale (CASSANDRA-19365) Merged from 4.0: ++ * Fix cleanup of old incremental repair sessions in case of owned token range changes or a table deleting (CASSANDRA-20877) + * Fix memory leak in BufferPoolAllocator when a capacity needs to be extended (CASSANDRA-20753) + * Leveled Compaction doesn't validate maxBytesForLevel when the table is altered/created (CASSANDRA-20570) + * Updated dtest-api to 0.0.18 and removed JMX-related classes that now live in the dtest-api (CASSANDRA-20884) + * Fixed incorrect error message constant for keyspace name length validation (CASSANDRA-20915) + * Prevent too long table names not fitting file names (CASSANDRA-20389) + * Update Jackson to 2.19.2 (CASSANDRA-20848) + * Update commons-lang3 to 3.18.0 (CASSANDRA-20849) + * Add NativeTransportMaxConcurrentConnectionsPerIp to StorageProxyMBean (CASSANDRA-20642) + * Make secondary index implementations notified about rows in fully expired SSTables in compaction (CASSANDRA-20829) + * Ensure prepared_statement INSERT timestamp precedes eviction DELETE (CASSANDRA-19703) + * Gossip doesn't converge due to race condition when updating EndpointStates multiple fields (CASSANDRA-20659) * Handle sstable metadata stats file getting a new mtime after compaction has finished (CASSANDRA-18119) * Honor MAX_PARALLEL_TRANSFERS correctly (CASSANDRA-20532) * Updating a column with a new TTL but same expiration time is non-deterministic and causes repair mismatches. (CASSANDRA-20561) diff --cc src/java/org/apache/cassandra/repair/consistent/LocalSessions.java index 47d614216d,e2bfe05eb2..04fb7a6cf4 --- a/src/java/org/apache/cassandra/repair/consistent/LocalSessions.java +++ b/src/java/org/apache/cassandra/repair/consistent/LocalSessions.java @@@ -256,7 -262,15 +262,15 @@@ public class LocalSession if (state == null) return false; - long minRepaired = state.minRepairedAt(session.ranges); + Collection<Range<Token>> actualRanges = rangesPerKeyspaceCache.computeIfAbsent(tableMetadata.keyspace, (keyspace) -> { - List<Range<Token>> localRanges = getLocalRanges(tableMetadata.keyspace); ++ Collection<Range<Token>> localRanges = getLocalRanges(tableMetadata.keyspace); + if (localRanges.isEmpty()) // to handle the case when we run before the information about owned ranges is properly populated + return session.ranges; + + // ignore token ranges which were moved to other nodes and not owned by the current one anymore + return Range.intersect(session.ranges, localRanges); + }); + long minRepaired = state.minRepairedAt(actualRanges); if (minRepaired <= session.repairedAt) return false; } @@@ -264,6 -278,18 +278,18 @@@ return true; } + @VisibleForTesting + protected TableMetadata getTableMetadata(TableId tableId) + { + return Schema.instance.getTableMetadata(tableId); + } + + @VisibleForTesting - protected List<Range<Token>> getLocalRanges(String keyspace) ++ protected Collection<Range<Token>> getLocalRanges(String keyspace) + { + return StorageService.instance.getLocalAndPendingRanges(keyspace); + } + public RepairedState.Stats getRepairedStats(TableId tid, Collection<Range<Token>> ranges) { RepairedState state = repairedStates.get(tid); diff --cc test/unit/org/apache/cassandra/dht/RangeTest.java index e8198b9cb4,fd982d9866..cac2594ddc --- a/test/unit/org/apache/cassandra/dht/RangeTest.java +++ b/test/unit/org/apache/cassandra/dht/RangeTest.java @@@ -766,417 -737,47 +766,461 @@@ public class RangeTest extends Cassandr assertEquals(Sets.newHashSet(r(1, 4), r(11, 15)), Range.subtract(ranges, asList(r(4, 7), r(8, 11)))); } + @Test + public void testIntersectsBounds() + { + Range<Token> r = r(0, 100); + assertTrue(r.intersects(bounds(5, 10))); + assertTrue(r.intersects(bounds(100, 110))); + assertTrue(r.intersects(bounds(-100, 200))); + assertTrue(r.intersects(bounds(10, 15))); + assertTrue(r.intersects(bounds(20,20))); + + assertFalse(r.intersects(bounds(-5, 0))); + assertFalse(r.intersects(bounds(-5, -1))); + assertFalse(r.intersects(bounds(110, 114))); + } + + private static Bounds<Token> bounds(long left, long right) + { + return new Bounds<>(t(left), t(right)); + } + + private static AbstractBounds<PartitionPosition> bounds(PartitionPosition left, boolean leftInclusive, PartitionPosition right, boolean rightInclusive) + { + return AbstractBounds.bounds(left, leftInclusive, right, rightInclusive); + } + + @Test + @UseMurmur3Partitioner + public void testIsInNormalizedRanges() + { + NormalizedRanges<Token> ranges = normalizedRanges(ImmutableList.of(fromString("(1,10]"), fromString("(10,20]"), fromString("(30,40]"), fromString("(50,60]"), fromString("(60,70]"), fromString("(80,90]"), fromString("(" + Long.MAX_VALUE + ",-9223372036854775808]"))); + for (int ii = 0; ii < 100; ii++) + { + boolean isIn = ranges.intersects(new LongToken(ii)); + if (ii > 1 && ii <= 20) + assertTrue("Index " + ii, isIn); + else if (ii > 30 && ii <= 40) + assertTrue("Index " + ii, isIn); + else if (ii > 50 && ii <= 70) + assertTrue("Index " + ii, isIn); + else if (ii > 80 && ii <= 90) + assertTrue("Index " + ii, isIn); + else + assertFalse("Index " + ii, isIn); + } + assertFalse(ranges.intersects(new LongToken(Long.MAX_VALUE))); + assertTrue(ranges.intersects(new LongToken(Long.MIN_VALUE))); + ranges = normalizedRanges(ImmutableList.of(fromString("(-9223372036854775808,-9223372036854775807]"))); + assertFalse(ranges.intersects(new LongToken(Long.MIN_VALUE))); + assertTrue(ranges.intersects(new LongToken(Long.MIN_VALUE + 1))); + ranges = normalizedRanges(ImmutableList.of(fromString("(" + (Long.MAX_VALUE - 1) + ",-9223372036854775808]"))); + assertFalse(ranges.intersects(new LongToken(Long.MAX_VALUE - 1))); + assertTrue(ranges.intersects(new LongToken(Long.MAX_VALUE))); + assertTrue(ranges.intersects(new LongToken(Long.MIN_VALUE))); + assertFalse(ranges.intersects(new LongToken(Long.MAX_VALUE - 1))); + assertTrue(ranges.intersects(new LongToken(Long.MAX_VALUE))); + assertTrue(ranges.intersects(new LongToken(Long.MIN_VALUE))); + } + + @Test + @UseMurmur3Partitioner + public void testSubtractNormalizedRanges() + { + NormalizedRanges<Token> ranges = normalizedRanges(ImmutableList.of(fromString("(1,10]"), fromString("(10,20]"), fromString("(30,40]"), fromString("(50,60]"), fromString("(60,70]"), fromString("(80,90]"), fromString("(" + Long.MAX_VALUE + ",-9223372036854775808]"))); + for (int ii = 0; ii < 100; ii++) + { + boolean isIn = ranges.intersects(new LongToken(ii)); + if (ii > 1 && ii <= 20) + assertTrue("Index " + ii, isIn); + else if (ii > 30 && ii <= 40) + assertTrue("Index " + ii, isIn); + else if (ii > 50 && ii <= 70) + assertTrue("Index " + ii, isIn); + else if (ii > 80 && ii <= 90) + assertTrue("Index " + ii, isIn); + else + assertFalse("Index " + ii, isIn); + } + NormalizedRanges<Token> rightMostRange = normalizedRanges(ImmutableList.of(r(Long.MAX_VALUE, Long.MIN_VALUE))); + NormalizedRanges<Token> maxLongRange = normalizedRanges(ImmutableList.of(r(Long.MAX_VALUE - 1, Long.MAX_VALUE))); + + assertEquals(emptyList(), ranges.subtract(ranges)); + assertEquals(emptyList(), rightMostRange.subtract(ranges)); + assertEquals(maxLongRange, maxLongRange.subtract(ranges)); + ranges = maxLongRange; + assertEquals(emptyList(), ranges.subtract(ranges)); + assertEquals(rightMostRange, rightMostRange.subtract(ranges)); + assertEquals(emptyList(), maxLongRange.subtract(ranges)); + ranges = normalizedRanges(ImmutableList.of(fromString("(" + (Long.MAX_VALUE - 1) + ",-9223372036854775808]"))); + assertEquals(emptyList(), ranges.subtract(ranges)); + assertEquals(emptyList(), rightMostRange.subtract(ranges)); + assertEquals(emptyList(), maxLongRange.subtract(ranges)); + } + + @Test + public void testExpensiveChecksBurn() + { + long seed = System.nanoTime(); + System.out.println(seed); + Random r = new java.util.Random(seed); + + Stopwatch elapsed = Stopwatch.createStarted(); + while (elapsed.elapsed(SECONDS) != 10) + { + int numRanges = 3; + List<Range<Token>> aList = new ArrayList(); + for (int ii = 0; ii < numRanges; ii++) + { + aList.add(new Range<>(new LongToken(r.nextLong()), new LongToken(r.nextLong()))); + } + NormalizedRanges<Token> a = normalizedRanges(aList); + List<Range<Token>> bList = new ArrayList(); + for (int ii = 0; ii < numRanges; ii++) + { + bList.add(new Range<>(new LongToken(r.nextLong()), new LongToken(r.nextLong()))); + } + NormalizedRanges<Token> b = normalizedRanges(bList); + + for (int ii = 0; ii < 1000; ii++) + { + Token t = new LongToken(r.nextLong()); + a.intersects(t); + b.intersects(t); + } + + a.intersection(b); + a.intersection(a); + b.intersection(a); + b.intersection(b); + + a.subtract(b); + a.subtract(a); + b.subtract(a); + b.subtract(b); + + if (!a.isEmpty()) + a.invert(); + if (!b.isEmpty()) + b.invert(); + } + } + + @Test + public void testIntersectionAndRemainder() + { + // Intersection will only make max key bounds so use minKeyBound + // so you know where the bound came from + Token oneT = t(1); + PartitionPosition onePP = oneT.minKeyBound(); + Token twoT = t(2); + PartitionPosition twoPP = twoT.minKeyBound(); + Token threeT = t(3); + PartitionPosition threePP = threeT.minKeyBound(); + Token fourT = t(4); + PartitionPosition fourPP = fourT.minKeyBound(); + Token fiveT = t(5); + PartitionPosition fivePP = fiveT.minKeyBound(); + Token sixT = t(6); + PartitionPosition sixPP = sixT.minKeyBound(); + Token sevenT = t(7); + PartitionPosition sevenPP = sevenT.minKeyBound(); + Token eightT = t(8); + PartitionPosition eightPP = eightT.minKeyBound(); + Token minT = Murmur3Partitioner.MINIMUM; + PartitionPosition minPP = minT.minKeyBound(); + + Range<Token> r = r(threeT, sixT); + + // Completely before + testInclusivity(onePP, twoPP, r, + Pair.create(null, null), + Pair.create(null, null), + Pair.create(null, null), + Pair.create(null, null)); + + // Completely after + testInclusivity(sevenPP, eightPP, r, + Pair.create(null, bounds(sevenPP, true, eightPP, true)), + Pair.create(null, bounds(sevenPP, true, eightPP, false)), + Pair.create(null, bounds(sevenPP, false, eightPP, true)), + Pair.create(null, bounds(sevenPP, false, eightPP, false))); + + // Overlapping + testInclusivity(threePP, sixPP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, sixPP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, sixPP, false), null), + Pair.create(bounds(threeT.maxKeyBound(), false, sixPP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, sixPP, false), null)); + + // Completely contained by range, should echo back the same bound + testInclusivity(fourPP, fivePP, r, + Pair.create(bounds(fourPP, true, fivePP, true), null), + Pair.create(bounds(fourPP, true, fivePP, false), null), + Pair.create(bounds(fourPP, false, fivePP, true), null), + Pair.create(bounds(fourPP, false, fivePP, false), null)); + + // Overlap left only + testInclusivity(threePP, fivePP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null)); + + // Overlap right only + testInclusivity(fourPP, sixPP, r, + Pair.create(bounds(fourPP, true, sixPP, true), null), + Pair.create(bounds(fourPP, true, sixPP, false), null), + Pair.create(bounds(fourPP, false, sixPP, true), null), + Pair.create(bounds(fourPP, false, sixPP, false), null)); + + // Contains range + testInclusivity(twoPP, sevenPP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, true)), + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, false)), + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, true)), + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, false))); + + // Split by range left bound + testInclusivity(twoPP, fivePP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null)); + + // Split by range right bound + testInclusivity(fivePP, sevenPP, r, + Pair.create(bounds(fivePP, true, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, true)), + Pair.create(bounds(fivePP, true, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, false)), + Pair.create(bounds(fivePP, false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, true)), + Pair.create(bounds(fivePP, false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, sevenPP, false))); + + /* + * Test size 1 bound + */ + // Completely before + testInclusivity(onePP, onePP, r, + Pair.create(null, null)); + + // Completely after + testInclusivity(eightPP, eightPP, r, + Pair.create(null, bounds(eightPP, true, eightPP, true))); + + // Completely contained by range, should echo back the same bound + testInclusivity(fivePP, fivePP, r, + Pair.create(bounds(fivePP, true, fivePP, true), null)); + + // Overlap left only + testInclusivity(threePP, threePP, r, + Pair.create(null, null)); + + // Overlap right only + testInclusivity(sixPP, sixPP, r, + Pair.create(bounds(sixPP, true, sixPP, true), null)); + + /* + * Test all cases where the right of Bounds is minimum + */ + // Completely after + testInclusivity(sevenPP, minPP, r, + Pair.create(null, bounds(sevenPP, true, minPP, true)), + Pair.create(null, bounds(sevenPP, true, minPP, false)), + Pair.create(null, bounds(sevenPP, false, minPP, true)), + Pair.create(null, bounds(sevenPP, false, minPP, false))); + + // Contains range + testInclusivity(twoPP, minPP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, true)), + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, false)), + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, true)), + Pair.create(bounds(threeT.maxKeyBound(), false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, false))); + + // Split by range right bound + testInclusivity(fivePP, minPP, r, + Pair.create(bounds(fivePP, true, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, true)), + Pair.create(bounds(fivePP, true, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, false)), + Pair.create(bounds(fivePP, false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, true)), + Pair.create(bounds(fivePP, false, sixT.maxKeyBound(), true), bounds(sixT.maxKeyBound(), false, minPP, false))); + + /* + * Test all cases where the right of the range is minimum + */ + r = r(threeT, minT); + // Completely before + testInclusivity(onePP, twoPP, r, + Pair.create(null, null), + Pair.create(null, null), + Pair.create(null, null), + Pair.create(null, null)); + + // Overlapping + testInclusivity(threePP, minPP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, false), null), + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, false), null)); + + // Completely contained by range, should echo back the same bound + testInclusivity(fourPP, fivePP, r, + Pair.create(bounds(fourPP, true, fivePP, true), null), + Pair.create(bounds(fourPP, true, fivePP, false), null), + Pair.create(bounds(fourPP, false, fivePP, true), null), + Pair.create(bounds(fourPP, false, fivePP, false), null)); + + // Overlap left only + testInclusivity(threePP, fivePP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null)); + + // Overlap right only + testInclusivity(fourPP, minPP, r, + Pair.create(bounds(fourPP, true, minPP, true), null), + Pair.create(bounds(fourPP, true, minPP, false), null), + Pair.create(bounds(fourPP, false, minPP, true), null), + Pair.create(bounds(fourPP, false, minPP, false), null)); + + // Contains range + testInclusivity(twoPP, minPP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, false), null), + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, minPP, false), null)); + + // Split by range left bound + testInclusivity(twoPP, fivePP, r, + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, true), null), + Pair.create(bounds(threeT.maxKeyBound(), false, fivePP, false), null)); + } + + private void testInclusivity(PartitionPosition left, PartitionPosition right, Range<Token> range, + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>> expected) + { + testInclusivity(left, right, range, expected, null, null, null); + } + + private void testInclusivity(PartitionPosition left, PartitionPosition right, Range<Token> range, + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>> expected1, + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>> expected2, + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>> expected3, + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>> expected4) + { + testInclusivity(left, right, range, new Pair[] { expected1, expected2, expected3, expected4 }); + } + + private void testInclusivity(PartitionPosition left, PartitionPosition right, Range<Token> range, + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>>[] expecteds) + { + int i = 0; + for (Boolean leftInclusive : ImmutableList.of(true, false)) + { + for (Boolean rightInclusive : ImmutableList.of(true, false)) + { + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>> expected = expecteds[i++]; + if (expected == null) + continue; + AbstractBounds<PartitionPosition> expectedIntersection = expected.left; + AbstractBounds<PartitionPosition> expectedRemainder = expected.right; + AbstractBounds<PartitionPosition> testBounds = bounds(left, leftInclusive, right, rightInclusive); + Pair<AbstractBounds<PartitionPosition>, AbstractBounds<PartitionPosition>> interSectionAndRemainder = intersectionAndRemainder(testBounds, range); + AbstractBounds<PartitionPosition> intersection = interSectionAndRemainder.left; + AbstractBounds<PartitionPosition> remainder = interSectionAndRemainder.right; + String message = format("Expected %s intersecting inclusive left %b, inclusive right %b, %s with %s", expected, leftInclusive, rightInclusive, bounds(left, leftInclusive, right, rightInclusive), range); + assertEquals(message, expected, intersectionAndRemainder(bounds(left, leftInclusive, right, rightInclusive), range)); + System.out.println(message.replace("Expected", "Expecting")); + + if (remainder == testBounds) + assertNull(intersection); + if (intersection == testBounds) + assertNull(remainder); + if (Objects.equals(remainder, testBounds) && remainder != testBounds) + fail("Should return existing bounds"); + if (Objects.equals(intersection, testBounds) && intersection != testBounds) + fail("Should return existing bounds"); + + // Need to validate that we roundtrip the actual exact input PartitionPosition and don't lose part of the input bound that might be needed + // Remainder can either be the entire thing because there is no intersection + // Remainder should always preserve the right bound since range is right inclusive, the left bound will always be a `maxKeyBound` from the range + if (remainder != null && remainder != testBounds) + { + assertTrue(remainder.right == expectedRemainder.right); + assertTrue(remainder.right == right); + assertEquals(remainder.inclusiveRight(), expectedRemainder.inclusiveRight()); + assertFalse(remainder.inclusiveLeft()); + assertFalse(remainder.left == left); + // Not strictly necessary, but we do always use the left of the range and create a new key bound + assertEquals(remainder.left, range.right.maxKeyBound()); + } + + // Range is left exclusive so the left should be preserved if it is greater than range left + // otherwise it should be replaced + if (intersection != null && intersection != testBounds) + { + if (intersection.left.getToken().compareTo(range.left) > 0) + { + assertEquals(intersection.inclusiveLeft(), expectedIntersection.inclusiveLeft()); + assertTrue(intersection.inclusiveRight()); + assertTrue(intersection.left == expectedIntersection.left); + assertTrue(intersection.left == left); + } + else + { + // Should be replaced by a KeyBound from the range + assertTrue(intersection.left != expectedIntersection.left); + assertTrue(intersection.left != left); + // Max bound since range is not left inclusive and excluded + assertEquals(intersection.left, range.left.maxKeyBound()); + } + } + } + } + } ++ + @Test + public void testGroupIntersection() + { + assertEquals(Collections.emptyList(), + Range.intersect(asList(r(1, 5), r(10, 15)), + asList(r(6, 7), r(20, 25)) + )); + + assertEquals(asList(r(5, 6)), + Range.intersect(asList(r(1, 6), r(10, 15)), + asList(r(5, 10)) + )); + + assertEquals(asList(r(5, 6), r(10, 11)), + Range.intersect(asList(r(1, 6), r(10, 15)), + asList(r(5, 11)) + )); + + assertEquals(asList(r(5, 6), r(10, 11)), + Range.intersect(asList(r(1, 6), r(10, 15)), + asList(r(5, 11)) + )); + + assertEquals(asList(r(5, 6), r(10, 11), r(12, 15)), + Range.intersect(asList(r(1, 6), r(10, 15)), + asList(r(5, 11), r(12, 20)) + )); + + assertEquals(asList(r(5, 6), r(10, 15)), + Range.intersect(asList(r(1, 6), r(10, 15)), + asList(r(5, 11), r(11, 20)) + )); + + assertEquals(Collections.emptyList(), + Range.intersect(Collections.emptyList(), + asList(r(5, 11), r(11, 20)) + )); + + assertEquals(Collections.emptyList(), + Range.intersect(asList(r(1, 6), r(10, 15)), + Collections.emptyList() + )); + } } diff --cc test/unit/org/apache/cassandra/repair/consistent/LocalSessionTest.java index 5908e7d8e7,57976c3e33..e4965d2a87 --- a/test/unit/org/apache/cassandra/repair/consistent/LocalSessionTest.java +++ b/test/unit/org/apache/cassandra/repair/consistent/LocalSessionTest.java @@@ -49,8 -52,9 +52,9 @@@ import org.apache.cassandra.net.Message import org.apache.cassandra.repair.AbstractRepairTest; import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.repair.KeyspaceRepairManager; +import org.apache.cassandra.schema.Schema; + import org.apache.cassandra.schema.TableId; import org.apache.cassandra.schema.TableMetadata; -import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.SchemaConstants; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
