Author: jbellis
Date: Wed Nov  4 17:07:49 2009
New Revision: 832798

URL: http://svn.apache.org/viewvc?rev=832798&view=rev
Log:
return clones of supercolumns from memtable so caller can't accidentally mutate 
them, fixing the failing test.

convert removeDeleted on SC to remove-oriented instead of clone-then-add-back 
to make this hurt performance less.

patch by jbellis; reviewed by gdusbabek for CASSANDRA-510

Modified:
    
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
    incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java
    incubator/cassandra/trunk/src/java/org/apache/cassandra/db/SuperColumn.java
    
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java
    
incubator/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java

Modified: 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
URL: 
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java?rev=832798&r1=832797&r2=832798&view=diff
==============================================================================
--- 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
 (original)
+++ 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
 Wed Nov  4 17:07:49 2009
@@ -499,45 +499,55 @@
             return null;
         }
 
+        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())
         {
             IColumn c = cf.getColumnsMap().get(cname);
-            if (c instanceof SuperColumn)
+            if ((c.isMarkedForDelete() && c.getLocalDeletionTime() <= gcBefore)
+                || c.timestamp() <= cf.getMarkedForDeleteAt())
             {
-                long minTimestamp = Math.max(c.getMarkedForDeleteAt(), 
cf.getMarkedForDeleteAt());
-                // don't operate directly on the supercolumn, it could be the 
one in the memtable.
-                // instead, create a new SC and add in the subcolumns that 
qualify.
                 cf.remove(cname);
-                SuperColumn sc = ((SuperColumn)c).cloneMeShallow();
-                for (IColumn subColumn : c.getSubColumns())
-                {
-                    if (subColumn.timestamp() > minTimestamp)
-                    {
-                        if (!subColumn.isMarkedForDelete() || 
subColumn.getLocalDeletionTime() > gcBefore)
-                        {
-                            sc.addColumn(subColumn);
-                        }
-                    }
-                }
-                if (sc.getSubColumns().size() > 0 || sc.getLocalDeletionTime() 
> gcBefore)
+            }
+        }
+    }
+
+    private static void removeDeletedSuper(ColumnFamily cf, int gcBefore)
+    {
+        // 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())
+        {
+            IColumn c = cf.getColumnsMap().get(cname);
+            long minTimestamp = Math.max(c.getMarkedForDeleteAt(), 
cf.getMarkedForDeleteAt());
+            for (IColumn subColumn : c.getSubColumns())
+            {
+                if (subColumn.timestamp() <= minTimestamp
+                    || (subColumn.isMarkedForDelete() && 
subColumn.getLocalDeletionTime() <= gcBefore))
                 {
-                    cf.addColumn(sc);
+                    ((SuperColumn)c).remove(subColumn.name());
                 }
             }
-            else if ((c.isMarkedForDelete() && c.getLocalDeletionTime() <= 
gcBefore)
-                     || c.timestamp() <= cf.getMarkedForDeleteAt())
+            if (c.getSubColumns().isEmpty() && c.getLocalDeletionTime() <= 
gcBefore)
             {
-                cf.remove(cname);
+                cf.remove(c.name());
             }
         }
-
-        if (cf.getColumnCount() == 0 && cf.getLocalDeletionTime() <= gcBefore)
-        {
-            return null;
-        }
-        return cf;
     }
 
     /*

Modified: 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java
URL: 
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java?rev=832798&r1=832797&r2=832798&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java 
(original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/db/Memtable.java 
Wed Nov  4 17:07:49 2009
@@ -262,7 +262,8 @@
         if (filter.reversed)
             ArrayUtils.reverse(columns);
         IColumn startIColumn;
-        if (DatabaseDescriptor.getColumnFamilyType(table_, 
filter.getColumnFamilyName()).equals("Standard"))
+        final boolean isStandard = 
DatabaseDescriptor.getColumnFamilyType(table_, 
filter.getColumnFamilyName()).equals("Standard");
+        if (isStandard)
             startIColumn = new Column(filter.start);
         else
             startIColumn = new SuperColumn(filter.start, null); // ok to not 
have subcolumnComparator since we won't be adding columns to this object
@@ -298,7 +299,8 @@
 
             public IColumn next()
             {
-                return columns[curIndex_++];
+                // clone supercolumns so caller can freely removeDeleted or 
otherwise mutate it
+                return isStandard ? columns[curIndex_++] : 
((SuperColumn)columns[curIndex_++]).cloneMe();
             }
         };
     }
@@ -307,6 +309,7 @@
     {
         final ColumnFamily cf = 
columnFamilies_.get(partitioner_.decorateKey(filter.key));
         final ColumnFamily columnFamily = cf == null ? 
ColumnFamily.create(table_, filter.getColumnFamilyName()) : cf.cloneMeShallow();
+        final boolean isStandard = 
DatabaseDescriptor.getColumnFamilyType(table_, 
filter.getColumnFamilyName()).equals("Standard");
 
         return new SimpleAbstractColumnIterator()
         {
@@ -329,7 +332,8 @@
                     current = iter.next();
                     IColumn column = cf.getColumn(current);
                     if (column != null)
-                        return column;
+                        // clone supercolumns so caller can freely 
removeDeleted or otherwise mutate it
+                        return isStandard ? column : 
((SuperColumn)column).cloneMe();
                 }
                 return endOfData();
             }

Modified: 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/SuperColumn.java
URL: 
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/SuperColumn.java?rev=832798&r1=832797&r2=832798&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/db/SuperColumn.java 
(original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/db/SuperColumn.java 
Wed Nov  4 17:07:49 2009
@@ -49,10 +49,15 @@
 
     SuperColumn(byte[] name, AbstractType comparator)
     {
+        this(name, new ConcurrentSkipListMap<byte[], IColumn>(comparator));
+    }
+
+    private SuperColumn(byte[] name, ConcurrentSkipListMap<byte[], IColumn> 
columns)
+    {
         assert name != null;
         assert name.length <= IColumn.MAX_NAME_LENGTH;
        name_ = name;
-        columns_ = new ConcurrentSkipListMap<byte[], IColumn>(comparator);
+        columns_ = columns;
     }
 
     public AbstractType getComparator()
@@ -67,6 +72,14 @@
         return sc;
     }
 
+
+    public IColumn cloneMe()
+    {
+        SuperColumn sc = new SuperColumn(name_, new 
ConcurrentSkipListMap<byte[], IColumn>(columns_));
+        sc.markForDeleteAt(localDeletionTime, markedForDeleteAt);
+        return sc;
+    }
+
        public boolean isMarkedForDelete()
        {
                return markedForDeleteAt > Long.MIN_VALUE;

Modified: 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java
URL: 
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java?rev=832798&r1=832797&r2=832798&view=diff
==============================================================================
--- 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java
 (original)
+++ 
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/filter/SliceQueryFilter.java
 Wed Nov  4 17:07:49 2009
@@ -66,6 +66,8 @@
 
     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.
+        // this may be a poor assumption.
         SuperColumn scFiltered = superColumn.cloneMeShallow();
         Iterator<IColumn> subcolumns;
         if (reversed)

Modified: 
incubator/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java
URL: 
http://svn.apache.org/viewvc/incubator/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java?rev=832798&r1=832797&r2=832798&view=diff
==============================================================================
--- 
incubator/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java
 (original)
+++ 
incubator/cassandra/trunk/test/unit/org/apache/cassandra/db/RemoveSuperColumnTest.java
 Wed Nov  4 17:07:49 2009
@@ -100,8 +100,8 @@
     {
         ColumnFamilyStore store = 
Table.open("Keyspace1").getColumnFamilyStore("Super1");
         ColumnFamily resolved = store.getColumnFamily(new 
NamesQueryFilter("key1", new QueryPath("Super1"), "SC1".getBytes()));
-        assert 
resolved.getSortedColumns().iterator().next().getMarkedForDeleteAt() == 1;
-        assert 
resolved.getSortedColumns().iterator().next().getSubColumns().size() == 0;
+        assert 
resolved.getSortedColumns().iterator().next().getMarkedForDeleteAt() == 1 : 
resolved;
+        assert 
resolved.getSortedColumns().iterator().next().getSubColumns().size() == 0 : 
resolved;
         assertNull(ColumnFamilyStore.removeDeleted(resolved, 
Integer.MAX_VALUE));
         assertNull(store.getColumnFamily(new NamesQueryFilter("key1", new 
QueryPath("Super1"), "SC1".getBytes()), Integer.MAX_VALUE));
         assertNull(store.getColumnFamily(new IdentityQueryFilter("key1", new 
QueryPath("Super1")), Integer.MAX_VALUE));


Reply via email to