You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jb...@apache.org on 2010/07/17 04:09:41 UTC

svn commit: r964995 - in /cassandra/trunk: src/java/org/apache/cassandra/db/ src/java/org/apache/cassandra/db/filter/ src/java/org/apache/cassandra/io/ test/unit/org/apache/cassandra/ test/unit/org/apache/cassandra/db/

Author: jbellis
Date: Sat Jul 17 02:09:41 2010
New Revision: 964995

URL: http://svn.apache.org/viewvc?rev=964995&view=rev
Log:
performance improvements to removeDeleted on read path.  patch by jbellis and tjake for CASSANDRA-1267

Modified:
    cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
    cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java
    cassandra/trunk/src/java/org/apache/cassandra/db/filter/QueryFilter.java
    cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java
    cassandra/trunk/src/java/org/apache/cassandra/io/LazilyCompactedRow.java
    cassandra/trunk/test/unit/org/apache/cassandra/Util.java
    cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyTest.java
    cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush1Test.java
    cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush2Test.java
    cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnTest.java
    cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSubColumnTest.java
    cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java

Modified: cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java Sat Jul 17 02:09:41 2010
@@ -496,6 +496,15 @@ public class ColumnFamilyStore implement
         writeStats_.addNano(System.nanoTime() - start);
     }
 
+    public static ColumnFamily removeDeletedCF(ColumnFamily cf, int gcBefore)
+    {
+        // in case of a timestamp tie, tombstones get priority over non-tombstones.
+        // (we want this to be deterministic to avoid confusion.)
+        if (cf.getColumnCount() == 0 && cf.getLocalDeletionTime() <= gcBefore)
+            return null;
+        return cf;
+    }
+
     /*
      This is complicated because we need to preserve deleted columns, supercolumns, and columnfamilies
      until they have been deleted for at least GC_GRACE_IN_SECONDS.  But, we do not need to preserve
@@ -509,25 +518,24 @@ public class ColumnFamilyStore implement
             return null;
         }
 
+        removeDeletedColumnsOnly(cf, gcBefore);
+        return removeDeletedCF(cf, gcBefore);
+    }
+
+    private static void removeDeletedColumnsOnly(ColumnFamily cf, int gcBefore)
+    {
         if (cf.isSuper())
             removeDeletedSuper(cf, gcBefore);
         else
             removeDeletedStandard(cf, gcBefore);
-
-        // in case of a timestamp tie, tombstones get priority over non-tombstones.
-        // (we want this to be deterministic to avoid confusion.)
-        if (cf.getColumnCount() == 0 && cf.getLocalDeletionTime() <= gcBefore)
-        {
-            return null;
-        }
-        return cf;
     }
 
     private static void removeDeletedStandard(ColumnFamily cf, int gcBefore)
     {
-        for (byte[] cname : cf.getColumnNames())
+        for (Map.Entry<byte[], IColumn> entry : cf.getColumnsMap().entrySet())
         {
-            IColumn c = cf.getColumnsMap().get(cname);
+            byte[] cname = entry.getKey();
+            IColumn c = entry.getValue();
             // remove columns if
             // (a) the column itself is tombstoned or
             // (b) the CF is tombstoned and the column is not newer than it
@@ -552,9 +560,9 @@ public class ColumnFamilyStore implement
         // TODO assume deletion means "most are deleted?" and add to clone, instead of remove from original?
         // this could be improved by having compaction, or possibly even removeDeleted, r/m the tombstone
         // once gcBefore has passed, so if new stuff is added in it doesn't used the wrong algorithm forever
-        for (byte[] cname : cf.getColumnNames())
+        for (Map.Entry<byte[], IColumn> entry : cf.getColumnsMap().entrySet())
         {
-            IColumn c = cf.getColumnsMap().get(cname);
+            SuperColumn c = (SuperColumn) entry.getValue();
             List<IClock> clocks = Arrays.asList(cf.getMarkedForDeleteAt());
             IClock minClock = c.getMarkedForDeleteAt().getSuperset(clocks);
             for (IColumn subColumn : c.getSubColumns())
@@ -565,14 +573,14 @@ public class ColumnFamilyStore implement
                 // (we split the test to avoid computing ClockRelationship if not necessary)
                 if (subColumn.isMarkedForDelete() && subColumn.getLocalDeletionTime() <= gcBefore)
                 {
-                    ((SuperColumn)c).remove(subColumn.name());
+                    c.remove(subColumn.name());
                 }
                 else
                 {
                     ClockRelationship subRel = subColumn.clock().compare(minClock);
                     if ((ClockRelationship.LESS_THAN == subRel) || (ClockRelationship.EQUAL == subRel))
                     {
-                        ((SuperColumn)c).remove(subColumn.name());
+                        c.remove(subColumn.name());
                     }
                 }
             }
@@ -783,6 +791,11 @@ public class ColumnFamilyStore implement
         return getColumnFamily(QueryFilter.getSliceFilter(key, path, start, finish, null, reversed, limit));
     }
 
+    /**
+     * get a list of columns starting from a given column, in a specified order.
+     * only the latest version of a column is returned.
+     * @return null if there is no data and no tombstones; otherwise a ColumnFamily
+     */
     public ColumnFamily getColumnFamily(QueryFilter filter)
     {
         return getColumnFamily(filter, CompactionManager.getDefaultGCBefore());
@@ -801,12 +814,7 @@ public class ColumnFamilyStore implement
         return cached;
     }
 
