diff --git a/CHANGES.txt b/CHANGES.txt index 7fb6de93c5..badb45e3a7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,7 @@ are thrown while handling native protocol messages (CASSANDRA-7470) * Fix row size miscalculation in LazilyCompactedRow (CASSANDRA-7543) * Fix race in background compaction check (CASSANDRA-7745) + * Don't clear out range tombstones during compaction (CASSANDRA-7808) 1.2.18 diff --git a/src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java b/src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java index d9f753c106..4360b0b337 100644 --- a/src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java +++ b/src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java @@ -167,8 +167,9 @@ public class LazilyCompactedRow extends AbstractCompactedRow implements Iterable public boolean isEmpty() { + // need to clone emptyColumnFamily to avoid resetting the deletion time. See CASSANDRA-7808. boolean cfIrrelevant = shouldPurge - ? ColumnFamilyStore.removeDeletedCF(emptyColumnFamily, controller.gcBefore) == null + ? ColumnFamilyStore.removeDeletedCF(emptyColumnFamily.cloneMeShallow(), controller.gcBefore) == null : !emptyColumnFamily.isMarkedForDelete(); // tombstones are relevant return cfIrrelevant && columnStats.columnCount == 0; } @@ -285,11 +286,12 @@ public class LazilyCompactedRow extends AbstractCompactedRow implements Iterable ColumnFamily purged = PrecompactedRow.removeDeletedAndOldShards(key, shouldPurge, controller, container); if (purged == null || !purged.iterator().hasNext()) { - container.clear(); + // don't call clear() because that resets the deletion time. See CASSANDRA-7808. + container = emptyColumnFamily.cloneMeShallow(); return null; } IColumn reduced = purged.iterator().next(); - container.clear(); + container = emptyColumnFamily.cloneMeShallow(); // PrecompactedRow.removeDeletedAndOldShards have only checked the top-level CF deletion times, // not the range tombstone. For that we use the columnIndexer tombstone tracker. diff --git a/test/unit/org/apache/cassandra/db/RangeTombstoneTest.java b/test/unit/org/apache/cassandra/db/RangeTombstoneTest.java index c2f8b83ad4..59be938bac 100644 --- a/test/unit/org/apache/cassandra/db/RangeTombstoneTest.java +++ b/test/unit/org/apache/cassandra/db/RangeTombstoneTest.java @@ -18,17 +18,22 @@ */ package org.apache.cassandra.db; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.*; +import java.util.concurrent.ExecutionException; import org.junit.Test; import org.apache.cassandra.SchemaLoader; +import org.apache.cassandra.Util; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.db.filter.*; import org.apache.cassandra.utils.ByteBufferUtil; import static org.apache.cassandra.Util.dk; +import static org.junit.Assert.assertEquals; public class RangeTombstoneTest extends SchemaLoader { @@ -96,6 +101,61 @@ public class RangeTombstoneTest extends SchemaLoader assert !isLive(cf, cf.getColumn(b(i))) : "Column " + i + " shouldn't be live"; } + @Test + public void test7808_1() throws ExecutionException, InterruptedException + { + DatabaseDescriptor.setInMemoryCompactionLimit(0); + Table table = Table.open(KSNAME); + ColumnFamilyStore cfs = table.getColumnFamilyStore(CFNAME); + cfs.metadata.gcGraceSeconds(2); + + String key = "7808_1"; + RowMutation rm; + rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key)); + for (int i = 0; i < 40; i += 2) + add(rm, i, 0); + rm.apply(); + cfs.forceBlockingFlush(); + rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key)); + ColumnFamily cf = rm.addOrGet(CFNAME); + cf.delete(new DeletionInfo(1, 1)); + rm.apply(); + cfs.forceBlockingFlush(); + Thread.sleep(5); + cfs.forceMajorCompaction(); + } + + @Test + public void test7808_2() throws ExecutionException, InterruptedException, IOException + { + DatabaseDescriptor.setInMemoryCompactionLimit(0); + Table table = Table.open(KSNAME); + ColumnFamilyStore cfs = table.getColumnFamilyStore(CFNAME); + cfs.metadata.gcGraceSeconds(2); + + String key = "7808_2"; + RowMutation rm; + rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key)); + for (int i = 10; i < 20; i++) + add(rm, i, 0); + rm.apply(); + cfs.forceBlockingFlush(); + + rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key)); + ColumnFamily cf = rm.addOrGet(CFNAME); + cf.delete(new DeletionInfo(0,0)); + rm.apply(); + + rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key)); + add(rm, 5, 1); + rm.apply(); + + cfs.forceBlockingFlush(); + Thread.sleep(5); + cfs.forceMajorCompaction(); + assertEquals(1, Util.getColumnFamily(table, Util.dk(key), CFNAME).getColumnCount()); + } + @Test public void overlappingRangeTest() throws Exception {