You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by "Alex Petrov (Jira)" <ji...@apache.org> on 2026/03/25 16:13:00 UTC
[jira] [Created] (CASSANDRA-21255) Range selects return 0 rows after compaction purges an open-ended range tombstone;
Alex Petrov created CASSANDRA-21255:
---------------------------------------
Summary: Range selects return 0 rows after compaction purges an open-ended range tombstone;
Key: CASSANDRA-21255
URL: https://issues.apache.org/jira/browse/CASSANDRA-21255
Project: Apache Cassandra
Issue Type: Bug
Reporter: Alex Petrov
Assignee: Alex Petrov
Minimal reproduction of a bug where range selects return 0 rows after compaction purges an open-ended range tombstone.
When column_index_size is small (1 KiB) and the partition spans multiple index blocks, an open-ended range delete whose lower bound falls in the last index block causes range selects that seek into later blocks to return empty results after compaction, even though the data is present (partition scans return correct results).
Reproduce:
{code}
1. column_index_size_in_kb = 1
2. Insert 30 rows with ~100-byte values (spans ~3 index blocks)
3. DELETE WHERE ck >= 'r026' (open-ended, in last block)
4. Flush both, major-compact (purges the tombstone with gc_grace=0)
5. SELECT WHERE ck >= 'r023' AND ck <= 'r029' → returns 0 (expected 3)
{code}
{code}
public class RawRangeTombstoneReproTest extends CQLTester
{
private int savedColumnIndexSizeInKiB;
private int savedColumnIndexCacheSizeInKiB;
@Before
public void saveConfig()
{
savedColumnIndexSizeInKiB = DatabaseDescriptor.getColumnIndexSizeInKiB();
savedColumnIndexCacheSizeInKiB = DatabaseDescriptor.getColumnIndexCacheSizeInKiB();
DatabaseDescriptor.setColumnIndexSizeInKiB(1);
DatabaseDescriptor.setColumnIndexCacheSize(1);
}
@After
public void restoreConfig()
{
DatabaseDescriptor.setColumnIndexSizeInKiB(savedColumnIndexSizeInKiB);
DatabaseDescriptor.setColumnIndexCacheSize(savedColumnIndexCacheSizeInKiB);
}
private ColumnFamilyStore getCfs()
{
return Keyspace.open(KEYSPACE).getColumnFamilyStore(currentTable());
}
@Test
public void testRangeSelectReturnsEmptyAfterCompaction()
{
createTable("CREATE TABLE %s (pk text, ck text, v1 text, PRIMARY KEY (pk, ck))" +
" WITH gc_grace_seconds = 0" +
" AND compression = {'enabled': 'false'}" +
" AND compaction = {'class': 'SizeTieredCompactionStrategy', 'enabled': false}");
// 30 rows with ~100-byte values → ~3 index blocks at 1 KiB each
String pad = "x".repeat(80);
for (int i = 0; i < 30; i++)
execute("INSERT INTO %s (pk, ck, v1) VALUES (?, ?, ?)",
"p", String.format("r%03d", i), pad + i);
getCfs().forceBlockingFlush(ColumnFamilyStore.FlushReason.UNIT_TESTS);
// Open-ended delete: removes rows 26-29 (last block)
execute("DELETE FROM %s WHERE pk = ? AND ck >= ?", "p", "r026");
getCfs().forceBlockingFlush(ColumnFamilyStore.FlushReason.UNIT_TESTS);
getCfs().forceMajorCompaction();
// Partition scan sees all 26 surviving rows — data is present
UntypedResultSet all = execute("SELECT * FROM %s WHERE pk = ?", "p");
assertEquals("partition scan should see 26 rows", 26, all.size());
// BUG: range select near end returns 0 instead of 3
UntypedResultSet range = execute(
"SELECT * FROM %s WHERE pk = ? AND ck >= ? AND ck <= ?",
"p", "r023", "r029");
assertEquals("range select [r023,r029] should return 3 rows (23,24,25)",
3, range.size());
}
}
{code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org