-    /**
-     * get a list of columns starting from a given column, in a specified order.
-     * only the latest version of a column is returned.
-     * @return null if there is no data and no tombstones; otherwise a ColumnFamily
-     */
-    public ColumnFamily getColumnFamily(QueryFilter filter, int gcBefore)
+    private ColumnFamily getColumnFamily(QueryFilter filter, int gcBefore)
     {
         assert columnFamily_.equals(filter.getColumnFamilyName());
 
@@ -814,17 +822,37 @@ public class ColumnFamilyStore implement
         try
         {
             if (ssTables_.getRowCache().getCapacity() == 0)
-                return removeDeleted(getTopLevelColumns(filter, gcBefore), gcBefore);
+            {
+                ColumnFamily cf = getTopLevelColumns(filter, gcBefore);
+                // TODO this is necessary because when we collate supercolumns together, we don't check
+                // their subcolumns for relevance, so we need to do a second prune post facto here.
+                return cf.isSuper() ? removeDeleted(cf, gcBefore) : removeDeletedCF(cf, gcBefore);
+            }
 
             ColumnFamily cached = cacheRow(filter.key);
             if (cached == null)
                 return null;
+            
+            // special case slicing the entire row:
+            // we can skip the filter step entirely, and we can help out removeDeleted by re-caching the result
+            // if any tombstones have aged out since last time.  (This means that the row cache will treat gcBefore as
+            // max(gcBefore, all previous gcBefore), which is fine for correctness.)
+            if (filter.filter instanceof SliceQueryFilter)
+            {
+                SliceQueryFilter sliceFilter = (SliceQueryFilter) filter.filter;
+                if (sliceFilter.start.length == 0 && sliceFilter.finish.length == 0 && sliceFilter.count > cached.getColumnCount())
+                {
+                    removeDeletedColumnsOnly(cached, gcBefore);
+                    return removeDeletedCF(cached, gcBefore);
+                }
+            }
+            
             IColumnIterator ci = filter.getMemtableColumnIterator(cached, null, getComparator());
-            ColumnFamily returnCF = ci.getColumnFamily().cloneMeShallow();
-            filter.collectCollatedColumns(returnCF, ci, gcBefore);
+            ColumnFamily cf = ci.getColumnFamily().cloneMeShallow();
+            filter.collectCollatedColumns(cf, ci, gcBefore);
             // TODO this is necessary because when we collate supercolumns together, we don't check
             // their subcolumns for relevance, so we need to do a second prune post facto here.
-            return removeDeleted(returnCF, gcBefore);
+            return cf.isSuper() ? removeDeleted(cf, gcBefore) : removeDeletedCF(cf, gcBefore);
         }
         catch (IOException e)
         {
@@ -878,7 +906,10 @@ public class ColumnFamilyStore implement
             Comparator<IColumn> comparator = QueryFilter.getColumnComparator(getComparator());
             Iterator collated = IteratorUtils.collatedIterator(comparator, iterators);
             filter.collectCollatedColumns(returnCF, collated, gcBefore);
-            return returnCF; // caller is responsible for final removeDeleted
+            // Caller is responsible for final removeDeletedCF.  This is important for cacheRow to work correctly:
+            // we need to distinguish between "there is no data at all for this row" (BF will let us rebuild that efficiently)
+            // and "there used to be data, but it's gone now" (we should cache the empty CF so we don't need to rebuild that slower)
+            return returnCF;
         }
         catch (IOException e)
         {

Modified: cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java Sat Jul 17 02:09:41 2010
@@ -238,9 +238,7 @@ public class Memtable implements Compara
 
             public IColumn next()
             {
-                // clone supercolumns so caller can freely removeDeleted or otherwise mutate it
-                // TODO can't the callers that wish to mutate it clone it themselves?
-                return isSuper ? ((SuperColumn) filteredIter.next()).cloneMe() : filteredIter.next();
+                return filteredIter.next();
             }
         };
     }

