Skip to content
Open
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
3 changes: 1 addition & 2 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
5.0.7
* Add blob type support to SAI (CASSANDRA-20012)
* Refactor SAI ANN query execution to use score ordered iterators for correctness and speed (CASSANDRA-20086)
* Disallow binding an identity to a superuser when the user is a regular user (CASSANDRA-21219)
* Fix ConcurrentModificationException in compaction garbagecollect (CASSANDRA-21065)
Expand Down Expand Up @@ -8273,5 +8274,3 @@ Full list of issues resolved in 0.4 is at https://issues.apache.org/jira/secure/
* Combined blocking and non-blocking versions of insert APIs
* Added FlushPeriodInMinutes configuration parameter to force
flushing of infrequently-updated ColumnFamilies


4 changes: 4 additions & 0 deletions conf/cassandra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,10 @@ drop_compact_storage_enabled: false
# sai_string_term_size_warn_threshold: 1KiB
# sai_string_term_size_fail_threshold: 8KiB

# Guardrail specifying warn/fail thresholds for the size of blob terms written to an SAI index
# sai_blob_term_size_warn_threshold: 1KiB
# sai_blob_term_size_fail_threshold: 8KiB

# Guardrail specifying warn/fail thresholds for the size of frozen terms written to an SAI index
# sai_frozen_term_size_warn_threshold: 1KiB
# sai_frozen_term_size_fail_threshold: 8KiB
Expand Down
16 changes: 16 additions & 0 deletions conf/cassandra_latest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2185,6 +2185,22 @@ drop_compact_storage_enabled: false
# before emitting a failure (defaults to -1 to disable)
#sai_sstable_indexes_per_query_fail_threshold: -1

# Guardrail specifying warn/fail thresholds for the size of string terms written to an SAI index
# sai_string_term_size_warn_threshold: 1KiB
# sai_string_term_size_fail_threshold: 8KiB

# Guardrail specifying warn/fail thresholds for the size of blob terms written to an SAI index
# sai_blob_term_size_warn_threshold: 1KiB
# sai_blob_term_size_fail_threshold: 8KiB

# Guardrail specifying warn/fail thresholds for the size of frozen terms written to an SAI index
# sai_frozen_term_size_warn_threshold: 1KiB
# sai_frozen_term_size_fail_threshold: 8KiB

# Guardrail specifying warn/fail thresholds for the size of vector terms written to an SAI index
# sai_vector_term_size_warn_threshold: 16KiB
# sai_vector_term_size_fail_threshold: 32KiB

# The default secondary index implementation when CREATE INDEX does not specify one via USING.
# ex. "legacy_local_table" - (default) legacy secondary index, implemented as a hidden table
# ex. "sai" - "storage-attched" index, implemented via optimized SSTable/Memtable-attached indexes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ SAI adds column-level indexes to any CQL table column of almost any CQL data typ
SAI enables queries that filter based on:

* vector embeddings
* AND/OR logic for numeric and text types
* IN logic (use an array of values) for numeric and text types
* AND/OR logic for numeric, text, and blob types
* IN logic (use an array of values) for numeric, text, and blob types
* numeric range
* non-variable length numeric types
* text type equality
* text and blob type equality
* CONTAINs logic (for collections)
* tokenized data
* row-aware query path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,11 @@ See xref:developing/cql/indexing/sai/sai-read-write-paths.adoc[SAI write path an

SAI supports two on-disk index formats, optimized for:

* Equality and non-exact matching on *strings*.
** Strings are indexed on-disk using the https://en.wikipedia.org/wiki/Trie[trie] data structure, in conjunction with postings (term/row pairs) lists.
The trie is heap friendly, providing string prefix compression for terms, and can match any query that can be expressed as a deterministic finite automaton.
* Equality and non-exact matching on *strings, blobs, and booleans*.
** These literal types are indexed on-disk using the https://en.wikipedia.org/wiki/Trie[trie] data structure, in conjunction with postings (term/row pairs) lists.
The trie is heap friendly, providing prefix compression for terms, and can match any query that can be expressed as a deterministic finite automaton.
The feature minimizes on-disk footprint and supports simple token skipping.
Note that `BLOB` and `BOOLEAN` types support equality queries only.

* Equality and range queries on *numeric and non-literal types*.
** Numeric values and the other non-literal CQL types (`timestamp`, `date`, `UUID`) are indexed on-disk using https://en.wikipedia.org/wiki/K-d_tree[k-dimensional tree], a balanced structure that provides fast lookups across one or more dimensions, and compression for both values and postings.
Expand All @@ -156,12 +157,11 @@ The SAI disk usage is largely dependent on the underlying data model and the num

== What are the supported column data types for SAI indexing?

The supported types are: `ASCII, BIGINT, DATE, DECIMAL, DOUBLE, FLOAT, INET, INT, SMALLINT, TEXT, TIME, TIMESTAMP, TIMEUUID, TINYINT, UUID, VARCHAR, VARINT`.
The supported types are: `ASCII, BIGINT, BLOB, BOOLEAN, DATE, DECIMAL, DOUBLE, FLOAT, INET, INT, SMALLINT, TEXT, TIME, TIMESTAMP, TIMEUUID, TINYINT, UUID, VARCHAR, VARINT`.

[NOTE]
====
* `INET` support for IPv4 and IPv6 was new starting with Cassandra ???.
* The `DECIMAL` and `VARINT` support was new starting with Cassandra ???.
* The `BLOB` support was new starting with Cassandra 5.0.7.
* SAI also supports collections -- see the xref:#saiCollectionColumnFaq[next FAQ].
====

Expand Down Expand Up @@ -348,4 +348,4 @@ SAI returns `InvalidRequestException` if you try to define an index on a column

== What partitioner does SAI support?

SAI supports only the `Murmur3Partitioner`.
SAI supports only the `Murmur3Partitioner`.
2 changes: 2 additions & 0 deletions src/java/org/apache/cassandra/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,8 @@ public static void setClientMode(boolean clientMode)
public volatile int sai_sstable_indexes_per_query_fail_threshold = -1;
public volatile DataStorageSpec.LongBytesBound sai_string_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("1KiB");
public volatile DataStorageSpec.LongBytesBound sai_string_term_size_fail_threshold = new DataStorageSpec.LongBytesBound("8KiB");
public volatile DataStorageSpec.LongBytesBound sai_blob_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("1KiB");
public volatile DataStorageSpec.LongBytesBound sai_blob_term_size_fail_threshold = new DataStorageSpec.LongBytesBound("8KiB");
public volatile DataStorageSpec.LongBytesBound sai_frozen_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("1KiB");
public volatile DataStorageSpec.LongBytesBound sai_frozen_term_size_fail_threshold = new DataStorageSpec.LongBytesBound("8KiB");
public volatile DataStorageSpec.LongBytesBound sai_vector_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("16KiB");
Expand Down
28 changes: 28 additions & 0 deletions src/java/org/apache/cassandra/config/GuardrailsOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,34 @@ public void setSaiStringTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBou
x -> config.sai_string_term_size_fail_threshold = x);
}

@Override
@Nullable
public DataStorageSpec.LongBytesBound getSaiBlobTermSizeWarnThreshold()
{
return config.sai_blob_term_size_warn_threshold;
}

@Override
@Nullable
public DataStorageSpec.LongBytesBound getSaiBlobTermSizeFailThreshold()
{
return config.sai_blob_term_size_fail_threshold;
}

@Override
public void setSaiBlobTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBound warn, @Nullable DataStorageSpec.LongBytesBound fail)
{
validateSizeThreshold(warn, fail, false, "sai_blob_term_size");
updatePropertyWithLogging("sai_blob_term_size_warn_threshold",
warn,
() -> config.sai_blob_term_size_warn_threshold,
x -> config.sai_blob_term_size_warn_threshold = x);
updatePropertyWithLogging("sai_blob_term_size_fail_threshold",
fail,
() -> config.sai_blob_term_size_fail_threshold,
x -> config.sai_blob_term_size_fail_threshold = x);
}

@Override
@Nullable
public DataStorageSpec.LongBytesBound getSaiFrozenTermSizeWarnThreshold()
Expand Down
32 changes: 32 additions & 0 deletions src/java/org/apache/cassandra/db/guardrails/Guardrails.java
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,18 @@ public final class Guardrails implements GuardrailsMBean
format("Value of column '%s' has size %s, this exceeds the %s threshold of %s.",
what, value, isWarning ? "warning" : "failure", threshold));

