Alex Petrov created CASSANDRA-21292:
---------------------------------------
Summary: Row re-inserted at the exact start of a range tombstone
disappears after major compaction
Key: CASSANDRA-21292
URL: https://issues.apache.org/jira/browse/CASSANDRA-21292
Project: Apache Cassandra
Issue Type: Bug
Reporter: Alex Petrov
Assignee: Alex Petrov
{code}
package org.apache.cassandra.db;
import org.junit.Assert;
import org.junit.Test;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
/**
* Repro for a bug where a row re-inserted at the exact start of a range
tombstone
* disappears after major compaction.
*
* Reproduces with LeveledCompactionStrategy and gc_grace_seconds=86400 (the
default).
*
* 1. Insert NUM_ROWS rows (ck = "row000000".."rowNNNNNN") into SSTable #1.
* 2. Delete ck >= TOMBSTONE_CK (open-ended; sorts after all "row*" keys).
* 3. Re-insert TOMBSTONE_CK with a timestamp newer than the delete.
* 4. Force major compaction.
* 5. Range-scan ... AND ck <= TOMBSTONE_CK -- the re-inserted row disappears.
*/
public class RangeTombstoneBoundaryReproTest extends CQLTester
{
// Padding makes each row large enough that NUM_ROWS rows span multiple 1 KiB
// column-index blocks. With ~300-byte rows and 1 KiB blocks, ~3 rows/block;
// NUM_ROWS = 9 is the minimum: 8 rows pass cleanly.
private static final String VALUE_PAD = "x".repeat(270);
private static final int NUM_ROWS = 9;
// Tombstone boundary: sorts after every "row*" key.
private static final String TOMBSTONE_CK = "z";
@Test
public void testReinsertedRowAtTombstoneBoundaryMissingAfterCompaction()
throws Throwable
{
int savedIdx = DatabaseDescriptor.getColumnIndexSizeInKiB();
int savedCache = DatabaseDescriptor.getColumnIndexCacheSizeInKiB();
DatabaseDescriptor.setColumnIndexSizeInKiB(1);
DatabaseDescriptor.setColumnIndexCacheSize(1);
try
{
createTable(
"CREATE TABLE %s (" +
" pk ascii, ck ascii, v text," +
" PRIMARY KEY (pk, ck)" +
") WITH" +
" compaction = {" +
" 'class': 'LeveledCompactionStrategy'," +
" 'enabled': false" +
" }"
);
final String PK = "p";
// SSTable 1: bulk insert
for (int i = 0; i < NUM_ROWS; i++)
execute("INSERT INTO %s (pk, ck, v) VALUES (?, ?, ?) USING
TIMESTAMP ?",
PK, String.format("row%06d", i), VALUE_PAD + i, (long)
i);
flush();
// SSTable 2: open-ended tombstone starting at TOMBSTONE_CK
execute("DELETE FROM %s USING TIMESTAMP 100 WHERE pk = ? AND ck >=
?",
PK, TOMBSTONE_CK);
flush();
// SSTable 3: re-insert at tombstone boundary with newer timestamp
execute("INSERT INTO %s (pk, ck, v) VALUES (?, ?, ?) USING TIMESTAMP
200",
PK, TOMBSTONE_CK, "reinserted");
flush();
// Sanity: row must exist before compaction
UntypedResultSet before = execute(
"SELECT ck FROM %s WHERE pk = ? AND ck >= ? AND ck <= ?",
PK, "s", TOMBSTONE_CK);
Assert.assertEquals("Row must exist before compaction", 1,
before.size());
getCurrentColumnFamilyStore().forceMajorCompaction(); // Comment /
uncomment this line to make the test pass
// BUG: re-inserted row disappears after compaction
UntypedResultSet after = execute(
"SELECT ck FROM %s WHERE pk = ? AND ck >= ? AND ck <= ?",
PK, "s", TOMBSTONE_CK);
Assert.assertEquals(
"Re-inserted row at tombstone boundary must survive compaction",
1, after.size());
}
finally
{
DatabaseDescriptor.setColumnIndexSizeInKiB(savedIdx);
DatabaseDescriptor.setColumnIndexCacheSize(savedCache);
}
}
}
{code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]