Modified: cassandra/trunk/src/java/org/apache/cassandra/db/filter/QueryFilter.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/db/filter/QueryFilter.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/db/filter/QueryFilter.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/db/filter/QueryFilter.java Sat Jul 17 02:09:41 2010
@@ -40,7 +40,7 @@ public class QueryFilter
 
     public final DecoratedKey key;
     public final QueryPath path;
-    private final IFilter filter;
+    public final IFilter filter;
     private final IFilter superFilter;
 
     public QueryFilter(DecoratedKey key, QueryPath path, IFilter filter)

Modified: cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java Sat Jul 17 02:09:41 2010
@@ -81,7 +81,6 @@ public class SliceQueryFilter implements
                : getBitmaskMatchColumnPredicate();
     }
 
-
     public SuperColumn filterSuperColumn(SuperColumn superColumn, int gcBefore)
     {
         // we clone shallow, then add, under the theory that generally we're interested in a relatively small number of subcolumns.

Modified: cassandra/trunk/src/java/org/apache/cassandra/io/LazilyCompactedRow.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/io/LazilyCompactedRow.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/io/LazilyCompactedRow.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/io/LazilyCompactedRow.java Sat Jul 17 02:09:41 2010
@@ -117,7 +117,7 @@ public class LazilyCompactedRow extends 
 
     public boolean isEmpty()
     {
-        boolean cfIrrelevant = ColumnFamilyStore.removeDeleted(emptyColumnFamily, gcBefore) == null;
+        boolean cfIrrelevant = ColumnFamilyStore.removeDeletedCF(emptyColumnFamily, gcBefore) == null;
         return cfIrrelevant && columnCount == 0;
     }
 

Modified: cassandra/trunk/test/unit/org/apache/cassandra/Util.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/Util.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/Util.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/Util.java Sat Jul 17 02:09:41 2010
@@ -105,4 +105,9 @@ public class Util
         assert cfStore != null : "Column family " + cfName + " has not been defined";
         return cfStore.getColumnFamily(QueryFilter.getIdentityFilter(key, new QueryPath(cfName)));
     }
+
+    public static ColumnFamily cloneAndRemoveDeleted(ColumnFamily cf, int gcBefore)
+    {
+        return ColumnFamilyStore.removeDeleted(cf.cloneMe(), gcBefore);
+    }
 }

Modified: cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyTest.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyTest.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyTest.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyTest.java Sat Jul 17 02:09:41 2010
@@ -53,6 +53,6 @@ public class RemoveColumnFamilyTest exte
         ColumnFamily retrieved = store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Standard1", null, "Column1".getBytes())));
         assert retrieved.isMarkedForDelete();
         assertNull(retrieved.getColumn("Column1".getBytes()));
-        assertNull(ColumnFamilyStore.removeDeleted(retrieved, Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(retrieved, Integer.MAX_VALUE));
     }
 }

Modified: cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush1Test.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush1Test.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush1Test.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush1Test.java Sat Jul 17 02:09:41 2010
@@ -55,6 +55,6 @@ public class RemoveColumnFamilyWithFlush
         ColumnFamily retrieved = store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Standard1")));
         assert retrieved.isMarkedForDelete();
         assertNull(retrieved.getColumn("Column1".getBytes()));
-        assertNull(ColumnFamilyStore.removeDeleted(retrieved, Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(retrieved, Integer.MAX_VALUE));
     }
 }

Modified: cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush2Test.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush2Test.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush2Test.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnFamilyWithFlush2Test.java Sat Jul 17 02:09:41 2010
@@ -53,6 +53,6 @@ public class RemoveColumnFamilyWithFlush
         ColumnFamily retrieved = store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Standard1", null, "Column1".getBytes())));
         assert retrieved.isMarkedForDelete();
         assertNull(retrieved.getColumn("Column1".getBytes()));
-        assertNull(ColumnFamilyStore.removeDeleted(retrieved, Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(retrieved, Integer.MAX_VALUE));
     }
 }

Modified: cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnTest.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnTest.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnTest.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveColumnTest.java Sat Jul 17 02:09:41 2010
@@ -24,7 +24,7 @@ import java.util.concurrent.ExecutionExc
 import org.junit.Test;
 
 import static junit.framework.Assert.assertNull;
-import org.apache.cassandra.db.filter.NamesQueryFilter;
+
 import org.apache.cassandra.db.filter.QueryFilter;
 import org.apache.cassandra.db.filter.QueryPath;
 
@@ -54,7 +54,7 @@ public class RemoveColumnTest extends Cl
 
         ColumnFamily retrieved = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Standard1"), "Column1".getBytes()));
         assert retrieved.getColumn("Column1".getBytes()).isMarkedForDelete();
-        assertNull(ColumnFamilyStore.removeDeleted(retrieved, Integer.MAX_VALUE));
-        assertNull(ColumnFamilyStore.removeDeleted(store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Standard1"))), Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(retrieved, Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Standard1"))), Integer.MAX_VALUE));
     }
 }

Modified: cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSubColumnTest.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSubColumnTest.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSubColumnTest.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSubColumnTest.java Sat Jul 17 02:09:41 2010
@@ -53,6 +53,6 @@ public class RemoveSubColumnTest extends
 
         ColumnFamily retrieved = store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Super1", "SC1".getBytes())));
         assert retrieved.getColumn("SC1".getBytes()).getSubColumn(getBytes(1)).isMarkedForDelete();
-        assertNull(ColumnFamilyStore.removeDeleted(retrieved, Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(retrieved, Integer.MAX_VALUE));
     }
 }

