diff --git a/CHANGES.txt b/CHANGES.txt index 899cb79843..bb0309f931 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.11.11 + * Nodetool garbagecollect should retain SSTableLevel for LCS (CASSANDRA-16634) * Ignore stale acks received in the shadow round (CASSANDRA-16588) * Add autocomplete and error messages for provide_overlapping_tombstones (CASSANDRA-16350) * Add StorageServiceMBean.getKeyspaceReplicationInfo(keyspaceName) (CASSANDRA-16447) diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java index 4ad5ceb432..76f6a98833 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java @@ -531,6 +531,12 @@ public class CompactionManager implements CompactionManagerMBean { return new CompactionController(cfStore, toCompact, gcBefore, null, tombstoneOption); } + + @Override + protected int getLevel() + { + return txn.onlyOne().getSSTableLevel(); + } }; task.setUserDefined(true); task.setCompactionType(OperationType.GARBAGE_COLLECT); diff --git a/test/long/org/apache/cassandra/cql3/GcCompactionBench.java b/test/long/org/apache/cassandra/cql3/GcCompactionBench.java index 84c0384403..203367dba5 100644 --- a/test/long/org/apache/cassandra/cql3/GcCompactionBench.java +++ b/test/long/org/apache/cassandra/cql3/GcCompactionBench.java @@ -229,6 +229,7 @@ public class GcCompactionBench extends CQLTester int startTombCount = countTombstoneMarkers(cfs); int startRowDeletions = countRowDeletions(cfs); int startTableCount = cfs.getLiveSSTables().size(); + int startTableMaxLevel = cfs.getLiveSSTables().stream().mapToInt(SSTableReader::getSSTableLevel).max().orElseGet(() -> 0); long startSize = SSTableReader.getTotalBytes(cfs.getLiveSSTables()); System.out.println(); @@ -242,6 +243,7 @@ public class GcCompactionBench extends CQLTester int endTombCount = countTombstoneMarkers(cfs); int endRowDeletions = countRowDeletions(cfs); int endTableCount = cfs.getLiveSSTables().size(); + int endTableMaxLevel = cfs.getLiveSSTables().stream().mapToInt(SSTableReader::getSSTableLevel).max().orElseGet(() -> 0); long endSize = SSTableReader.getTotalBytes(cfs.getLiveSSTables()); System.out.println(cfs.getCompactionParametersJson()); @@ -253,9 +255,11 @@ public class GcCompactionBench extends CQLTester startTableCount, startSize, startRowCount, startRowDeletions, startTombCount)); System.out.println(String.format("At end: %12d tables %12d bytes %12d rows %12d deleted rows %12d tombstone markers", endTableCount, endSize, endRowCount, endRowDeletions, endTombCount)); + System.out.println(String.format("Max SSTable level before: %d and after %d", startTableMaxLevel, endTableMaxLevel)); String hashesAfter = getHashes(); Assert.assertEquals(hashesBefore, hashesAfter); + Assert.assertEquals(startTableMaxLevel, endTableMaxLevel); } private String getHashes() throws Throwable diff --git a/test/unit/org/apache/cassandra/cql3/GcCompactionTest.java b/test/unit/org/apache/cassandra/cql3/GcCompactionTest.java index 3af5dee24d..ee16a4d635 100644 --- a/test/unit/org/apache/cassandra/cql3/GcCompactionTest.java +++ b/test/unit/org/apache/cassandra/cql3/GcCompactionTest.java @@ -36,6 +36,7 @@ import org.apache.cassandra.db.rows.*; import org.apache.cassandra.io.sstable.ISSTableScanner; import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.schema.CompactionParams; +import org.apache.cassandra.schema.CompactionParams.TombstoneOption; import org.apache.cassandra.utils.FBUtilities; public class GcCompactionTest extends CQLTester @@ -150,6 +151,66 @@ public class GcCompactionTest extends CQLTester assertTrue(rowCount > countRows(table3)); } + @Test + public void testGarbageCollectRetainsLCSLevel() throws Throwable + { + + createTable("CREATE TABLE %s(" + + " key int," + + " column int," + + " data int," + + " PRIMARY KEY ((key), column)" + + ") WITH compaction = { 'class' : 'LeveledCompactionStrategy' };"); + + assertEquals("LeveledCompactionStrategy", getCurrentColumnFamilyStore().getCompactionStrategyManager().getName()); + + for (int i = 0; i < KEY_COUNT; ++i) + for (int j = 0; j < CLUSTERING_COUNT; ++j) + execute("INSERT INTO %s (key, column, data) VALUES (?, ?, ?)", i, j, i * j + j); + + ColumnFamilyStore cfs = getCurrentColumnFamilyStore(); + cfs.disableAutoCompaction(); + flush(); + assertEquals(1, cfs.getLiveSSTables().size()); + SSTableReader table = cfs.getLiveSSTables().iterator().next(); + int gen = table.descriptor.generation; + assertEquals(KEY_COUNT * CLUSTERING_COUNT, countRows(table)); + + assertEquals(0, table.getSSTableLevel()); // flush writes to L0 + + CompactionManager.AllSSTableOpStatus status; + status = CompactionManager.instance.performGarbageCollection(cfs, TombstoneOption.ROW, 1); + assertEquals(CompactionManager.AllSSTableOpStatus.SUCCESSFUL, status); + + assertEquals(1, cfs.getLiveSSTables().size()); + SSTableReader collected = cfs.getLiveSSTables().iterator().next(); + int collectedGen = collected.descriptor.generation; + assertTrue(collectedGen > gen); + assertEquals(KEY_COUNT * CLUSTERING_COUNT, countRows(collected)); + + assertEquals(0, collected.getSSTableLevel()); // garbagecollect should leave the LCS level where it was + + CompactionManager.instance.performMaximal(cfs, false); + + assertEquals(1, cfs.getLiveSSTables().size()); + SSTableReader compacted = cfs.getLiveSSTables().iterator().next(); + int compactedGen = compacted.descriptor.generation; + assertTrue(compactedGen > collectedGen); + assertEquals(KEY_COUNT * CLUSTERING_COUNT, countRows(compacted)); + + assertEquals(1, compacted.getSSTableLevel()); // full compaction with LCS should move to L1 + + status = CompactionManager.instance.performGarbageCollection(cfs, TombstoneOption.ROW, 1); + assertEquals(CompactionManager.AllSSTableOpStatus.SUCCESSFUL, status); + + assertEquals(1, cfs.getLiveSSTables().size()); + SSTableReader collected2 = cfs.getLiveSSTables().iterator().next(); + assertTrue(collected2.descriptor.generation > compactedGen); + assertEquals(KEY_COUNT * CLUSTERING_COUNT, countRows(collected2)); + + assertEquals(1, collected2.getSSTableLevel()); // garbagecollect should leave the LCS level where it was + } + @Test public void testGarbageCollectOrder() throws Throwable {