mirror of https://github.com/apache/cassandra
Avoid over-skipping of key iterators from static column indexes during mixed intersections
patch by Caleb Rackliffe; reviewed by Piotr Kołaczkowski and Alex Petrov for CASSANDRA-19278 Co-authored-by: Caleb Rackliffe <calebrackliffe@gmail.com> Co-authored-by: Piotr Kołaczkowski <pkolaczk@gmail.com>
This commit is contained in:
parent
c00ebc8d2d
commit
87f2a7b763
|
|
@ -1,4 +1,5 @@
|
|||
5.0-beta2
|
||||
* Avoid over-skipping of key iterators from static column indexes during mixed intersections (CASSANDRA-19278)
|
||||
* Make concurrent_index_builders configurable at runtime (CASSANDRA-19266)
|
||||
* Fix storage_compatibility_mode for streaming (CASSANDRA-19126)
|
||||
* Add support of vector type to cqlsh COPY command (CASSANDRA-19118)
|
||||
|
|
|
|||
|
|
@ -70,12 +70,6 @@ public class RowMapping
|
|||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
private final InMemoryTrie<Long> rowMapping = new InMemoryTrie<>(BufferType.OFF_HEAP);
|
||||
|
|
@ -84,10 +78,14 @@ public class RowMapping
|
|||
|
||||
public PrimaryKey minKey;
|
||||
public PrimaryKey maxKey;
|
||||
public PrimaryKey minStaticKey;
|
||||
public PrimaryKey maxStaticKey;
|
||||
|
||||
public long maxSSTableRowId = -1;
|
||||
public long maxStaticSSTableRowId = -1;
|
||||
|
||||
public int count;
|
||||
public int rowCount;
|
||||
public int staticRowCount;
|
||||
|
||||
private RowMapping()
|
||||
{}
|
||||
|
|
@ -171,20 +169,32 @@ public class RowMapping
|
|||
|
||||
rowMapping.putSingleton(key, sstableRowId, OVERWRITE_TRANSFORMER);
|
||||
|
||||
maxSSTableRowId = Math.max(maxSSTableRowId, sstableRowId);
|
||||
|
||||
// data is written in token sorted order
|
||||
if (minKey == null)
|
||||
minKey = key;
|
||||
maxKey = key;
|
||||
count++;
|
||||
// Data is written in primary key order. If a schema contains clustering keys, it may also contain static
|
||||
// columns. We track min, max, and count for static keys separately here so that we can pass them to the segment
|
||||
// metadata for indexes on static columns.
|
||||
if (key.kind() == PrimaryKey.Kind.STATIC)
|
||||
{
|
||||
if (minStaticKey == null)
|
||||
minStaticKey = key;
|
||||
maxStaticKey = key;
|
||||
staticRowCount++;
|
||||
maxStaticSSTableRowId = Math.max(maxStaticSSTableRowId, sstableRowId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (minKey == null)
|
||||
minKey = key;
|
||||
maxKey = key;
|
||||
rowCount++;
|
||||
maxSSTableRowId = Math.max(maxSSTableRowId, sstableRowId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SSTable row Id for a {@link PrimaryKey}
|
||||
* Returns the SSTable row ID for a {@link PrimaryKey}
|
||||
*
|
||||
* @param key the {@link PrimaryKey}
|
||||
* @return a valid SSTable row Id for the {@link PrimaryKey} or -1 if the {@link PrimaryKey} doesn't exist
|
||||
* @return a valid SSTable row ID for the {@link PrimaryKey} or -1 if the {@link PrimaryKey} doesn't exist
|
||||
* in the {@link RowMapping}
|
||||
*/
|
||||
public int get(PrimaryKey key)
|
||||
|
|
@ -193,13 +203,8 @@ public class RowMapping
|
|||
return sstableRowId == null ? -1 : Math.toIntExact(sstableRowId);
|
||||
}
|
||||
|
||||
public int size()
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean hasRows()
|
||||
{
|
||||
return maxSSTableRowId >= 0;
|
||||
return maxSSTableRowId >= 0 || maxStaticSSTableRowId >= 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,15 +155,15 @@ public class MemtableIndexWriter implements PerColumnIndexWriter
|
|||
return 0;
|
||||
}
|
||||
|
||||
PrimaryKey minKey = indexTermType.columnMetadata().isStatic() ? rowMapping.minStaticKey : rowMapping.minKey;
|
||||
PrimaryKey maxKey = indexTermType.columnMetadata().isStatic() ? rowMapping.maxStaticKey : rowMapping.maxKey;
|
||||
|
||||
// During index memtable flush, the data is sorted based on terms.
|
||||
SegmentMetadata metadata = new SegmentMetadata(0,
|
||||
numRows,
|
||||
terms.getMinSSTableRowId(),
|
||||
terms.getMaxSSTableRowId(),
|
||||
rowMapping.minKey,
|
||||
rowMapping.maxKey,
|
||||
terms.getMinTerm(),
|
||||
terms.getMaxTerm(),
|
||||
terms.getMinSSTableRowId(), terms.getMaxSSTableRowId(),
|
||||
minKey, maxKey,
|
||||
terms.getMinTerm(), terms.getMaxTerm(),
|
||||
indexMetas);
|
||||
|
||||
try (MetadataWriter metadataWriter = new MetadataWriter(indexDescriptor.openPerIndexOutput(IndexComponent.META, indexIdentifier)))
|
||||
|
|
@ -176,18 +176,19 @@ public class MemtableIndexWriter implements PerColumnIndexWriter
|
|||
|
||||
private void flushVectorIndex(long startTime, Stopwatch stopwatch) throws IOException
|
||||
{
|
||||
SegmentMetadata.ComponentMetadataMap metadataMap = memtable.writeDirect(indexDescriptor, indexIdentifier, rowMapping::get);
|
||||
int rowCount = indexTermType.columnMetadata().isStatic() ? rowMapping.staticRowCount : rowMapping.rowCount;
|
||||
PrimaryKey minKey = indexTermType.columnMetadata().isStatic() ? rowMapping.minStaticKey : rowMapping.minKey;
|
||||
PrimaryKey maxKey = indexTermType.columnMetadata().isStatic() ? rowMapping.maxStaticKey : rowMapping.maxKey;
|
||||
long maxSSTableRowId = indexTermType.columnMetadata().isStatic() ? rowMapping.maxStaticSSTableRowId : rowMapping.maxSSTableRowId;
|
||||
|
||||
completeIndexFlush(rowMapping.size(), startTime, stopwatch);
|
||||
SegmentMetadata.ComponentMetadataMap metadataMap = memtable.writeDirect(indexDescriptor, indexIdentifier, rowMapping::get);
|
||||
completeIndexFlush(rowCount, startTime, stopwatch);
|
||||
|
||||
SegmentMetadata metadata = new SegmentMetadata(0,
|
||||
rowMapping.size(),
|
||||
0,
|
||||
rowMapping.maxSSTableRowId,
|
||||
rowMapping.minKey,
|
||||
rowMapping.maxKey,
|
||||
ByteBufferUtil.bytes(0),
|
||||
ByteBufferUtil.bytes(0),
|
||||
rowCount,
|
||||
0, maxSSTableRowId,
|
||||
minKey, maxKey,
|
||||
ByteBufferUtil.bytes(0), ByteBufferUtil.bytes(0),
|
||||
metadataMap);
|
||||
|
||||
try (MetadataWriter writer = new MetadataWriter(indexDescriptor.openPerIndexOutput(IndexComponent.META, indexIdentifier)))
|
||||
|
|
|
|||
|
|
@ -28,13 +28,19 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import org.apache.cassandra.config.CassandraRelevantProperties;
|
||||
import org.apache.cassandra.index.sai.utils.PrimaryKey;
|
||||
import org.apache.cassandra.index.sai.utils.PrimaryKey.Kind;
|
||||
import org.apache.cassandra.io.util.FileUtils;
|
||||
import org.apache.cassandra.tracing.Tracing;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A simple intersection iterator that makes no real attempts at optimising the iteration apart from
|
||||
* initially sorting the ranges. This implementation also supports an intersection limit which limits
|
||||
* the number of ranges that will be included in the intersection. This currently defaults to 2.
|
||||
* initially sorting the ranges. This implementation also supports an intersection limit via
|
||||
* {@code CassandraRelevantProperties.SAI_INTERSECTION_CLAUSE_LIMIT} which limits the number of ranges that will
|
||||
* be included in the intersection.
|
||||
* <p>
|
||||
* Intersection only works for ranges that are compatible according to {@link PrimaryKey.Kind#isIntersectable(Kind)}.
|
||||
*/
|
||||
public class KeyRangeIntersectionIterator extends KeyRangeIterator
|
||||
{
|
||||
|
|
@ -56,52 +62,82 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
|
|||
@Override
|
||||
protected PrimaryKey computeNext()
|
||||
{
|
||||
// Range iterator that has been advanced in the previous cycle of the outer loop.
|
||||
// Initially there hasn't been the previous cycle, so set to null.
|
||||
int alreadyAvanced = -1;
|
||||
|
||||
// The highest primary key seen on any range iterator so far.
|
||||
// Advance one iterator to the next key and remember the key as the highest seen so far.
|
||||
// It can become null when we reach the end of the iterator.
|
||||
PrimaryKey highestKey = getCurrent();
|
||||
// If there are both static and non-static keys being iterated here, we advance a non-static one,
|
||||
// regardless of the order of ranges in the ranges list.
|
||||
PrimaryKey highestKey = advanceOneRange();
|
||||
|
||||
outer:
|
||||
// We need to check if highestKey exceeds the maximum because the maximum is
|
||||
// the lowest value maximum of all the ranges. As a result any value could
|
||||
// potentially exceed it.
|
||||
// After advancing one iterator, we must try to advance all the other iterators that got behind,
|
||||
// so they catch up to it. Note that we will not advance the iterators for static columns
|
||||
// as long as they point to the partition of the highest key. (This is because STATIC primary keys
|
||||
// compare to other keys only by partition.) This loop continues until all iterators point to the same key,
|
||||
// or if we run out of keys on any of them, or if we exceed the maximum key.
|
||||
// There is no point in iterating after maximum, because no keys will match beyond that point.
|
||||
while (highestKey != null && highestKey.compareTo(getMaximum()) <= 0)
|
||||
{
|
||||
// Try advance all iterators to the highest key seen so far.
|
||||
// Try to advance all iterators to the highest key seen so far.
|
||||
// Once this inner loop finishes normally, all iterators are guaranteed to be at the same value.
|
||||
for (int index = 0; index < ranges.size(); index++)
|
||||
for (KeyRangeIterator range : ranges)
|
||||
{
|
||||
if (index != alreadyAvanced)
|
||||
if (range.getCurrent().compareTo(highestKey) < 0)
|
||||
{
|
||||
KeyRangeIterator range = ranges.get(index);
|
||||
PrimaryKey nextKey = nextOrNull(range, highestKey);
|
||||
if (nextKey == null || nextKey.compareTo(highestKey) > 0)
|
||||
// If we advance a STATIC key, then we must advance it to the same partition as the highestKey.
|
||||
// Advancing a STATIC key to a WIDE key directly (without throwing away the clustering) would
|
||||
// go too far, as WIDE keys are stored after STATIC in the posting list.
|
||||
PrimaryKey nextKey = range.getCurrent().kind() == Kind.STATIC
|
||||
? nextOrNull(range, highestKey.toStatic())
|
||||
: nextOrNull(range, highestKey);
|
||||
|
||||
// We use strict comparison here, since it orders WIDE primary keys after STATIC primary keys
|
||||
// in the same partition. When WIDE keys are present, we want to return them rather than STATIC
|
||||
// keys to avoid retrieving and post-filtering entire partitions.
|
||||
if (nextKey == null || nextKey.compareToStrict(highestKey) > 0)
|
||||
{
|
||||
// We jumped over the highest key seen so far, so make it the new highest key.
|
||||
highestKey = nextKey;
|
||||
// Remember this iterator to avoid advancing it again, because it is already at the highest key
|
||||
alreadyAvanced = index;
|
||||
// This iterator jumped over, so the other iterators are lagging behind now,
|
||||
|
||||
// This iterator jumped over, so the other iterators might be lagging behind now,
|
||||
// including the ones already advanced in the earlier cycles of the inner loop.
|
||||
// Therefore, restart the inner loop in order to advance
|
||||
// the other iterators except this one to match the new highest key.
|
||||
// Therefore, restart the inner loop in order to advance the lagging iterators.
|
||||
continue outer;
|
||||
}
|
||||
assert nextKey.compareTo(highestKey) == 0:
|
||||
String.format("skipped to an item smaller than the target; " +
|
||||
"iterator: %s, target key: %s, returned key: %s", range, highestKey, nextKey);
|
||||
assert nextKey.compareTo(highestKey) == 0 :
|
||||
String.format("Skipped to a key smaller than the target! " +
|
||||
"iterator: %s, target key: %s, returned key: %s", range, highestKey, nextKey);
|
||||
}
|
||||
}
|
||||
// If we reached here, next() has been called at least once on each range iterator and
|
||||
// the last call to next() on each iterator returned a value equal to the highestKey.
|
||||
|
||||
// If we get here, all iterators have been advanced to the same key. When STATIC and WIDE keys are
|
||||
// mixed, this means WIDE keys point to exactly the same row, and STATIC keys the same partition.
|
||||
return highestKey;
|
||||
}
|
||||
|
||||
return endOfData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the iterator of one range to the next item, which becomes the highest seen so far.
|
||||
* Iterators pointing to STATIC keys are advanced only if no non-STATIC keys have been advanced.
|
||||
*
|
||||
* @return the next highest key or null if the iterator has reached the end
|
||||
*/
|
||||
private @Nullable PrimaryKey advanceOneRange()
|
||||
{
|
||||
PrimaryKey highestKey = getCurrent();
|
||||
|
||||
for (KeyRangeIterator range : ranges)
|
||||
if (range.getCurrent().kind() != Kind.STATIC)
|
||||
return nextOrNull(range, highestKey);
|
||||
|
||||
for (KeyRangeIterator range : ranges)
|
||||
if (range.getCurrent().kind() == Kind.STATIC)
|
||||
return nextOrNull(range, highestKey);
|
||||
|
||||
throw new IllegalStateException("There should be at least one range to advance!");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performSkipTo(PrimaryKey nextKey)
|
||||
{
|
||||
|
|
@ -236,6 +272,20 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
|
|||
if (ranges.size() == 1)
|
||||
return ranges.get(0);
|
||||
|
||||
// Make sure intersection is supported on the ranges provided:
|
||||
PrimaryKey.Kind firstKind = null;
|
||||
|
||||
for (KeyRangeIterator range : ranges)
|
||||
{
|
||||
PrimaryKey key = range.getCurrent();
|
||||
|
||||
if (key != null)
|
||||
if (firstKind == null)
|
||||
firstKind = key.kind();
|
||||
else if (!firstKind.isIntersectable(key.kind()))
|
||||
throw new IllegalArgumentException("Cannot intersect " + firstKind + " and " + key.kind() + " ranges!");
|
||||
}
|
||||
|
||||
return new KeyRangeIntersectionIterator(statistics, ranges);
|
||||
}
|
||||
|
||||
|
|
@ -277,18 +327,18 @@ public class KeyRangeIntersectionIterator extends KeyRangeIterator
|
|||
|
||||
/**
|
||||
* Ranges are overlapping the following cases:
|
||||
*
|
||||
* <p>
|
||||
* * When they have a common subrange:
|
||||
*
|
||||
* <p>
|
||||
* min b.current max b.max
|
||||
* +---------|--------------+------------|
|
||||
*
|
||||
* <p>
|
||||
* b.current min max b.max
|
||||
* |--------------+---------+------------|
|
||||
*
|
||||
* <p>
|
||||
* min b.current b.max max
|
||||
* +----------|-------------|------------+
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* If either range is empty, they're disjoint.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -211,19 +211,19 @@ public abstract class KeyRangeIterator extends AbstractGuavaIterator<PrimaryKey>
|
|||
}
|
||||
}
|
||||
|
||||
protected static <T extends Comparable<T>> T nullSafeMin(T a, T b)
|
||||
protected static PrimaryKey nullSafeMin(PrimaryKey a, PrimaryKey b)
|
||||
{
|
||||
if (a == null) return b;
|
||||
if (b == null) return a;
|
||||
|
||||
return a.compareTo(b) > 0 ? b : a;
|
||||
return a.compareToStrict(b) > 0 ? b : a;
|
||||
}
|
||||
|
||||
protected static <T extends Comparable<T>> T nullSafeMax(T a, T b)
|
||||
protected static PrimaryKey nullSafeMax(PrimaryKey a, PrimaryKey b)
|
||||
{
|
||||
if (a == null) return b;
|
||||
if (b == null) return a;
|
||||
|
||||
return a.compareTo(b) > 0 ? a : b;
|
||||
return a.compareToStrict(b) > 0 ? a : b;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ package org.apache.cassandra.index.sai.utils;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.cassandra.db.BufferDecoratedKey;
|
||||
|
|
@ -59,6 +58,18 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
|
|||
{
|
||||
this.hasClustering = hasClustering;
|
||||
}
|
||||
|
||||
public boolean isIntersectable(Kind other)
|
||||
{
|
||||
if (this == TOKEN)
|
||||
return other == TOKEN;
|
||||
else if (this == SKINNY)
|
||||
return other == SKINNY;
|
||||
else if (this == WIDE || this == STATIC)
|
||||
return other == WIDE || other == STATIC;
|
||||
|
||||
throw new AssertionError("Unknown Kind: " + other);
|
||||
}
|
||||
}
|
||||
|
||||
class Factory
|
||||
|
|
@ -315,6 +326,15 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareToStrict(PrimaryKey o)
|
||||
{
|
||||
int cmp = compareTo(o);
|
||||
// Always order this STATIC key before a WIDE key in the same partition, as this corresponds to the
|
||||
// order of the corresponding row IDs in an on-disk postings list.
|
||||
return o.kind() == Kind.WIDE && cmp == 0 ? -1 : cmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
|
|
@ -326,6 +346,12 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
|
|||
{
|
||||
return String.format("PrimaryKey: { token: %s, partition: %s, clustering: STATIC } ", token(), partitionKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimaryKey toStatic()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class WidePrimaryKey extends SkinnyPrimaryKey
|
||||
|
|
@ -376,6 +402,15 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
|
|||
return clusteringComparator.compare(clustering(), o.clustering());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareToStrict(PrimaryKey o)
|
||||
{
|
||||
int cmp = compareTo(o);
|
||||
// Always order this WIDE key before a STATIC key in the same partition, as this corresponds to the
|
||||
// order of the corresponding row IDs in an on-disk postings list.
|
||||
return o.kind() == Kind.STATIC && cmp == 0 ? 1 : cmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
|
|
@ -393,6 +428,12 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
|
|||
.map(ByteBufferUtil::bytesToHex)
|
||||
.collect(Collectors.joining(", ")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrimaryKey toStatic()
|
||||
{
|
||||
return new StaticPrimaryKey(partitionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -438,4 +479,14 @@ public interface PrimaryKey extends Comparable<PrimaryKey>, ByteComparable
|
|||
* @throws UnsupportedOperationException for {@link PrimaryKey} implementations that are not byte-comparable
|
||||
*/
|
||||
ByteSource asComparableBytes(ByteComparable.Version version);
|
||||
|
||||
default PrimaryKey toStatic()
|
||||
{
|
||||
throw new UnsupportedOperationException("Only STATIC and WIDE keys can be converted to STATIC");
|
||||
}
|
||||
|
||||
default int compareToStrict(PrimaryKey o)
|
||||
{
|
||||
return compareTo(o);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,227 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import org.apache.cassandra.index.sai.utils.SAIRandomizedTester;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RandomIntersectionTest extends SAIRandomizedTester
|
||||
{
|
||||
private static final Object[][] EMPTY_ROWS = new Object[][]{};
|
||||
|
||||
@Parameterized.Parameter
|
||||
public String testName;
|
||||
|
||||
@Parameterized.Parameter(1)
|
||||
public boolean partitionRestricted;
|
||||
|
||||
@Parameterized.Parameter(2)
|
||||
public boolean largePartition;
|
||||
|
||||
@Parameterized.Parameter(3)
|
||||
public boolean v1Cardinality;
|
||||
|
||||
@Parameterized.Parameter(4)
|
||||
public boolean v2Cardinality;
|
||||
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[]{ "Large partition restricted high high", true, true, true, true });
|
||||
parameters.add(new Object[]{ "Large partition restricted low low", true, true, false, false });
|
||||
parameters.add(new Object[]{ "Large partition restricted high low", true, true, true, false });
|
||||
parameters.add(new Object[]{ "Large partition unrestricted high high", false, true, true, true });
|
||||
parameters.add(new Object[]{ "Large partition unrestricted low low", false, true, false, false });
|
||||
parameters.add(new Object[]{ "Large partition unrestricted high low", false, true, true, false });
|
||||
parameters.add(new Object[]{ "Small partition restricted high high", true, false, true, true });
|
||||
parameters.add(new Object[]{ "Small partition restricted low low", true, false, false, false });
|
||||
parameters.add(new Object[]{ "Small partition restricted high low", true, false, true, false });
|
||||
parameters.add(new Object[]{ "Small partition unrestricted high high", false, false, true, true });
|
||||
parameters.add(new Object[]{ "Small partition unrestricted low low", false, false, false, false });
|
||||
parameters.add(new Object[]{ "Small partition unrestricted high low", false, false, true, false });
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private int numRows;
|
||||
|
||||
@Before
|
||||
public void createTableAndIndexes()
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int, ck int, v1 int, v2 int, PRIMARY KEY(pk, ck))");
|
||||
createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(v2) USING 'sai'");
|
||||
|
||||
numRows = nextInt(50000, 200000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomIntersectionTest() throws Throwable
|
||||
{
|
||||
if (partitionRestricted)
|
||||
runRestrictedQueries();
|
||||
else
|
||||
runUnrestrictedQueries();
|
||||
}
|
||||
|
||||
private void runRestrictedQueries() throws Throwable
|
||||
{
|
||||
Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
|
||||
|
||||
beforeAndAfterFlush(() -> {
|
||||
for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
|
||||
{
|
||||
int pk = testRowMap.keySet().stream().skip(nextInt(0, testRowMap.size())).findFirst().orElseThrow();
|
||||
int v1 = nextV1();
|
||||
int v2 = nextV2();
|
||||
|
||||
List<Object[]> expected = testRowMap.get(pk)
|
||||
.stream()
|
||||
.sorted(Comparator.comparingInt(o -> o.ck))
|
||||
.filter(row -> row.v1 > v1 && row.v2 > v2)
|
||||
.map(row -> row(row.ck))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertRows(execute("SELECT ck FROM %s WHERE pk = ? AND v1 > ? AND v2 > ?", pk, v1, v2), expected.toArray(EMPTY_ROWS));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void runUnrestrictedQueries() throws Throwable
|
||||
{
|
||||
Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
|
||||
|
||||
beforeAndAfterFlush(() -> {
|
||||
for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
|
||||
{
|
||||
int v1 = nextV1();
|
||||
int v2 = nextV2();
|
||||
|
||||
List<Object[]> expected = testRowMap.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.filter(row -> row.v1 == v1 && row.v2 == v2)
|
||||
.map(row -> row(row.ck))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertRowsIgnoringOrder(execute("SELECT ck FROM %s WHERE v1 = ? AND v2 = ?", v1, v2), expected.toArray(EMPTY_ROWS));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Map<Integer, List<TestRow>> buildAndLoadTestRows()
|
||||
{
|
||||
Map<Integer, List<TestRow>> testRowMap = new HashMap<>();
|
||||
|
||||
int clusterSize = largePartition ? nextInt(500, 5000) : nextInt(10, 100);
|
||||
int partition = nextInt(0, numRows);
|
||||
List<TestRow> rowList = new ArrayList<>(clusterSize);
|
||||
testRowMap.put(partition, rowList);
|
||||
int clusterCount = 0;
|
||||
for (int index = 0; index < numRows; index++)
|
||||
{
|
||||
TestRow row = new TestRow(partition, nextInt(10, numRows), nextV1(), nextV2());
|
||||
while (rowList.contains(row))
|
||||
row = new TestRow(partition, nextInt(10, numRows), nextV1(), nextV2());
|
||||
|
||||
rowList.add(row);
|
||||
clusterCount++;
|
||||
if (clusterCount == clusterSize)
|
||||
{
|
||||
clusterCount = 0;
|
||||
clusterSize = largePartition ? nextInt(500, 5000) : nextInt(10, 100);
|
||||
partition = nextInt(0, numRows);
|
||||
while (testRowMap.containsKey(partition))
|
||||
partition = nextInt(0, numRows);
|
||||
rowList = new ArrayList<>(clusterSize);
|
||||
testRowMap.put(partition, rowList);
|
||||
}
|
||||
}
|
||||
testRowMap.values().stream().flatMap(Collection::stream).forEach(row -> execute("INSERT INTO %s (pk, ck, v1, v2) VALUES (?, ?, ?, ?)",
|
||||
row.pk, row.ck, row.v1, row.v2));
|
||||
return testRowMap;
|
||||
}
|
||||
|
||||
private int nextV1()
|
||||
{
|
||||
return v1Cardinality ? nextInt(10, numRows/10) : nextInt(10, numRows/1000);
|
||||
}
|
||||
|
||||
private int nextV2()
|
||||
{
|
||||
return v2Cardinality ? nextInt(10, numRows/10) : nextInt(10, numRows/1000);
|
||||
}
|
||||
|
||||
private static class TestRow implements Comparable<TestRow>
|
||||
{
|
||||
final int pk;
|
||||
final int ck;
|
||||
final int v1;
|
||||
final int v2;
|
||||
|
||||
TestRow(int pk, int ck, int v1, int v2)
|
||||
{
|
||||
this.pk = pk;
|
||||
this.ck = ck;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestRow other)
|
||||
{
|
||||
int cmp = Integer.compare(pk, other.pk);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
return Integer.compare(ck, other.ck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj instanceof TestRow)
|
||||
return compareTo((TestRow) obj) == 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(pk, ck);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +49,26 @@ public class StaticColumnIndexTest extends SAITester
|
|||
execute("INSERT INTO %s(pk, ck, val2) VALUES(?, ?, ?)", 1, 2, 2000);
|
||||
execute("INSERT INTO %s(pk, ck, val1, val2) VALUES(?, ?, ?, ?)", 2, 1, 40, 2000);
|
||||
|
||||
beforeAndAfterFlush(() -> assertRows(execute("SELECT pk, ck, val1, val2 FROM %s WHERE val1 = 20 AND val2 = 2000 ALLOW FILTERING"),
|
||||
beforeAndAfterFlush(() -> assertRows(execute("SELECT pk, ck, val1, val2 FROM %s WHERE val1 = 20 AND val2 = 2000"),
|
||||
row(1, 2, 20, 2000)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staticAndNonStaticRangeIntersection() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int, ck int, v1 int, s1 int static, PRIMARY KEY(pk, ck))");
|
||||
createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(s1) USING 'sai'");
|
||||
|
||||
execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 1, 0);
|
||||
execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 2, 1);
|
||||
execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 3, 2);
|
||||
execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 4, 3);
|
||||
execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 5, 4);
|
||||
execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)", 0, 6, 5);
|
||||
|
||||
execute("INSERT INTO %s (pk, s1) VALUES (?, ?)", 0, 100);
|
||||
|
||||
beforeAndAfterFlush(() -> assertRowCount(execute("SELECT * FROM %s WHERE pk = ? AND v1 > ? AND s1 = ?", 0, 2, 100), 3));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import org.apache.cassandra.config.CassandraRelevantProperties;
|
||||
import org.apache.cassandra.cql3.UntypedResultSet;
|
||||
import org.apache.cassandra.index.sai.utils.SAIRandomizedTester;
|
||||
|
||||
public abstract class RandomIntersectionTester extends SAIRandomizedTester
|
||||
{
|
||||
private static final Object[][] EMPTY_ROWS = new Object[][]{};
|
||||
|
||||
protected enum Mode { REGULAR, STATIC, REGULAR_STATIC, TWO_REGULAR_ONE_STATIC }
|
||||
|
||||
@Parameterized.Parameter
|
||||
public String testName;
|
||||
|
||||
@Parameterized.Parameter(1)
|
||||
public boolean partitionRestricted;
|
||||
|
||||
@Parameterized.Parameter(2)
|
||||
public boolean largePartition;
|
||||
|
||||
@Parameterized.Parameter(3)
|
||||
public boolean v1Cardinality;
|
||||
|
||||
@Parameterized.Parameter(4)
|
||||
public boolean v2Cardinality;
|
||||
|
||||
@Parameterized.Parameter(5)
|
||||
public Mode mode;
|
||||
|
||||
private int numPartitions;
|
||||
|
||||
@Before
|
||||
public void createTableAndIndexes()
|
||||
{
|
||||
CassandraRelevantProperties.SAI_INTERSECTION_CLAUSE_LIMIT.setInt(3);
|
||||
|
||||
createTable("CREATE TABLE %s (pk int, ck int, v1 int, v2 int, s1 int static, s2 int static, PRIMARY KEY(pk, ck))");
|
||||
createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(v2) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(s1) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(s2) USING 'sai'");
|
||||
|
||||
numPartitions = nextInt(15000, 100000);
|
||||
}
|
||||
|
||||
protected void runRestrictedQueries() throws Throwable
|
||||
{
|
||||
Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
|
||||
|
||||
beforeAndAfterFlush(() -> {
|
||||
for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
|
||||
{
|
||||
int pk = testRowMap.keySet().stream().skip(nextInt(0, testRowMap.size())).findFirst().orElseThrow();
|
||||
int v1 = nextV1();
|
||||
int v2 = nextV2();
|
||||
|
||||
Predicate<TestRow> predicate = null;
|
||||
|
||||
if (mode == Mode.REGULAR)
|
||||
predicate = row -> row.v1 > v1 && row.v2 > v2;
|
||||
else if (mode == Mode.STATIC)
|
||||
predicate = row -> row.s1 > v1 && row.s2 > v2;
|
||||
else if (mode == Mode.REGULAR_STATIC)
|
||||
predicate = row -> row.v1 > v1 && row.s2 > v2;
|
||||
else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
|
||||
predicate = row -> row.v1 > v1 && row.v2 > v2 && row.s2 > v2;
|
||||
|
||||
assert predicate != null : "Predicate should be assigned!";
|
||||
|
||||
List<Object[]> expected = testRowMap.get(pk)
|
||||
.stream()
|
||||
.sorted(Comparator.comparingInt(o -> o.ck))
|
||||
.filter(predicate)
|
||||
.map(row -> row(row.pk, row.ck))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
UntypedResultSet result = null;
|
||||
|
||||
if (mode == Mode.REGULAR)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND v1 > ? AND v2 > ?", pk, v1, v2);
|
||||
else if (mode == Mode.STATIC)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND s1 > ? AND s2 > ?", pk, v1, v2);
|
||||
else if (mode == Mode.REGULAR_STATIC)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND v1 > ? AND s2 > ?", pk, v1, v2);
|
||||
else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE pk = ? AND v1 > ? AND v2 > ? AND s2 > ?", pk, v1, v2, v2);
|
||||
|
||||
assertRows(result, expected.toArray(EMPTY_ROWS));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void runUnrestrictedQueries() throws Throwable
|
||||
{
|
||||
Map<Integer, List<TestRow>> testRowMap = buildAndLoadTestRows();
|
||||
|
||||
beforeAndAfterFlush(() -> {
|
||||
for (int queryCount = 0; queryCount < nextInt(10, 100); queryCount++)
|
||||
{
|
||||
int v1 = nextV1();
|
||||
int v2 = nextV2();
|
||||
|
||||
Predicate<TestRow> predicate = null;
|
||||
|
||||
if (mode == Mode.REGULAR)
|
||||
predicate = row -> row.v1 == v1 && row.v2 > v2;
|
||||
else if (mode == Mode.STATIC)
|
||||
predicate = row -> row.s1 > v1 && row.s2 > v2;
|
||||
else if (mode == Mode.REGULAR_STATIC)
|
||||
predicate = row -> row.v1 == v1 && row.s2 > v2;
|
||||
else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
|
||||
predicate = row -> row.v1 == v1 && row.v2 > v2 && row.s2 > v2;
|
||||
|
||||
assert predicate != null : "Predicate should be assigned!";
|
||||
|
||||
List<Object[]> expected = testRowMap.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.filter(predicate)
|
||||
.map(row -> row(row.pk, row.ck))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
UntypedResultSet result = null;
|
||||
|
||||
if (mode == Mode.REGULAR)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE v1 = ? AND v2 > ?", v1, v2);
|
||||
else if (mode == Mode.STATIC)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE s1 > ? AND s2 > ?", v1, v2);
|
||||
else if (mode == Mode.REGULAR_STATIC)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE v1 = ? AND s2 > ?", v1, v2);
|
||||
else if (mode == Mode.TWO_REGULAR_ONE_STATIC)
|
||||
result = execute("SELECT pk, ck FROM %s WHERE v1 = ? AND v2 > ? AND s2 > ?", v1, v2, v2);
|
||||
|
||||
assertRowsIgnoringOrder(result, expected.toArray(EMPTY_ROWS));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Map<Integer, List<TestRow>> buildAndLoadTestRows()
|
||||
{
|
||||
Map<Integer, List<TestRow>> testRowMap = new HashMap<>();
|
||||
|
||||
int clusterSize = nextPartitionSize();
|
||||
int partition = nextInt(0, numPartitions);
|
||||
int s1 = nextV1();
|
||||
int s2 = nextV2();
|
||||
List<TestRow> rowList = new ArrayList<>(clusterSize);
|
||||
testRowMap.put(partition, rowList);
|
||||
int clusterCount = 0;
|
||||
|
||||
for (int index = 0; index < numPartitions; index++)
|
||||
{
|
||||
TestRow row = new TestRow(partition, nextInt(10, numPartitions), nextV1(), nextV2(), s1, s2);
|
||||
while (rowList.contains(row))
|
||||
row = new TestRow(partition, nextInt(10, numPartitions), nextV1(), nextV2(), s1, s2);
|
||||
|
||||
rowList.add(row);
|
||||
clusterCount++;
|
||||
|
||||
if (clusterCount == clusterSize)
|
||||
{
|
||||
clusterCount = 0;
|
||||
clusterSize = nextPartitionSize();
|
||||
partition = nextInt(0, numPartitions);
|
||||
while (testRowMap.containsKey(partition))
|
||||
partition = nextInt(0, numPartitions);
|
||||
rowList = new ArrayList<>(clusterSize);
|
||||
testRowMap.put(partition, rowList);
|
||||
}
|
||||
}
|
||||
|
||||
testRowMap.values().stream().flatMap(Collection::stream).forEach(row -> {
|
||||
execute("INSERT INTO %s (pk, ck, v1, v2) VALUES (?, ?, ?, ?)", row.pk, row.ck, row.v1, row.v2);
|
||||
execute("INSERT INTO %s (pk, s1, s2) VALUES (?, ?, ?)", row.pk, row.s1, row.s2);
|
||||
});
|
||||
|
||||
return testRowMap;
|
||||
}
|
||||
|
||||
private int nextPartitionSize()
|
||||
{
|
||||
return largePartition ? nextInt(1024, 4096) : nextInt(1, 64);
|
||||
}
|
||||
|
||||
private int nextV1()
|
||||
{
|
||||
return v1Cardinality ? nextInt(10, numPartitions / 10) : nextInt(10, numPartitions / 1000);
|
||||
}
|
||||
|
||||
private int nextV2()
|
||||
{
|
||||
return v2Cardinality ? nextInt(10, numPartitions / 10) : nextInt(10, numPartitions / 1000);
|
||||
}
|
||||
|
||||
private static class TestRow implements Comparable<TestRow>
|
||||
{
|
||||
final int pk;
|
||||
final int ck;
|
||||
final int v1;
|
||||
final int v2;
|
||||
final int s1;
|
||||
final int s2;
|
||||
|
||||
TestRow(int pk, int ck, int v1, int v2, int s1, int s2)
|
||||
{
|
||||
this.pk = pk;
|
||||
this.ck = ck;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
this.s1 = s1;
|
||||
this.s2 = s2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestRow other)
|
||||
{
|
||||
int cmp = Integer.compare(pk, other.pk);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
return Integer.compare(ck, other.ck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj instanceof TestRow)
|
||||
return compareTo((TestRow) obj) == 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(pk, ck);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR_STATIC;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RandomMixedIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, REGULAR_STATIC});
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedIntersection() throws Throwable
|
||||
{
|
||||
runUnrestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR_STATIC;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RandomMixedPartitionIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, REGULAR_STATIC});
|
||||
parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, REGULAR_STATIC});
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedIntersection() throws Throwable
|
||||
{
|
||||
runRestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RandomRegularIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, REGULAR });
|
||||
parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, REGULAR });
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, REGULAR });
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, REGULAR });
|
||||
parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, REGULAR });
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, REGULAR });
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegularIntersection() throws Throwable
|
||||
{
|
||||
runUnrestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.REGULAR;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RandomRegularPartitionIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, REGULAR });
|
||||
parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, REGULAR });
|
||||
parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, REGULAR });
|
||||
parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, REGULAR });
|
||||
parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, REGULAR });
|
||||
parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, REGULAR });
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegularIntersection() throws Throwable
|
||||
{
|
||||
runRestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.STATIC;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RandomStaticIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, STATIC });
|
||||
parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, STATIC });
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, STATIC });
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, STATIC });
|
||||
parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, STATIC });
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, STATIC });
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticIntersection() throws Throwable
|
||||
{
|
||||
runUnrestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.STATIC;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class RandomStaticPartitionIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, STATIC });
|
||||
parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, STATIC });
|
||||
parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, STATIC });
|
||||
parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, STATIC });
|
||||
parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, STATIC });
|
||||
parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, STATIC });
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticIntersection() throws Throwable
|
||||
{
|
||||
runRestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.TWO_REGULAR_ONE_STATIC;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class TwoRegularOneStaticIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, high", false, true, true, true, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Large partition unrestricted, low, low", false, true, false, false, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Large partition unrestricted, high, low", false, true, true, false, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, high", false, false, true, true, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Small partition unrestricted, low, low", false, false, false, false, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Small partition unrestricted, high, low", false, false, true, false, TWO_REGULAR_ONE_STATIC});
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedIntersection() throws Throwable
|
||||
{
|
||||
runUnrestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cassandra.index.sai.cql.intersection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.cassandra.index.sai.cql.intersection.RandomIntersectionTester.Mode.TWO_REGULAR_ONE_STATIC;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class TwoRegularOneStaticPartitionIntersectionTest extends RandomIntersectionTester
|
||||
{
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static List<Object[]> parameters()
|
||||
{
|
||||
List<Object[]> parameters = new LinkedList<>();
|
||||
|
||||
parameters.add(new Object[] { "Large partition restricted, high, high", true, true, true, true, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Large partition restricted, low, low", true, true, false, false, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Large partition restricted, high, low", true, true, true, false, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Small partition restricted, high, high", true, false, true, true, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Small partition restricted, low, low", true, false, false, false, TWO_REGULAR_ONE_STATIC});
|
||||
parameters.add(new Object[] { "Small partition restricted, high, low", true, false, true, false, TWO_REGULAR_ONE_STATIC});
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedIntersection() throws Throwable
|
||||
{
|
||||
runRestrictedQueries();
|
||||
}
|
||||
}
|
||||
|
|
@ -83,8 +83,7 @@ public class PrimaryKeyTest extends AbstractPrimaryKeyTester
|
|||
public void simplePartitonStaticAndSingleClusteringAscTest()
|
||||
{
|
||||
PrimaryKey.Factory factory = new PrimaryKey.Factory(Murmur3Partitioner.instance, simplePartitionStaticAndSingleClusteringAsc.comparator);
|
||||
// int rows = nextInt(10, 100);
|
||||
int rows = 10;
|
||||
int rows = nextInt(10, 100);
|
||||
PrimaryKey[] keys = new PrimaryKey[rows];
|
||||
int partition = 0;
|
||||
int clustering = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue