Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Cassandra SASI (SSTable Attached Secondary Index)

SASI (SSTable Attached Secondary Index) was introduced in Cassandra 3.4 (2016) as an experimental indexing system providing range queries and text search capabilities. While offering significant improvements over legacy secondary indexes, SASI remains experimental and has known limitations.

Experimental Status

SASI is marked as experimental in Cassandra. For production workloads on Cassandra 5.0+, use SAI instead.


SASI was developed by Apple and contributed to Apache Cassandra in 2016 (CASSANDRA-10661). The design addressed key limitations of legacy secondary indexes:

  • Inability to perform range queries
  • No text search capabilities
  • Scatter-gather query patterns
  • Separate index table compaction

SASI introduced several architectural improvements:

  1. SSTable attachment: Index data stored alongside base table SSTables
  2. Range queries: Support for inequality operators (>, <, >=, <=)
  3. Text search: PREFIX and CONTAINS operations
  4. Single-pass intersection: Multiple predicates evaluated together

Despite its capabilities, SASI has remained experimental since introduction:

  • Complex codebase with limited maintainership
  • Memory management concerns during queries
  • Known bugs in edge cases
  • Superseded by SAI in Cassandra 5.0

Unlike legacy secondary indexes that use separate hidden tables, SASI stores index data as additional SSTable components:

SASI: Index Data Attached to SSTableSASI: Index Data Attached to SSTableSSTable ComponentsData.db(row data)Index.db(partition index)Filter.db(bloom filter)-SASI.db(SASI index)SASI index files createdper indexed columnCompacted with SSTable

Benefits of SSTable attachment:

  • Index compacts with base data
  • No separate compaction coordination
  • Index lifecycle matches data lifecycle
  • Reduced storage overhead

SASI supports three index modes optimized for different data types:

SASI Index ModesSASI Index ModesPREFIX ModeCONTAINS ModeSPARSE ModeTrie-based structureOptimized for string prefixesLIKE 'abc%' queriesN-gram tokenizationFull substring searchLIKE '%abc%' queriesB+ tree structureNumeric range queriesGreater/less than
ModeData TypeQuery SupportUse Case
PREFIXTextLIKE 'abc%'String prefix matching
CONTAINSTextLIKE '%abc%'Full-text search
SPARSENumeric>, <, >=, <=Range queries

SASI queries iterate through SSTables, applying predicates locally before returning results:

SASI Query ExecutionSASI Query ExecutionLocal Node ProcessingSSTable 1SSTable 2SSTable 3Single-passintersectionQuery:WHERE age > 25AND city LIKE 'New%'MatchingPartition Keys

Single-pass intersection: Multiple SASI predicates are intersected within each SSTable iteration on each node, providing more efficient local processing for multi-predicate queries. Note that in a distributed cluster, queries still scatter to replicas and gather results at the coordinator—the intersection occurs locally on each node, not globally.


-- Basic SASI index (PREFIX mode for text)
CREATE CUSTOM INDEX ON users (email)
USING 'org.apache.cassandra.index.sasi.SASIIndex';
-- CONTAINS mode for substring search
CREATE CUSTOM INDEX ON users (bio)
USING 'org.apache.cassandra.index.sasi.SASIIndex'
WITH OPTIONS = {
'mode': 'CONTAINS',
'analyzer_class': 'org.apache.cassandra.index.sasi.analyzer.StandardAnalyzer',
'case_sensitive': 'false'
};
-- SPARSE mode for numeric ranges
CREATE CUSTOM INDEX ON events (timestamp)
USING 'org.apache.cassandra.index.sasi.SASIIndex'
WITH OPTIONS = { 'mode': 'SPARSE' };
-- PREFIX mode with case insensitivity
CREATE CUSTOM INDEX ON products (name)
USING 'org.apache.cassandra.index.sasi.SASIIndex'
WITH OPTIONS = {
'mode': 'PREFIX',
'case_sensitive': 'false'
};
OptionValuesDefaultDescription
modePREFIX, CONTAINS, SPARSEPREFIXIndex mode for query types
case_sensitivetrue, falsetrueCase sensitivity for text
analyzedtrue, falsefalseEnable text analysis
analyzer_classclass name-Custom analyzer for tokenization
max_compaction_flush_memory_in_mbinteger1024Memory limit during compaction

For text search with CONTAINS mode:

-- Standard analyzer (whitespace tokenization)
CREATE CUSTOM INDEX ON articles (content)
USING 'org.apache.cassandra.index.sasi.SASIIndex'
WITH OPTIONS = {
'mode': 'CONTAINS',
'analyzer_class': 'org.apache.cassandra.index.sasi.analyzer.StandardAnalyzer',
'tokenization_enable_stemming': 'true',
'tokenization_locale': 'en',
'tokenization_skip_stop_words': 'true'
};
-- Non-tokenizing analyzer (exact substring matching)
CREATE CUSTOM INDEX ON logs (message)
USING 'org.apache.cassandra.index.sasi.SASIIndex'
WITH OPTIONS = {
'mode': 'CONTAINS',
'analyzer_class': 'org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer',
'case_sensitive': 'false'
};

-- PREFIX: Starts with
SELECT * FROM users WHERE email LIKE 'john%';
-- CONTAINS: Substring anywhere
SELECT * FROM articles WHERE content LIKE '%database%';
-- Case insensitive (if configured)
SELECT * FROM products WHERE name LIKE 'Apple%';
-- Greater than
SELECT * FROM events WHERE timestamp > '2024-01-01';
-- Less than or equal
SELECT * FROM sensors WHERE reading <= 100.0;
-- Range (requires two conditions)
SELECT * FROM events
WHERE timestamp >= '2024-01-01'
AND timestamp < '2024-02-01';
-- Multiple SASI indexes
SELECT * FROM users
WHERE age > 25
AND city LIKE 'New%'
AND status = 'active';
-- SASI with partition key (most efficient)
SELECT * FROM events
WHERE sensor_id = ?
AND timestamp > '2024-01-01';

SASI queries can consume significant memory:

Problem: CONTAINS mode builds in-memory structures
Large result sets held in memory
No streaming for intermediate results
Symptoms:
- GC pressure during queries
- OOM errors on large datasets
- Query timeouts

Mitigation:

  • Use LIMIT clauses
  • Combine with partition key restrictions
  • Tune max_compaction_flush_memory_in_mb

SASI has known issues with tombstones:

Problem: Tombstones not always properly filtered
Deleted data may appear in results
Impact: Consistency issues in rare cases

N-gram tokenization for CONTAINS mode creates storage overhead:

Example: String "database"
N-grams (n=3): dat, ata, tab, aba, bas, ase
Storage impact: ~3x original string size
Query impact: More index entries to scan

Known issues include:

  • Memory leaks under specific query patterns
  • Incorrect results with certain predicate combinations
  • Performance degradation with high tombstone ratios
  • Compaction issues with large indexes

Primary advantage over legacy secondary indexes:

-- Not possible with 2i, possible with SASI
SELECT * FROM metrics WHERE value > 100.0;
SELECT * FROM logs WHERE timestamp >= '2024-01-01';

Built-in text search without external systems:

-- Substring search
SELECT * FROM products WHERE description LIKE '%wireless%';
-- Prefix search
SELECT * FROM users WHERE name LIKE 'John%';

Single-pass intersection reduces overhead on each node:

-- 2i: Two separate index lookups per node + coordinator merge
-- SASI: Single pass through SSTables with local intersection per node
SELECT * FROM users
WHERE age > 25 AND city LIKE 'San%';

Note: The query still contacts multiple nodes; the efficiency gain is in local processing on each node.

Index lifecycle matches data:

  • Compacts together
  • Deleted together
  • No orphaned index entries

ScenarioRationale
Development/testing with range queriesFaster than data model redesign
Low-traffic text searchAvoids external search system
Cassandra 3.4 - 4.x without SAIOnly range query option
Proof of conceptValidate query patterns before SAI migration
ScenarioAlternative
Production Cassandra 5.0+Use SAI
High-throughput queriesDenormalized tables
Large CONTAINS searchesExternal search (Elasticsearch)
Mission-critical workloadsSAI or data model redesign

For Cassandra 5.0+, migrate SASI indexes to SAI:

-- Drop SASI index
DROP INDEX IF EXISTS users_email_idx;
-- Create SAI index
CREATE INDEX users_email_idx ON users (email)
USING 'sai';
-- SAI with text analysis
CREATE INDEX users_bio_idx ON users (bio)
USING 'sai'
WITH OPTIONS = {
'index_analyzer': 'standard'
};

Migration considerations:

  • SAI syntax differs from SASI
  • Some SASI analyzers have no SAI equivalent
  • Test query patterns after migration
  • SAI is production-ready; SASI is not

Terminal window
# Check index build status
nodetool describecluster
# Table statistics
nodetool tablestats keyspace.table
SymptomLikely CauseAction
High GC during queriesMemory pressureAdd LIMIT, restrict partition
Slow CONTAINS queriesLarge n-gram indexConsider external search
Inconsistent resultsTombstone bugsVerify with full scan
Compaction failuresMemory limitsTune flush memory