/**
* Guardrail on the size of a blob term written to SAI index.
*/
public static final MaxThreshold saiBlobTermSize =
new MaxThreshold("sai_blob_term_size",
null,
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getSaiBlobTermSizeWarnThreshold()),
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getSaiBlobTermSizeFailThreshold()),
(isWarning, what, value, threshold) ->
format("Value of column '%s' has size %s, this exceeds the %s threshold of %s.",
what, value, isWarning ? "warning" : "failure", threshold));

/**
* Guardrail on the size of a frozen term written to SAI index.
*/
Expand Down Expand Up @@ -1329,6 +1341,26 @@ public void setSaiStringTermSizeThreshold(@Nullable String warnSize, @Nullable S
DEFAULT_CONFIG.setSaiStringTermSizeThreshold(sizeFromString(warnSize), sizeFromString(failSize));
}

@Override
@Nullable
public String getSaiBlobTermSizeWarnThreshold()
{
return sizeToString(DEFAULT_CONFIG.getSaiBlobTermSizeWarnThreshold());
}

@Override
@Nullable
public String getSaiBlobTermSizeFailThreshold()
{
return sizeToString(DEFAULT_CONFIG.getSaiBlobTermSizeFailThreshold());
}

@Override
public void setSaiBlobTermSizeThreshold(@Nullable String warnSize, @Nullable String failSize)
{
DEFAULT_CONFIG.setSaiBlobTermSizeThreshold(sizeFromString(warnSize), sizeFromString(failSize));
}

@Override
@Nullable
public String getSaiFrozenTermSizeWarnThreshold()
Expand Down
18 changes: 18 additions & 0 deletions src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,24 @@ void setMinimumTimestampThreshold(@Nullable DurationSpec.LongMicrosecondsBound w
*/
void setSaiStringTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBound warn, @Nullable DataStorageSpec.LongBytesBound fail);

/**
* @return the warning threshold for the size of blob terms written to an SAI index
*/
DataStorageSpec.LongBytesBound getSaiBlobTermSizeWarnThreshold();

/**
* @return the failure threshold for the size of blob terms written to an SAI index
*/
DataStorageSpec.LongBytesBound getSaiBlobTermSizeFailThreshold();

/**
* Sets warning and failure thresholds for the size of blob terms written to an SAI index
*
* @param warn value to set for warn threshold
* @param fail value to set for fail threshold
*/
void setSaiBlobTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBound warn, @Nullable DataStorageSpec.LongBytesBound fail);

/**
* @return the warning threshold for the size of frozen terms written to an SAI index
*/
Expand Down
24 changes: 24 additions & 0 deletions src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,30 @@ public interface GuardrailsMBean
*/
void setSaiStringTermSizeThreshold(@Nullable String warnSize, @Nullable String failSize);

/**
* @return The warning threshold for blob terms written to an SAI index, as a human-readable string.
* (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) A {@code null} value means disabled.
*/
@Nullable
String getSaiBlobTermSizeWarnThreshold();

/**
* @return The failure threshold for blob terms written to an SAI index, as a human-readable string.
* (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) A {@code null} value means disabled.
*/
@Nullable
String getSaiBlobTermSizeFailThreshold();

/**
* @param warnSize The warning threshold for blob terms written to an SAI index, as a human-readable string.
* (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B})
* A {@code null} value means disabled.
* @param failSize The failure threshold for blob terms written to an SAI index, as a human-readable string.
* (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B})
* A {@code null} value means disabled.
*/
void setSaiBlobTermSizeThreshold(@Nullable String warnSize, @Nullable String failSize);

