Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,11 @@ else if (analyzerFactory.isNGram())
}
else if (type.isVector())
{
if (!Version.current(metadata.keyspace).onOrAfter(Version.JVECTOR_EARLIEST))
{
throw new InvalidRequestException(vectorUnsupportedByVersionError(Version.current(metadata.keyspace)));
}

if (type.valueLengthIfFixed() == 4 && config.getSimilarityFunction() == VectorSimilarityFunction.COSINE)
throw new InvalidRequestException("Cosine similarity is not supported for single-dimension vectors");

Expand Down Expand Up @@ -450,12 +455,7 @@ private Future<?> startInitialBuild(ColumnFamilyStore baseCfs, boolean validate,

if (indexContext.isVector() && indexContext.version().compareTo(Version.JVECTOR_EARLIEST) < 0)
{
throw new FeatureNeedsIndexRebuildException(String.format("The current configured on-disk format version %s does not support vector indexes. " +
"The minimum version that supports vectors is %s. " +
"The on-disk format version can be set via the -D%s system property.",
indexContext.version(),
Version.JVECTOR_EARLIEST,
CassandraRelevantProperties.SAI_CURRENT_VERSION.name()));
throw new FeatureNeedsIndexRebuildException(vectorUnsupportedByVersionError(indexContext.version()));
}

// stop in-progress compaction tasks to prevent compacted sstables not being indexed.
Expand Down Expand Up @@ -500,6 +500,17 @@ private Future<?> startInitialBuild(ColumnFamilyStore baseCfs, boolean validate,
return Futures.allAsList(futures);
}

@VisibleForTesting
public static String vectorUnsupportedByVersionError(Version currentVersion)
{
return String.format("The current configured on-disk format version %s does not support vector indexes. " +
"The minimum version that supports vectors is %s. " +
"The on-disk format version can be set via the -D%s system property.",
currentVersion,
Version.JVECTOR_EARLIEST,
CassandraRelevantProperties.SAI_CURRENT_VERSION.name());
}

/**
* Splits SSTables into groups of similar overall size.
*
Expand Down
14 changes: 12 additions & 2 deletions test/unit/org/apache/cassandra/index/sai/SAITester.java
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,12 @@ protected long getDiskUsage()

protected void verifyNoIndexFiles()
{
assertTrue(indexFiles().size() == 0);
verifyNoIndexFiles(KEYSPACE, currentTable());
}

protected void verifyNoIndexFiles(String keyspace, String table)
{
assertTrue(indexFiles(keyspace, table).isEmpty());
}

// Verify every sstables is indexed correctly and the components are valid.
Expand Down Expand Up @@ -656,7 +661,12 @@ protected boolean isBuildCompletionMarker(IndexComponentType indexComponentType)

protected Set<File> indexFiles()
{
ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore(currentTable());
return indexFiles(KEYSPACE, currentTable());
}

protected Set<File> indexFiles(String keyspace, String table)
{
ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore(table);
return cfs.getDirectories().getCFDirectories()
.stream()
.flatMap(dir -> Arrays.stream(dir.tryList()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@

package org.apache.cassandra.index.sai.cql;

import java.util.Collection;
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.cql3.UntypedResultSet;
import org.apache.cassandra.exceptions.ReadFailureException;
import org.apache.cassandra.exceptions.RequestFailureReason;
import org.apache.cassandra.index.sai.SAIUtil;
import org.apache.cassandra.index.sai.StorageAttachedIndex;
import org.apache.cassandra.index.sai.disk.format.Version;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Collection;
import java.util.stream.Collectors;

import static org.assertj.core.api.Assertions.assertThat;

Expand Down Expand Up @@ -76,24 +76,25 @@ public void testANNSupport()
createIndex("CREATE CUSTOM INDEX ON %s(x) USING 'StorageAttachedIndex'");

// vector index creation will be rejected in older versions
String idx = createIndexAsync("CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex'");
waitForIndexBuilds(idx);
boolean isIndexQueryable = isIndexQueryable(keyspace(), idx);
String createIndexQuery = "CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex'";
if (version.onOrAfter(Version.JVECTOR_EARLIEST))
{
assertThat(isIndexQueryable).isTrue();
String idx = createIndex(createIndexQuery);
assertThat(isIndexQueryable(keyspace(), idx)).isTrue();

// the index is marked as queryable, so we should be able to query it
assertRows(execute("SELECT k FROM %s ORDER BY v ANN OF [2.5, 3.5, 4.5] LIMIT 3"), row(2), row(1), row(3));
assertRows(execute("SELECT k FROM %s ORDER BY v ANN OF [2.5, 3.5, 4.5] LIMIT 3 WITH ann_options = {'rerank_k':3}"), row(2), row(1), row(3));

dropIndex("DROP INDEX %s." + idx);
}
else
{
assertThat(isIndexQueryable).isFalse();
Assertions.assertThatThrownBy(() -> createIndex(createIndexQuery))
.hasMessageContaining(StorageAttachedIndex.vectorUnsupportedByVersionError(version));
}

// vector index creation will be accepted on newer versions, even if there is still another index in the older version
dropIndex("DROP INDEX %s." + idx);
SAIUtil.setCurrentVersion(Version.LATEST);
createIndex("CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex'");

Expand Down Expand Up @@ -124,24 +125,25 @@ public void testGeoDistance()
createIndex("CREATE CUSTOM INDEX ON %s(x) USING 'StorageAttachedIndex'");

// vector index creation will be rejected in older versions
String idx = createIndexAsync("CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex' WITH OPTIONS = {'similarity_function' : 'euclidean'}");
waitForIndexBuilds(idx);
boolean isIndexQueryable = isIndexQueryable(keyspace(), idx);
String createIndexQuery = "CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex' WITH OPTIONS = {'similarity_function' : 'euclidean'}";
if (version.onOrAfter(Version.JVECTOR_EARLIEST))
{
assertThat(isIndexQueryable).isTrue();
String idx = createIndex(createIndexQuery);
assertThat(isIndexQueryable(keyspace(), idx)).isTrue();

// the index is marked as queryable, so we should be able to query it
assertRowsIgnoringOrder(execute("SELECT k FROM %s WHERE GEO_DISTANCE(v, [5,5]) < 157000"), row(2), row(3));
assertRowsIgnoringOrder(execute("SELECT k FROM %s WHERE GEO_DISTANCE(v, [5,5]) < 157011"), row(1), row(2), row(3));

dropIndex("DROP INDEX %s." + idx);
}
else
{
assertThat(isIndexQueryable).isFalse();
Assertions.assertThatThrownBy(() -> createIndex(createIndexQuery))
.hasMessageContaining(StorageAttachedIndex.vectorUnsupportedByVersionError(version));
}

// vector index creation will be accepted on newer versions, even if there is still an index in the older version
dropIndex("DROP INDEX %s." + idx);
SAIUtil.setCurrentVersion(Version.LATEST);
createIndex("CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex' WITH OPTIONS = {'similarity_function' : 'euclidean'}");

Expand Down
Loading
Loading