From ad642d5e25201dd6dba937942025b61884f53447 Mon Sep 17 00:00:00 2001 From: Stefan Miklosovic Date: Tue, 2 Jan 2024 13:00:33 +0100 Subject: [PATCH] Creating a SASI index after creating an SAI index does not break secondary index queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit patch by Stefan Miklosovic; reviewed by Caleb Rackliffe, Andres de la Peña for CASSANDRA-18939 --- CHANGES.txt | 1 + .../cassandra/index/SecondaryIndexManager.java | 4 ++-- .../cassandra/index/sai/plan/QueryController.java | 7 ++++--- .../index/sai/plan/VectorTopKProcessor.java | 5 +---- .../cassandra/index/sasi/plan/QueryController.java | 8 ++++---- .../cassandra/index/SecondaryIndexManagerTest.java | 12 ++++++++++++ 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 31e29059e1..fa6b00a1d7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0-beta2 + * Creating a SASI index after creating an SAI index does not break secondary index queries (CASSANDRA-18939) * Optionally fail when a non-partition-restricted query is issued against an index (CASSANDRA-18796) * Add a startup check to fail startup when using invalid configuration with certain Kernel and FS type (CASSANDRA-19196) * UCS min_sstable_size should not be lower than target_sstable_size lower bound (CASSANDRA-19112) diff --git a/src/java/org/apache/cassandra/index/SecondaryIndexManager.java b/src/java/org/apache/cassandra/index/SecondaryIndexManager.java index 9fd24bcec3..cc2a8182af 100644 --- a/src/java/org/apache/cassandra/index/SecondaryIndexManager.java +++ b/src/java/org/apache/cassandra/index/SecondaryIndexManager.java @@ -1277,13 +1277,13 @@ public class SecondaryIndexManager implements IndexRegistry, INotificationConsum return indexes.values().stream().filter((i) -> i.supportsExpression(expression.column(), expression.operator())).findFirst(); } - public Set getBestIndexFor(RowFilter.Expression expression, Class indexType) + public Optional getBestIndexFor(RowFilter.Expression expression, Class indexType) { return indexes.values() .stream() .filter(i -> indexType.isInstance(i) && i.supportsExpression(expression.column(), expression.operator())) .map(indexType::cast) - .collect(Collectors.toSet()); + .findFirst(); } /** diff --git a/src/java/org/apache/cassandra/index/sai/plan/QueryController.java b/src/java/org/apache/cassandra/index/sai/plan/QueryController.java index e97ab91cea..597e339aaa 100644 --- a/src/java/org/apache/cassandra/index/sai/plan/QueryController.java +++ b/src/java/org/apache/cassandra/index/sai/plan/QueryController.java @@ -23,9 +23,10 @@ import java.io.UncheckedIOException; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; +import javax.annotation.Nullable; + import com.google.common.collect.Lists; import org.apache.cassandra.cql3.Operator; @@ -135,10 +136,10 @@ public class QueryController return ranges; } + @Nullable public StorageAttachedIndex indexFor(RowFilter.Expression expression) { - Set indexes = cfs.indexManager.getBestIndexFor(expression, StorageAttachedIndex.class); - return indexes.isEmpty() ? null : indexes.iterator().next(); + return cfs.indexManager.getBestIndexFor(expression, StorageAttachedIndex.class).orElse(null); } public boolean hasAnalyzer(RowFilter.Expression expression) diff --git a/src/java/org/apache/cassandra/index/sai/plan/VectorTopKProcessor.java b/src/java/org/apache/cassandra/index/sai/plan/VectorTopKProcessor.java index fb22b949f2..b3a34356f2 100644 --- a/src/java/org/apache/cassandra/index/sai/plan/VectorTopKProcessor.java +++ b/src/java/org/apache/cassandra/index/sai/plan/VectorTopKProcessor.java @@ -20,7 +20,6 @@ package org.apache.cassandra.index.sai.plan; import java.nio.ByteBuffer; import java.util.Comparator; -import java.util.Optional; import java.util.PriorityQueue; import java.util.TreeMap; import java.util.TreeSet; @@ -41,7 +40,6 @@ import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator; import org.apache.cassandra.db.rows.BaseRowIterator; import org.apache.cassandra.db.rows.Row; import org.apache.cassandra.db.rows.Unfiltered; -import org.apache.cassandra.index.Index; import org.apache.cassandra.index.SecondaryIndexManager; import org.apache.cassandra.index.sai.StorageAttachedIndex; import org.apache.cassandra.index.sai.utils.InMemoryPartitionIterator; @@ -187,7 +185,6 @@ public class VectorTopKProcessor if (e.operator() != Operator.ANN) return null; - Optional index = sim.getBestIndexFor(e); - return (StorageAttachedIndex) index.filter(i -> i instanceof StorageAttachedIndex).orElse(null); + return sim.getBestIndexFor(e, StorageAttachedIndex.class).orElse(null); } } diff --git a/src/java/org/apache/cassandra/index/sasi/plan/QueryController.java b/src/java/org/apache/cassandra/index/sasi/plan/QueryController.java index 78fecd8f31..15865570ce 100644 --- a/src/java/org/apache/cassandra/index/sasi/plan/QueryController.java +++ b/src/java/org/apache/cassandra/index/sasi/plan/QueryController.java @@ -20,6 +20,8 @@ package org.apache.cassandra.index.sasi.plan; import java.util.*; import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + import com.google.common.collect.Sets; import org.apache.cassandra.db.ColumnFamilyStore; @@ -32,7 +34,6 @@ import org.apache.cassandra.db.filter.DataLimits; import org.apache.cassandra.db.filter.RowFilter; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.rows.UnfilteredRowIterator; -import org.apache.cassandra.index.Index; import org.apache.cassandra.index.sasi.SASIIndex; import org.apache.cassandra.index.sasi.SSTableIndex; import org.apache.cassandra.index.sasi.TermIterator; @@ -90,13 +91,12 @@ public class QueryController return cfs.metadata().partitionKeyType; } + @Nullable public ColumnIndex getIndex(RowFilter.Expression expression) { - Optional index = cfs.indexManager.getBestIndexFor(expression); - return index.isPresent() ? ((SASIIndex) index.get()).getIndex() : null; + return cfs.indexManager.getBestIndexFor(expression, SASIIndex.class).map(SASIIndex::getIndex).orElse(null); } - public UnfilteredRowIterator getPartition(DecoratedKey key, ReadExecutionController executionController) { if (key == null) diff --git a/test/unit/org/apache/cassandra/index/SecondaryIndexManagerTest.java b/test/unit/org/apache/cassandra/index/SecondaryIndexManagerTest.java index d63d63840c..f8e272b4ec 100644 --- a/test/unit/org/apache/cassandra/index/SecondaryIndexManagerTest.java +++ b/test/unit/org/apache/cassandra/index/SecondaryIndexManagerTest.java @@ -56,6 +56,18 @@ public class SecondaryIndexManagerTest extends CQLTester TestingIndex.clear(); } + @Test + public void createSasiAfterSai() + { + createTable("CREATE TABLE %s (id int PRIMARY KEY, val text)"); + createIndex("CREATE INDEX idx0 ON %s (val) USING 'sai'"); + execute("INSERT INTO %s (id, val) VALUES (1, 'a')"); + execute("SELECT * FROM %s WHERE val = 'a'"); + flush(); + createIndex("CREATE CUSTOM INDEX idx1 ON %s (val) USING 'org.apache.cassandra.index.sasi.SASIIndex'"); + execute("SELECT * FROM %s WHERE val = 'a'"); + } + @Test public void creatingIndexMarksTheIndexAsBuilt() {