/**
* @return The warning threshold for frozen terms written to an SAI index, as a human-readable string.
* (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) A {@code null} value means disabled.
Expand Down
15 changes: 10 additions & 5 deletions src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ public class StorageAttachedIndex implements Index
CQL3Type.Native.SMALLINT, CQL3Type.Native.TEXT, CQL3Type.Native.TIME,
CQL3Type.Native.TIMESTAMP, CQL3Type.Native.TIMEUUID, CQL3Type.Native.TINYINT,
CQL3Type.Native.UUID, CQL3Type.Native.VARCHAR, CQL3Type.Native.INET,
CQL3Type.Native.VARINT, CQL3Type.Native.DECIMAL, CQL3Type.Native.BOOLEAN);
CQL3Type.Native.VARINT, CQL3Type.Native.DECIMAL, CQL3Type.Native.BOOLEAN,
CQL3Type.Native.BLOB);

private static final Set<Class<? extends IPartitioner>> ILLEGAL_PARTITIONERS =
ImmutableSet.of(OrderPreservingPartitioner.class, LocalPartitioner.class, ByteOrderedPartitioner.class, RandomPartitioner.class);
Expand Down Expand Up @@ -204,10 +205,14 @@ public StorageAttachedIndex(ColumnFamilyStore baseCfs, IndexMetadata indexMetada
analyzerFactory = AbstractAnalyzer.fromOptions(indexTermType, indexMetadata.options);
memtableIndexManager = new MemtableIndexManager(this);
indexMetrics = new IndexMetrics(this, memtableIndexManager);
maxTermSizeGuardrail = indexTermType.isVector()
? Guardrails.saiVectorTermSize
: (indexTermType.isFrozen() ? Guardrails.saiFrozenTermSize
: Guardrails.saiStringTermSize);
if (indexTermType.isVector())
maxTermSizeGuardrail = Guardrails.saiVectorTermSize;
else if (indexTermType.isFrozen())
maxTermSizeGuardrail = Guardrails.saiFrozenTermSize;
else if (indexTermType.isBytes())
maxTermSizeGuardrail = Guardrails.saiBlobTermSize;
else
maxTermSizeGuardrail = Guardrails.saiStringTermSize;
}

/**
Expand Down
24 changes: 17 additions & 7 deletions src/java/org/apache/cassandra/index/sai/utils/IndexTermType.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.BooleanType;
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.CollectionType;
import org.apache.cassandra.db.marshal.CompositeType;
import org.apache.cassandra.db.marshal.DecimalType;
Expand Down Expand Up @@ -78,7 +79,8 @@ public class IndexTermType
private static final Set<AbstractType<?>> EQ_ONLY_TYPES = ImmutableSet.of(UTF8Type.instance,
AsciiType.instance,
BooleanType.instance,
UUIDType.instance);
UUIDType.instance,
BytesType.instance);

private static final byte[] IPV4_PREFIX = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 };

Expand All @@ -102,6 +104,7 @@ private enum Capability
BIG_DECIMAL,
LONG,
BOOLEAN,
BYTES,
LITERAL,
REVERSED,
FROZEN,
Expand Down Expand Up @@ -172,7 +175,7 @@ private IndexTermType(ColumnMetadata columnMetadata, List<ColumnMetadata> partit

/**
* Returns {@code true} if the index type is a literal type and will use a literal index. This applies to
* string types, frozen types, composite types and boolean type.
* string types, frozen types, composite types, bytes type and boolean type.
*/
public boolean isLiteral()
{
Expand Down Expand Up @@ -206,6 +209,14 @@ public boolean isReversed()
return capabilities.contains(Capability.REVERSED);
}

/**
* Returns {@code true} if the index type is a bytes/blob type.
*/
public boolean isBytes()
{
return capabilities.contains(Capability.BYTES);
}

/**
* Returns {@code true} if the index type is frozen, e.g. the type is wrapped with {@code frozen<type>}.
*/
Expand Down Expand Up @@ -328,11 +339,6 @@ public boolean dependsOn(ColumnMetadata columnMetadata)
return this.columnMetadata.compareTo(columnMetadata) == 0;
}

public static boolean isEqOnlyType(AbstractType<?> type)
{
return EQ_ONLY_TYPES.contains(type);
}

/**
* Indicates if the type encoding supports rounding of the raw value.
* <p>
Expand Down Expand Up @@ -683,8 +689,12 @@ else if (!indexType.subTypes().isEmpty() && !indexType.isMultiCell())
if (indexType instanceof BooleanType)
capabilities.add(Capability.BOOLEAN);

if (indexType instanceof BytesType)
capabilities.add(Capability.BYTES);

if (capabilities.contains(Capability.STRING) ||
capabilities.contains(Capability.BOOLEAN) ||
capabilities.contains(Capability.BYTES) ||
capabilities.contains(Capability.FROZEN) ||
capabilities.contains(Capability.COMPOSITE))
capabilities.add(Capability.LITERAL);
Expand Down
Loading