Fix KeyRangeIntersectionIterator count

patch by Jonathan Ellis; reviewed by Andrés de la Peña
This commit is contained in:
Jonathan Ellis 2023-07-17 14:23:09 +02:00 committed by Caleb Rackliffe
parent 2531cb0458
commit b57181032d
3 changed files with 31 additions and 10 deletions

View File

@ -249,6 +249,8 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
private static class IntersectionStatistics extends KeyRangeIterator.Builder.Statistics private static class IntersectionStatistics extends KeyRangeIterator.Builder.Statistics
{ {
private boolean empty = true;
@Override @Override
public void update(KeyRangeIterator range) public void update(KeyRangeIterator range)
{ {
@ -256,7 +258,15 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
min = nullSafeMax(min, range.getMinimum()); min = nullSafeMax(min, range.getMinimum());
// maximum of the intersection is the smallest maximum of individual iterators // maximum of the intersection is the smallest maximum of individual iterators
max = nullSafeMin(max, range.getMaximum()); max = nullSafeMin(max, range.getMaximum());
count += range.getCount(); if (empty)
{
empty = false;
count = range.getCount();
}
else
{
count = Math.min(count, range.getCount());
}
} }
} }

View File

@ -18,10 +18,10 @@
package org.apache.cassandra.index.sai.iterators; package org.apache.cassandra.index.sai.iterators;
import java.io.Closeable; import java.io.Closeable;
import java.util.List;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import org.apache.cassandra.index.sai.utils.PrimaryKey; import org.apache.cassandra.index.sai.utils.PrimaryKey;
import org.apache.cassandra.utils.AbstractGuavaIterator; import org.apache.cassandra.utils.AbstractGuavaIterator;
@ -29,6 +29,8 @@ import org.apache.cassandra.utils.AbstractGuavaIterator;
/** /**
* An abstract implementation of {@link AbstractGuavaIterator} that supports the building and management of * An abstract implementation of {@link AbstractGuavaIterator} that supports the building and management of
* concatanation, union and intersection iterators. * concatanation, union and intersection iterators.
*
* Only certain methods are designed to be overriden. The others are marked private or final.
*/ */
public abstract class KeyRangeIterator extends AbstractGuavaIterator<PrimaryKey> implements Closeable public abstract class KeyRangeIterator extends AbstractGuavaIterator<PrimaryKey> implements Closeable
{ {
@ -81,7 +83,8 @@ public abstract class KeyRangeIterator extends AbstractGuavaIterator<PrimaryKey>
* *
* @param nextKey value to skip the iterator forward until matching * @param nextKey value to skip the iterator forward until matching
* *
* @return The next current key after the skip was performed * @return The key skipped to, which will be the key returned by the
* next call to {@link #next()}, i.e., we are "peeking" at the next key as part of the skip.
*/ */
public final PrimaryKey skipTo(PrimaryKey nextKey) public final PrimaryKey skipTo(PrimaryKey nextKey)
{ {
@ -106,14 +109,22 @@ public abstract class KeyRangeIterator extends AbstractGuavaIterator<PrimaryKey>
return recomputeNext(); return recomputeNext();
} }
/**
* Skip to nextKey.
*
* That is, implementations should set up the iterator state such that
* calling computeNext() will return nextKey if present,
* or the first one after it if not present.
*/
protected abstract void performSkipTo(PrimaryKey nextKey); protected abstract void performSkipTo(PrimaryKey nextKey);
protected PrimaryKey recomputeNext() private PrimaryKey recomputeNext()
{ {
return tryToComputeNext() ? peek() : endOfData(); return tryToComputeNext() ? peek() : endOfData();
} }
protected boolean tryToComputeNext() @Override
protected final boolean tryToComputeNext()
{ {
boolean hasNext = super.tryToComputeNext(); boolean hasNext = super.tryToComputeNext();
current = hasNext ? next : getMaximum(); current = hasNext ? next : getMaximum();
@ -159,9 +170,9 @@ public abstract class KeyRangeIterator extends AbstractGuavaIterator<PrimaryKey>
return statistics.count; return statistics.count;
} }
public Builder add(List<KeyRangeIterator> ranges) public Builder add(Iterable<KeyRangeIterator> ranges)
{ {
if (ranges == null || ranges.isEmpty()) if (ranges == null || Iterables.isEmpty(ranges))
return this; return this;
ranges.forEach(this::add); ranges.forEach(this::add);

View File

@ -145,14 +145,14 @@ public class KeyRangeIntersectionIteratorTest extends AbstractKeyRangeIteratorTe
builder.add(new LongIterator(new long[]{7L, 8L, 9L})); builder.add(new LongIterator(new long[]{7L, 8L, 9L}));
assertEquals(9L, builder.getMaximum().token().getLongValue()); assertEquals(9L, builder.getMaximum().token().getLongValue());
assertEquals(9L, builder.getCount()); assertEquals(3L, builder.getCount());
KeyRangeIterator tokens = builder.build(); KeyRangeIterator tokens = builder.build();
assertNotNull(tokens); assertNotNull(tokens);
assertEquals(7L, tokens.getMinimum().token().getLongValue()); assertEquals(7L, tokens.getMinimum().token().getLongValue());
assertEquals(9L, tokens.getMaximum().token().getLongValue()); assertEquals(9L, tokens.getMaximum().token().getLongValue());
assertEquals(9L, tokens.getCount()); assertEquals(3L, tokens.getCount());
assertEquals(convert(9L), convert(builder.build())); assertEquals(convert(9L), convert(builder.build()));
} }
@ -173,7 +173,7 @@ public class KeyRangeIntersectionIteratorTest extends AbstractKeyRangeIteratorTe
assertEquals(6L, builder.getMinimum().token().getLongValue()); assertEquals(6L, builder.getMinimum().token().getLongValue());
assertEquals(6L, builder.getMaximum().token().getLongValue()); assertEquals(6L, builder.getMaximum().token().getLongValue());
assertEquals(9L, builder.getCount()); assertEquals(3L, builder.getCount());
assertEquals(3L, builder.rangeCount()); assertEquals(3L, builder.rangeCount());
assertFalse(builder.isDisjoint()); assertFalse(builder.isDisjoint());