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:
Caleb Rackliffe 2024-01-19 04:39:02 -06:00
parent c00ebc8d2d
commit 87f2a7b763
18 changed files with 897 additions and 304 deletions

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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)))

View File

@ -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.
*/

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;