diff --git a/src/java/org/apache/cassandra/db/Column.java b/src/java/org/apache/cassandra/db/Column.java index 74d70bb2d7..5d7f5c8538 100644 --- a/src/java/org/apache/cassandra/db/Column.java +++ b/src/java/org/apache/cassandra/db/Column.java @@ -198,6 +198,17 @@ public final class Column implements IColumn assert isMarkedForDelete; return ByteBuffer.wrap(value).getInt(); } + + // note that we do not call this simply compareTo since it also makes sense to compare Columns by name + public long comparePriority(Column o) + { + if (isMarkedForDelete) + { + // tombstone always wins ties. + return timestamp < o.timestamp ? -1 : 1; + } + return timestamp - o.timestamp; + } } class ColumnSerializer implements ICompactSerializer2 @@ -302,5 +313,6 @@ class ColumnSerializer implements ICompactSerializer2 int size = dis.readInt(); dis.skip(size); } + } diff --git a/src/java/org/apache/cassandra/db/ColumnFamily.java b/src/java/org/apache/cassandra/db/ColumnFamily.java index e33ab63c5d..9b1dd0ac4f 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamily.java +++ b/src/java/org/apache/cassandra/db/ColumnFamily.java @@ -266,7 +266,7 @@ public final class ColumnFamily } else { - if (oldColumn.timestamp() <= column.timestamp()) + if (((Column)oldColumn).comparePriority((Column)column) <= 0) { columns_.put(name, column); size_.addAndGet(column.size()); diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index 6c11d2c040..c9135ff7f1 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -615,19 +615,25 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean if (cf == null) return null; + // in case of a timestamp tie, tombstones get priority over non-tombstones. + // we want this to be deterministic in general to avoid confusion; + // either way (tombstone or non- getting priority) would be fine, + // but we picked this way because it makes removing delivered hints + // easier for HintedHandoffManager. for (String cname : new ArrayList(cf.getColumns().keySet())) { IColumn c = cf.getColumns().get(cname); if (c instanceof SuperColumn) { long minTimestamp = Math.max(c.getMarkedForDeleteAt(), cf.getMarkedForDeleteAt()); - // don't operate directly on the supercolumn, it could be the one in the memtable + // 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 = new SuperColumn(cname); sc.markForDeleteAt(c.getLocalDeletionTime(), c.getMarkedForDeleteAt()); for (IColumn subColumn : c.getSubColumns()) { - if (subColumn.timestamp() >= minTimestamp) + if (subColumn.timestamp() > minTimestamp) { if (!subColumn.isMarkedForDelete() || subColumn.getLocalDeletionTime() > gcBefore) { diff --git a/src/java/org/apache/cassandra/db/SuperColumn.java b/src/java/org/apache/cassandra/db/SuperColumn.java index 09b7cf727f..d0bdd766f8 100644 --- a/src/java/org/apache/cassandra/db/SuperColumn.java +++ b/src/java/org/apache/cassandra/db/SuperColumn.java @@ -176,8 +176,8 @@ public final class SuperColumn implements IColumn, Serializable public void addColumn(String name, IColumn column) { - if ( column instanceof SuperColumn ) - throw new UnsupportedOperationException("A super column cannot hold other super columns."); + if (!(column instanceof Column)) + throw new UnsupportedOperationException("A super column can only contain simple columns."); IColumn oldColumn = columns_.get(name); if ( oldColumn == null ) { @@ -186,7 +186,7 @@ public final class SuperColumn implements IColumn, Serializable } else { - if ( oldColumn.timestamp() <= column.timestamp() ) + if (((Column)oldColumn).comparePriority((Column)column) <= 0) { columns_.put(name, column); int delta = (-1)*oldColumn.size();