Skip to content

CNDB-14481: Fix IllegalStateException in SegmentMetadataBuilder #1808

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 18, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import javax.annotation.concurrent.NotThreadSafe;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -492,22 +493,32 @@ public long analyzeAndAdd(ByteBuffer rawTerm, AbstractType<?> type, PrimaryKey k

private long add(List<ByteBuffer> terms, PrimaryKey key, long sstableRowId)
{
assert !flushed : "Cannot add to flushed segment.";
assert sstableRowId >= maxSSTableRowId;
if (terms.isEmpty())
return 0;

Preconditions.checkState(!flushed, "Cannot add to flushed segment");
Preconditions.checkArgument(sstableRowId >= maxSSTableRowId,
"rowId must be greater than or equal to the last rowId added: %s < %s", sstableRowId, maxSSTableRowId);
Preconditions.checkArgument(maxKey == null || key.compareTo(maxKey) >= 0,
"Key must be greater than or equal to the last key added: %s < %s", key, maxKey);

minSSTableRowId = minSSTableRowId < 0 ? sstableRowId : minSSTableRowId;
maxSSTableRowId = sstableRowId;

assert maxKey == null || maxKey.compareTo(key) <= 0;
minKey = minKey == null ? key : minKey;
maxKey = key;

// Update term boundaries for all terms in this row
for (ByteBuffer term : terms)
{
assert term != null : "term must not be null";
minTerm = TypeUtil.min(term, minTerm, termComparator, Version.current());
maxTerm = TypeUtil.max(term, maxTerm, termComparator, Version.current());
}

assert minTerm != null : "minTerm should not be null at this point";
assert maxTerm != null : "maxTerm should not be null at this point";

// segmentRowIdOffset should encode sstableRowId into Integer
int segmentRowId = Math.toIntExact(sstableRowId - segmentRowIdOffset);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;

import com.google.common.base.Preconditions;

import org.apache.cassandra.index.sai.IndexContext;
import org.apache.cassandra.index.sai.disk.PostingList;
import org.apache.cassandra.index.sai.disk.TermsIterator;
Expand Down Expand Up @@ -99,14 +101,16 @@ public void setTotalTermCount(long totalTermCount)

public void setKeyRange(@Nonnull PrimaryKey minKey, @Nonnull PrimaryKey maxKey)
{
assert minKey.compareTo(maxKey) <= 0: "minKey (" + minKey + ") must not be greater than (" + maxKey + ')';
Preconditions.checkNotNull(minKey, "minKey must not be null");
Preconditions.checkNotNull(maxKey, "maxKey must not be null");
Preconditions.checkArgument(minKey.compareTo(maxKey) <= 0, "minKey (" + minKey + ") must not be greater than (" + maxKey + ')');
this.minKey = minKey;
this.maxKey = maxKey;
}

public void setRowIdRange(long minRowId, long maxRowId)
{
assert minRowId <= maxRowId: "minRowId (" + minRowId + ") must not be greater than (" + maxRowId + ')';
Preconditions.checkArgument(minRowId <= maxRowId, "minRowId (" + minRowId + ") must not be greater than (" + maxRowId + ')');
this.minRowId = minRowId;
this.maxRowId = maxRowId;
}
Expand All @@ -120,6 +124,8 @@ public void setRowIdRange(long minRowId, long maxRowId)
*/
public void setTermRange(@Nonnull ByteBuffer minTerm, @Nonnull ByteBuffer maxTerm)
{
Preconditions.checkNotNull(minTerm, "minTerm must not be null");
Preconditions.checkNotNull(maxTerm, "maxTerm must not be null");
this.minTerm = minTerm;
this.maxTerm = maxTerm;
}
Expand Down
34 changes: 34 additions & 0 deletions test/unit/org/apache/cassandra/index/sai/cql/AnalyzerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,40 @@

public class AnalyzerTest extends SAITester
{
@Test
public void testEmptyOnInitialBuild()
{
createTable("CREATE TABLE %s (k int PRIMARY KEY, v text)");
execute("INSERT INTO %s (k, v) VALUES (1, '')");
flush();
createIndex("CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex' WITH OPTIONS = {" +
"'index_analyzer': 'standard'};");
}

@Test
public void testEmptyOnCompaction()
{
createTable("CREATE TABLE %s (k int PRIMARY KEY, v text)");
createIndex("CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex' WITH OPTIONS = {" +
"'index_analyzer': 'standard'};");
execute("INSERT INTO %s (k, v) VALUES (1, 'apple orange')");
flush();
execute("INSERT INTO %s (k, v) VALUES (1, '')");
flush();
compact();
}

@Test
public void testEmptyWithStopwords()
{
createTable("CREATE TABLE %s (k int PRIMARY KEY, v text)");
execute("INSERT INTO %s (k, v) VALUES (1, 'and then')"); // will yield no indexed terms
flush();
createIndex("CREATE CUSTOM INDEX ON %s(v) USING 'StorageAttachedIndex' WITH OPTIONS = {" +
"'index_analyzer': 'english'};");
assertEmpty(execute("SELECT * FROM %s WHERE v = 'and'"));
}

@Test
public void createAnalyzerWrongTypeTest()
{
Expand Down