Modified: cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java?rev=964995&r1=964994&r2=964995&view=diff
==============================================================================
--- cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java (original)
+++ cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java Sat Jul 17 02:09:41 2010
@@ -93,20 +93,24 @@ public class RemoveSuperColumnTest exten
     private void validateRemoveSubColumn(DecoratedKey dk) throws IOException
     {
         ColumnFamilyStore store = Table.open("Keyspace1").getColumnFamilyStore("Super3");
-        assertNull(store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super3", "SC1".getBytes()), Util.getBytes(1)), Integer.MAX_VALUE));
-        assertNotNull(store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super3", "SC1".getBytes()), Util.getBytes(2)), Integer.MAX_VALUE));
+        ColumnFamily cf = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super3", "SC1".getBytes()), Util.getBytes(1)));
+        assertNull(Util.cloneAndRemoveDeleted(cf, Integer.MAX_VALUE));
+        cf = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super3", "SC1".getBytes()), Util.getBytes(2)));
+        assertNotNull(Util.cloneAndRemoveDeleted(cf, Integer.MAX_VALUE));
     }
 
     private void validateRemoveTwoSources(DecoratedKey dk) throws IOException
     {
         ColumnFamilyStore store = Table.open("Keyspace1").getColumnFamilyStore("Super1");
-        ColumnFamily resolved = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super1"), "SC1".getBytes()));
-        assert ((TimestampClock)resolved.getSortedColumns().iterator().next().getMarkedForDeleteAt()).timestamp() == 1 : resolved;
-        assert resolved.getSortedColumns().iterator().next().getSubColumns().size() == 0 : resolved;
-        assertNull(ColumnFamilyStore.removeDeleted(resolved, Integer.MAX_VALUE));
-        assertNull(store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super1"), "SC1".getBytes()), Integer.MAX_VALUE));
-        assertNull(store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Super1")), Integer.MAX_VALUE));
-        assertNull(ColumnFamilyStore.removeDeleted(store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Super1"))), Integer.MAX_VALUE));
+        ColumnFamily cf = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super1"), "SC1".getBytes()));
+        assert ((TimestampClock)cf.getSortedColumns().iterator().next().getMarkedForDeleteAt()).timestamp() == 1 : cf;
+        assert cf.getSortedColumns().iterator().next().getSubColumns().size() == 0 : cf;
+        assertNull(Util.cloneAndRemoveDeleted(cf, Integer.MAX_VALUE));
+        cf = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super1"), "SC1".getBytes()));
+        assertNull(Util.cloneAndRemoveDeleted(cf, Integer.MAX_VALUE));
+        cf = store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Super1")));
+        assertNull(Util.cloneAndRemoveDeleted(cf, Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(store.getColumnFamily(QueryFilter.getIdentityFilter(dk, new QueryPath("Super1"))), Integer.MAX_VALUE));
     }
 
     private void validateRemoveCompacted(DecoratedKey dk) throws IOException
@@ -154,8 +158,8 @@ public class RemoveSuperColumnTest exten
     private void validateRemoveWithNewData(DecoratedKey dk) throws IOException
     {
         ColumnFamilyStore store = Table.open("Keyspace1").getColumnFamilyStore("Super2");
-        ColumnFamily resolved = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super2", "SC1".getBytes()), getBytes(2)), Integer.MAX_VALUE);
-        Collection<IColumn> subColumns = resolved.getSortedColumns().iterator().next().getSubColumns();
+        ColumnFamily cf = store.getColumnFamily(QueryFilter.getNamesFilter(dk, new QueryPath("Super2", "SC1".getBytes()), getBytes(2)));
+        Collection<IColumn> subColumns = cf.getSortedColumns().iterator().next().getSubColumns();
         assert subColumns.size() == 1;
         assert ((TimestampClock)subColumns.iterator().next().clock()).timestamp() == 2;
     }
@@ -176,7 +180,7 @@ public class RemoveSuperColumnTest exten
         rm = new RowMutation("Keyspace1", key.key);
         rm.delete(new QueryPath("Super2", "SC1".getBytes()), new TimestampClock(1));
         rm.apply();
-        assertNull(store.getColumnFamily(QueryFilter.getNamesFilter(key, new QueryPath("Super2"), "SC1".getBytes()), Integer.MAX_VALUE));
+        assertNull(Util.cloneAndRemoveDeleted(store.getColumnFamily(QueryFilter.getNamesFilter(key, new QueryPath("Super2"), "SC1".getBytes())), Integer.MAX_VALUE));
 
         // resurrect
         rm = new RowMutation("Keyspace1", key.key);
@@ -184,8 +188,9 @@ public class RemoveSuperColumnTest exten
         rm.apply();
 
         // validate
-        ColumnFamily resolved = store.getColumnFamily(QueryFilter.getNamesFilter(key, new QueryPath("Super2"), "SC1".getBytes()), Integer.MAX_VALUE);
-        Collection<IColumn> subColumns = resolved.getSortedColumns().iterator().next().getSubColumns();
+        ColumnFamily cf = store.getColumnFamily(QueryFilter.getNamesFilter(key, new QueryPath("Super2"), "SC1".getBytes()));
+        cf = Util.cloneAndRemoveDeleted(cf, Integer.MAX_VALUE);
+        Collection<IColumn> subColumns = cf.getSortedColumns().iterator().next().getSubColumns();
         assert subColumns.size() == 1;
         assert ((TimestampClock)subColumns.iterator().next().clock()).timestamp() == 2;
     }