You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ma...@apache.org on 2014/10/02 10:50:39 UTC

[2/3] git commit: Merge branch 'cassandra-2.0' into cassandra-2.1

Merge branch 'cassandra-2.0' into cassandra-2.1

Conflicts:
	src/java/org/apache/cassandra/db/ColumnFamily.java
	src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java
	src/java/org/apache/cassandra/io/sstable/SSTableWriter.java


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/97b1d9f6
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/97b1d9f6
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/97b1d9f6

Branch: refs/heads/trunk
Commit: 97b1d9f6322067864146d0bb92afa1271c9cc4ac
Parents: b701dd5 7110d98
Author: Marcus Eriksson <ma...@apache.org>
Authored: Thu Oct 2 10:39:31 2014 +0200
Committer: Marcus Eriksson <ma...@apache.org>
Committed: Thu Oct 2 10:40:40 2014 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../org/apache/cassandra/db/ColumnFamily.java   |  32 ++--
 .../db/compaction/LazilyCompactedRow.java       |  36 +++--
 .../cassandra/io/sstable/ColumnStats.java       |  67 ++++++++
 .../cassandra/io/sstable/SSTableWriter.java     |  32 ++--
 .../cql3/SSTableMetadataTrackingTest.java       | 160 +++++++++++++++++++
 .../apache/cassandra/db/RangeTombstoneTest.java |  94 +++++++++++
 7 files changed, 385 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/97b1d9f6/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index d83b86f,c9b4e59..d3a0ba2
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,58 -1,6 +1,59 @@@
 -2.0.11:
 +2.1.1
 + * Make native server start block until properly bound (CASSANDRA-7885)
 + * (cqlsh) Fix IPv6 support (CASSANDRA-7988)
 + * Ignore fat clients when checking for endpoint collision (CASSANDRA-7939)
 + * Make sstablerepairedset take a list of files (CASSANDRA-7995)
 + * (cqlsh) Tab completeion for indexes on map keys (CASSANDRA-7972)
 + * (cqlsh) Fix UDT field selection in select clause (CASSANDRA-7891)
 + * Fix resource leak in event of corrupt sstable
 + * (cqlsh) Add command line option for cqlshrc file path (CASSANDRA-7131)
 + * Provide visibility into prepared statements churn (CASSANDRA-7921, CASSANDRA-7930)
 + * Invalidate prepared statements when their keyspace or table is
 +   dropped (CASSANDRA-7566)
 + * cassandra-stress: fix support for NetworkTopologyStrategy (CASSANDRA-7945)
 + * Fix saving caches when a table is dropped (CASSANDRA-7784)
 + * Add better error checking of new stress profile (CASSANDRA-7716)
 + * Use ThreadLocalRandom and remove FBUtilities.threadLocalRandom (CASSANDRA-7934)
 + * Prevent operator mistakes due to simultaneous bootstrap (CASSANDRA-7069)
 + * cassandra-stress supports whitelist mode for node config (CASSANDRA-7658)
 + * GCInspector more closely tracks GC; cassandra-stress and nodetool report it (CASSANDRA-7916)
 + * nodetool won't output bogus ownership info without a keyspace (CASSANDRA-7173)
 + * Add human readable option to nodetool commands (CASSANDRA-5433)
 + * Don't try to set repairedAt on old sstables (CASSANDRA-7913)
 + * Add metrics for tracking PreparedStatement use (CASSANDRA-7719)
 + * (cqlsh) tab-completion for triggers (CASSANDRA-7824)
 + * (cqlsh) Support for query paging (CASSANDRA-7514)
 + * (cqlsh) Show progress of COPY operations (CASSANDRA-7789)
 + * Add syntax to remove multiple elements from a map (CASSANDRA-6599)
 + * Support non-equals conditions in lightweight transactions (CASSANDRA-6839)
 + * Add IF [NOT] EXISTS to create/drop triggers (CASSANDRA-7606)
 + * (cqlsh) Display the current logged-in user (CASSANDRA-7785)
 + * (cqlsh) Don't ignore CTRL-C during COPY FROM execution (CASSANDRA-7815)
 + * (cqlsh) Order UDTs according to cross-type dependencies in DESCRIBE
 +   output (CASSANDRA-7659)
 + * (cqlsh) Fix handling of CAS statement results (CASSANDRA-7671)
 + * (cqlsh) COPY TO/FROM improvements (CASSANDRA-7405)
 + * Support list index operations with conditions (CASSANDRA-7499)
 + * Add max live/tombstoned cells to nodetool cfstats output (CASSANDRA-7731)
 + * Validate IPv6 wildcard addresses properly (CASSANDRA-7680)
 + * (cqlsh) Error when tracing query (CASSANDRA-7613)
 + * Avoid IOOBE when building SyntaxError message snippet (CASSANDRA-7569)
 + * SSTableExport uses correct validator to create string representation of partition
 +   keys (CASSANDRA-7498)
 + * Avoid NPEs when receiving type changes for an unknown keyspace (CASSANDRA-7689)
 + * Add support for custom 2i validation (CASSANDRA-7575)
 + * Pig support for hadoop CqlInputFormat (CASSANDRA-6454)
 + * Add listen_interface and rpc_interface options (CASSANDRA-7417)
 + * Improve schema merge performance (CASSANDRA-7444)
 + * Adjust MT depth based on # of partition validating (CASSANDRA-5263)
 + * Optimise NativeCell comparisons (CASSANDRA-6755)
 + * Configurable client timeout for cqlsh (CASSANDRA-7516)
 + * Include snippet of CQL query near syntax error in messages (CASSANDRA-7111)
 + * Make repair -pr work with -local (CASSANDRA-7450)
 + * Fix error in sstableloader with -cph > 1 (CASSANDRA-8007)
 +Merged from 2.0:
   * Better validation of collection values (CASSANDRA-7833)
+  * Track min/max timestamps correctly (CASSANDRA-7969)
   * Fix possible overflow while sorting CL segments for replay (CASSANDRA-7992)
   * Increase nodetool Xmx (CASSANDRA-7956)
   * Archive any commitlog segments present at startup (CASSANDRA-6904)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/97b1d9f6/src/java/org/apache/cassandra/db/ColumnFamily.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/ColumnFamily.java
index cd96785,7edf825..38b39fe
--- a/src/java/org/apache/cassandra/db/ColumnFamily.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamily.java
@@@ -394,50 -397,62 +394,60 @@@ public abstract class ColumnFamily impl
          return cf1.diff(cf2);
      }
  
 -    public void resolve(ColumnFamily cf)
 -    {
 -        resolve(cf, HeapAllocator.instance);
 -    }
 -
 -    public void resolve(ColumnFamily cf, Allocator allocator)
 -    {
 -        // Row _does_ allow null CF objects :(  seems a necessary evil for efficiency
 -        if (cf == null)
 -            return;
 -        addAll(cf, allocator);
 -    }
 -
      public ColumnStats getColumnStats()
      {
-         long minTimestampSeen = deletionInfo().isLive() ? Long.MAX_VALUE : deletionInfo().minTimestamp();
-         long maxTimestampSeen = deletionInfo().maxTimestamp();
+         // note that we default to MIN_VALUE/MAX_VALUE here to be able to override them later in this method
+         // we are checking row/range tombstones and actual cells - there should always be data that overrides
+         // these with actual values
+         ColumnStats.MinTracker<Long> minTimestampTracker = new ColumnStats.MinTracker<>(Long.MIN_VALUE);
+         ColumnStats.MaxTracker<Long> maxTimestampTracker = new ColumnStats.MaxTracker<>(Long.MAX_VALUE);
          StreamingHistogram tombstones = new StreamingHistogram(SSTable.TOMBSTONE_HISTOGRAM_BIN_SIZE);
-         int maxLocalDeletionTime = Integer.MIN_VALUE;
+         ColumnStats.MaxTracker<Integer> maxDeletionTimeTracker = new ColumnStats.MaxTracker<>(Integer.MAX_VALUE);
          List<ByteBuffer> minColumnNamesSeen = Collections.emptyList();
          List<ByteBuffer> maxColumnNamesSeen = Collections.emptyList();
 +        boolean hasLegacyCounterShards = false;
  
          if (deletionInfo().getTopLevelDeletion().localDeletionTime < Integer.MAX_VALUE)
+         {
              tombstones.update(deletionInfo().getTopLevelDeletion().localDeletionTime);
+             maxDeletionTimeTracker.update(deletionInfo().getTopLevelDeletion().localDeletionTime);
+             minTimestampTracker.update(deletionInfo().getTopLevelDeletion().markedForDeleteAt);
+             maxTimestampTracker.update(deletionInfo().getTopLevelDeletion().markedForDeleteAt);
+         }
          Iterator<RangeTombstone> it = deletionInfo().rangeIterator();
          while (it.hasNext())
          {
              RangeTombstone rangeTombstone = it.next();
              tombstones.update(rangeTombstone.getLocalDeletionTime());
-             minTimestampSeen = Math.min(minTimestampSeen, rangeTombstone.timestamp());
-             maxTimestampSeen = Math.max(maxTimestampSeen, rangeTombstone.timestamp());
 -            minTimestampTracker.update(rangeTombstone.minTimestamp());
 -            maxTimestampTracker.update(rangeTombstone.maxTimestamp());
++            minTimestampTracker.update(rangeTombstone.timestamp());
++            maxTimestampTracker.update(rangeTombstone.timestamp());
+             maxDeletionTimeTracker.update(rangeTombstone.getLocalDeletionTime());
              minColumnNamesSeen = ColumnNameHelper.minComponents(minColumnNamesSeen, rangeTombstone.min, metadata.comparator);
              maxColumnNamesSeen = ColumnNameHelper.maxComponents(maxColumnNamesSeen, rangeTombstone.max, metadata.comparator);
          }
  
 -        for (Column column : this)
 +        for (Cell cell : this)
          {
-             minTimestampSeen = Math.min(minTimestampSeen, cell.timestamp());
-             maxTimestampSeen = Math.max(maxTimestampSeen, cell.timestamp());
-             maxLocalDeletionTime = Math.max(maxLocalDeletionTime, cell.getLocalDeletionTime());
 -            minTimestampTracker.update(column.minTimestamp());
 -            maxTimestampTracker.update(column.maxTimestamp());
 -            maxDeletionTimeTracker.update(column.getLocalDeletionTime());
 -            int deletionTime = column.getLocalDeletionTime();
++            minTimestampTracker.update(cell.timestamp());
++            maxTimestampTracker.update(cell.timestamp());
++            maxDeletionTimeTracker.update(cell.getLocalDeletionTime());
++
 +            int deletionTime = cell.getLocalDeletionTime();
              if (deletionTime < Integer.MAX_VALUE)
                  tombstones.update(deletionTime);
 -            minColumnNamesSeen = ColumnNameHelper.minComponents(minColumnNamesSeen, column.name, metadata.comparator);
 -            maxColumnNamesSeen = ColumnNameHelper.maxComponents(maxColumnNamesSeen, column.name, metadata.comparator);
 +            minColumnNamesSeen = ColumnNameHelper.minComponents(minColumnNamesSeen, cell.name(), metadata.comparator);
 +            maxColumnNamesSeen = ColumnNameHelper.maxComponents(maxColumnNamesSeen, cell.name(), metadata.comparator);
 +            if (cell instanceof CounterCell)
 +                hasLegacyCounterShards = hasLegacyCounterShards || ((CounterCell) cell).hasLegacyShards();
          }
 -        return new ColumnStats(getColumnCount(), minTimestampTracker.get(), maxTimestampTracker.get(), maxDeletionTimeTracker.get(), tombstones, minColumnNamesSeen, maxColumnNamesSeen);
 +        return new ColumnStats(getColumnCount(),
-                                minTimestampSeen,
-                                maxTimestampSeen,
-                                maxLocalDeletionTime,
++                               minTimestampTracker.get(),
++                               maxTimestampTracker.get(),
++                               maxDeletionTimeTracker.get(),
 +                               tombstones,
 +                               minColumnNamesSeen,
 +                               maxColumnNamesSeen,
 +                               hasLegacyCounterShards);
      }
  
      public boolean isMarkedForDelete()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/97b1d9f6/src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java
index 0ae41ee,2757411..fa59dba
--- a/src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java
+++ b/src/java/org/apache/cassandra/db/compaction/LazilyCompactedRow.java
@@@ -118,14 -109,16 +118,14 @@@ public class LazilyCompactedRow extend
              throw new RuntimeException(e);
          }
          // reach into the reducer (created during iteration) to get column count, size, max column timestamp
 -        // (however, if there are zero columns, iterator() will not be called by ColumnIndexer and reducer will be null)
 -        columnStats = new ColumnStats(reducer == null ? 0 : reducer.columns,
 -                                      reducer == null ? Long.MAX_VALUE : reducer.minTimestampTracker.get(),
 -                                      reducer == null ? emptyColumnFamily.maxTimestamp() : Math.max(emptyColumnFamily.maxTimestamp(), reducer.maxTimestampTracker.get()),
 -                                      reducer == null ? Integer.MIN_VALUE : reducer.maxDeletionTimeTracker.get(),
 -                                      reducer == null ? new StreamingHistogram(SSTable.TOMBSTONE_HISTOGRAM_BIN_SIZE) : reducer.tombstones,
 -                                      reducer == null ? Collections.<ByteBuffer>emptyList() : reducer.minColumnNameSeen,
 -                                      reducer == null ? Collections.<ByteBuffer>emptyList() : reducer.maxColumnNameSeen
 -        );
 -        reducer = null;
 +        columnStats = new ColumnStats(reducer.columns,
-                                       reducer.minTimestampSeen,
-                                       Math.max(emptyColumnFamily.deletionInfo().maxTimestamp(), reducer.maxTimestampSeen),
-                                       reducer.maxLocalDeletionTimeSeen,
++                                      reducer.minTimestampTracker.get(),
++                                      Math.max(emptyColumnFamily.deletionInfo().maxTimestamp(), reducer.maxTimestampTracker.get()),
++                                      reducer.maxDeletionTimeTracker.get(),
 +                                      reducer.tombstones,
 +                                      reducer.minColumnNameSeen,
 +                                      reducer.maxColumnNameSeen,
 +                                      reducer.hasLegacyCounterShards);
  
          // in case no columns were ever written, we may still need to write an empty header with a top-level tombstone
          indexBuilder.maybeWriteEmptyRowHeader();
@@@ -201,8 -213,14 +207,15 @@@
          StreamingHistogram tombstones = new StreamingHistogram(SSTable.TOMBSTONE_HISTOGRAM_BIN_SIZE);
          List<ByteBuffer> minColumnNameSeen = Collections.emptyList();
          List<ByteBuffer> maxColumnNameSeen = Collections.emptyList();
 +        boolean hasLegacyCounterShards = false;
  
+         public Reducer()
+         {
+             minTimestampTracker.update(maxRowTombstone.isLive() ? Long.MAX_VALUE : maxRowTombstone.markedForDeleteAt);
+             maxTimestampTracker.update(maxRowTombstone.markedForDeleteAt);
+             maxDeletionTimeTracker.update(maxRowTombstone.isLive() ? Integer.MIN_VALUE : maxRowTombstone.localDeletionTime);
+         }
+ 
          /**
           * Called once per version of a cell that we need to merge, after which getReduced() is called.  In other words,
           * this will be called one or more times with cells that share the same column name.
@@@ -246,10 -267,12 +259,11 @@@
                  else
                  {
                      tombstones.update(t.getLocalDeletionTime());
-                     minTimestampSeen = Math.min(minTimestampSeen, t.timestamp());
-                     maxTimestampSeen = Math.max(maxTimestampSeen, t.timestamp());
 -                    minTimestampTracker.update(t.minTimestamp());
 -                    maxTimestampTracker.update(t.maxTimestamp());
++                    minTimestampTracker.update(t.timestamp());
++                    maxTimestampTracker.update(t.timestamp());
+                     maxDeletionTimeTracker.update(t.getLocalDeletionTime());
                      minColumnNameSeen = ColumnNameHelper.minComponents(minColumnNameSeen, t.min, controller.cfs.metadata.comparator);
                      maxColumnNameSeen = ColumnNameHelper.maxComponents(maxColumnNameSeen, t.max, controller.cfs.metadata.comparator);
 -
                      return t;
                  }
              }
@@@ -281,11 -297,13 +295,11 @@@
                      indexer.remove(reduced);
                      return null;
                  }
 -                int localDeletionTime = purged.deletionInfo().getTopLevelDeletion().localDeletionTime;
 -                if (localDeletionTime < Integer.MAX_VALUE)
 -                    tombstones.update(localDeletionTime);
 +
                  columns++;
-                 minTimestampSeen = Math.min(minTimestampSeen, reduced.timestamp());
-                 maxTimestampSeen = Math.max(maxTimestampSeen, reduced.timestamp());
-                 maxLocalDeletionTimeSeen = Math.max(maxLocalDeletionTimeSeen, reduced.getLocalDeletionTime());
 -                minTimestampTracker.update(reduced.minTimestamp());
 -                maxTimestampTracker.update(reduced.maxTimestamp());
++                minTimestampTracker.update(reduced.timestamp());
++                maxTimestampTracker.update(reduced.timestamp());
+                 maxDeletionTimeTracker.update(reduced.getLocalDeletionTime());
                  minColumnNameSeen = ColumnNameHelper.minComponents(minColumnNameSeen, reduced.name(), controller.cfs.metadata.comparator);
                  maxColumnNameSeen = ColumnNameHelper.maxComponents(maxColumnNameSeen, reduced.name(), controller.cfs.metadata.comparator);
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/97b1d9f6/src/java/org/apache/cassandra/io/sstable/ColumnStats.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/io/sstable/ColumnStats.java
index d09f965,446c41c..873aa09
--- a/src/java/org/apache/cassandra/io/sstable/ColumnStats.java
+++ b/src/java/org/apache/cassandra/io/sstable/ColumnStats.java
@@@ -59,6 -51,71 +60,72 @@@ public class ColumnStat
          this.tombstoneHistogram = tombstoneHistogram;
          this.minColumnNames = minColumnNames;
          this.maxColumnNames = maxColumnNames;
 +        this.hasLegacyCounterShards = hasLegacyCounterShards;
      }
+ 
+     public static class MinTracker<T extends Comparable<T>>
+     {
+         private final T defaultValue;
+         private boolean isSet = false;
+         private T value;
+ 
+         public MinTracker(T defaultValue)
+         {
+             this.defaultValue = defaultValue;
+         }
+ 
+         public void update(T value)
+         {
+             if (!isSet)
+             {
+                 this.value = value;
+                 isSet = true;
+             }
+             else
+             {
+                 if (value.compareTo(this.value) < 0)
+                     this.value = value;
+             }
+         }
+ 
+         public T get()
+         {
+             if (isSet)
+                 return value;
+             return defaultValue;
+         }
+     }
+ 
+     public static class MaxTracker<T extends Comparable<T>>
+     {
+         private final T defaultValue;
+         private boolean isSet = false;
+         private T value;
+ 
+         public MaxTracker(T defaultValue)
+         {
+             this.defaultValue = defaultValue;
+         }
+ 
+         public void update(T value)
+         {
+             if (!isSet)
+             {
+                 this.value = value;
+                 isSet = true;
+             }
+             else
+             {
+                 if (value.compareTo(this.value) > 0)
+                     this.value = value;
+             }
+         }
+ 
+         public T get()
+         {
+             if (isSet)
+                 return value;
+             return defaultValue;
+         }
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/97b1d9f6/src/java/org/apache/cassandra/io/sstable/SSTableWriter.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/io/sstable/SSTableWriter.java
index bfb2445,4619ddc..9e861a1
--- a/src/java/org/apache/cassandra/io/sstable/SSTableWriter.java
+++ b/src/java/org/apache/cassandra/io/sstable/SSTableWriter.java
@@@ -251,24 -219,37 +250,29 @@@ public class SSTableWriter extends SSTa
          List<ByteBuffer> minColumnNames = Collections.emptyList();
          List<ByteBuffer> maxColumnNames = Collections.emptyList();
          StreamingHistogram tombstones = new StreamingHistogram(TOMBSTONE_HISTOGRAM_BIN_SIZE);
 -        ColumnFamily cf = ArrayBackedSortedColumns.factory.create(metadata);
 -
 -        // skip row size for version < ja
 -        if (version.hasRowSizeAndColumnCount)
 -            FileUtils.skipBytesFully(in, 8);
 +        boolean hasLegacyCounterShards = false;
  
 +        ColumnFamily cf = ArrayBackedSortedColumns.factory.create(metadata);
          cf.delete(DeletionTime.serializer.deserialize(in));
  
 -        ColumnIndex.Builder columnIndexer = new ColumnIndex.Builder(cf, key.key, dataFile.stream);
 -
 -        // read column count for version < ja
 -        int columnCount = Integer.MAX_VALUE;
 -        if (version.hasRowSizeAndColumnCount)
 -            columnCount = in.readInt();
 +        ColumnIndex.Builder columnIndexer = new ColumnIndex.Builder(cf, key.getKey(), dataFile.stream);
  
          if (cf.deletionInfo().getTopLevelDeletion().localDeletionTime < Integer.MAX_VALUE)
+         {
              tombstones.update(cf.deletionInfo().getTopLevelDeletion().localDeletionTime);
+             maxDeletionTimeTracker.update(cf.deletionInfo().getTopLevelDeletion().localDeletionTime);
+             minTimestampTracker.update(cf.deletionInfo().getTopLevelDeletion().markedForDeleteAt);
+             maxTimestampTracker.update(cf.deletionInfo().getTopLevelDeletion().markedForDeleteAt);
+         }
  
          Iterator<RangeTombstone> rangeTombstoneIterator = cf.deletionInfo().rangeIterator();
          while (rangeTombstoneIterator.hasNext())
          {
              RangeTombstone rangeTombstone = rangeTombstoneIterator.next();
              tombstones.update(rangeTombstone.getLocalDeletionTime());
-             minTimestamp = Math.min(minTimestamp, rangeTombstone.timestamp());
-             maxTimestamp = Math.max(maxTimestamp, rangeTombstone.timestamp());
- 
 -            minTimestampTracker.update(rangeTombstone.minTimestamp());
 -            maxTimestampTracker.update(rangeTombstone.maxTimestamp());
++            minTimestampTracker.update(rangeTombstone.timestamp());
++            maxTimestampTracker.update(rangeTombstone.timestamp());
+             maxDeletionTimeTracker.update(rangeTombstone.getLocalDeletionTime());
              minColumnNames = ColumnNameHelper.minComponents(minColumnNames, rangeTombstone.min, metadata.comparator);
              maxColumnNames = ColumnNameHelper.maxComponents(maxColumnNames, rangeTombstone.max, metadata.comparator);
          }
@@@ -292,12 -267,14 +294,12 @@@
  
                  int deletionTime = atom.getLocalDeletionTime();
                  if (deletionTime < Integer.MAX_VALUE)
 -                {
                      tombstones.update(deletionTime);
-                 minTimestamp = Math.min(minTimestamp, atom.timestamp());
-                 maxTimestamp = Math.max(maxTimestamp, atom.timestamp());
 -                }
 -                minTimestampTracker.update(atom.minTimestamp());
 -                maxTimestampTracker.update(atom.maxTimestamp());
++                minTimestampTracker.update(atom.timestamp());
++                maxTimestampTracker.update(atom.timestamp());
                  minColumnNames = ColumnNameHelper.minComponents(minColumnNames, atom.name(), metadata.comparator);
                  maxColumnNames = ColumnNameHelper.maxComponents(maxColumnNames, atom.name(), metadata.comparator);
-                 maxLocalDeletionTime = Math.max(maxLocalDeletionTime, atom.getLocalDeletionTime());
+                 maxDeletionTimeTracker.update(atom.getLocalDeletionTime());
  
                  columnIndexer.add(atom); // This write the atom on disk too
              }
@@@ -310,15 -287,14 +312,15 @@@
              throw new FSWriteError(e, dataFile.getPath());
          }
  
-         sstableMetadataCollector.updateMinTimestamp(minTimestamp)
-                                 .updateMaxTimestamp(maxTimestamp)
-                                 .updateMaxLocalDeletionTime(maxLocalDeletionTime)
 -        sstableMetadataCollector.updateMinTimestamp(minTimestampTracker.get());
 -        sstableMetadataCollector.updateMaxTimestamp(maxTimestampTracker.get());
 -        sstableMetadataCollector.updateMaxLocalDeletionTime(maxDeletionTimeTracker.get());
 -        sstableMetadataCollector.addRowSize(dataFile.getFilePointer() - currentPosition);
 -        sstableMetadataCollector.addColumnCount(columnIndexer.writtenAtomCount());
 -        sstableMetadataCollector.mergeTombstoneHistogram(tombstones);
 -        sstableMetadataCollector.updateMinColumnNames(minColumnNames);
 -        sstableMetadataCollector.updateMaxColumnNames(maxColumnNames);
++        sstableMetadataCollector.updateMinTimestamp(minTimestampTracker.get())
++                                .updateMaxTimestamp(maxTimestampTracker.get())
++                                .updateMaxLocalDeletionTime(maxDeletionTimeTracker.get())
 +                                .addRowSize(dataFile.getFilePointer() - currentPosition)
 +                                .addColumnCount(columnIndexer.writtenAtomCount())
 +                                .mergeTombstoneHistogram(tombstones)
 +                                .updateMinColumnNames(minColumnNames)
 +                                .updateMaxColumnNames(maxColumnNames)
 +                                .updateHasLegacyCounterShards(hasLegacyCounterShards);
          afterAppend(key, currentPosition, RowIndexEntry.create(currentPosition, cf.deletionInfo().getTopLevelDeletion(), columnIndexer.build()));
          return currentPosition;
      }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/97b1d9f6/test/unit/org/apache/cassandra/cql3/SSTableMetadataTrackingTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/SSTableMetadataTrackingTest.java
index 0000000,9104269..7c3965f
mode 000000,100644..100644
--- a/test/unit/org/apache/cassandra/cql3/SSTableMetadataTrackingTest.java
+++ b/test/unit/org/apache/cassandra/cql3/SSTableMetadataTrackingTest.java
@@@ -1,0 -1,223 +1,160 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.cassandra.cql3;
+ 
 -import org.junit.AfterClass;
 -import org.junit.Before;
 -import org.junit.BeforeClass;
+ import org.junit.Test;
+ 
 -import org.apache.cassandra.SchemaLoader;
+ import org.apache.cassandra.db.ColumnFamilyStore;
 -import org.apache.cassandra.db.ConsistencyLevel;
+ import org.apache.cassandra.db.Keyspace;
 -import org.apache.cassandra.gms.Gossiper;
 -import org.apache.cassandra.io.sstable.SSTableMetadata;
 -import org.apache.cassandra.service.ClientState;
 -import static org.apache.cassandra.cql3.QueryProcessor.process;
 -import static org.apache.cassandra.cql3.QueryProcessor.processInternal;
++import org.apache.cassandra.io.sstable.metadata.StatsMetadata;
+ import static org.junit.Assert.assertEquals;
+ 
 -public class SSTableMetadataTrackingTest
++public class SSTableMetadataTrackingTest extends CQLTester
+ {
 -    private static String keyspace = "sstable_metadata_tracking_test";
 -    private static ClientState clientState;
 -
 -    @BeforeClass
 -    public static void setup() throws Throwable
 -    {
 -        SchemaLoader.loadSchema();
 -        createKeyspace("CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}");
 -        clientState = ClientState.forInternalCalls();
 -    }
 -
 -    
+     @Test
+     public void baseCheck() throws Throwable
+     {
 -        createTable("CREATE TABLE %s.basecheck (a int, b int, c text, PRIMARY KEY (a, b))");
 -        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore("basecheck");
 -        execute("INSERT INTO %s.basecheck (a,b,c) VALUES (1,1,'1') using timestamp 9999");
++        createTable("CREATE TABLE %s (a int, b int, c text, PRIMARY KEY (a, b))");
++        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable());
++        execute("INSERT INTO %s (a,b,c) VALUES (1,1,'1') using timestamp 9999");
+         cfs.forceBlockingFlush();
 -        SSTableMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(Integer.MAX_VALUE, metadata.maxLocalDeletionTime);
+         cfs.forceMajorCompaction();
+         metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(Integer.MAX_VALUE, metadata.maxLocalDeletionTime);
+     }
+ 
+     @Test
+     public void testMinMaxtimestampRange() throws Throwable
+     {
 -        createTable("CREATE TABLE %s.minmaxtsrange (a int, b int, c text, PRIMARY KEY (a, b))");
 -        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore("minmaxtsrange");
 -        execute("INSERT INTO %s.minmaxtsrange (a,b,c) VALUES (1,1,'1') using timestamp 10000");
 -        execute("DELETE FROM %s.minmaxtsrange USING TIMESTAMP 9999 WHERE a = 1 and b = 1");
++        createTable("CREATE TABLE %s (a int, b int, c text, PRIMARY KEY (a, b))");
++        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable());
++        execute("INSERT INTO %s (a,b,c) VALUES (1,1,'1') using timestamp 10000");
++        execute("DELETE FROM %s USING TIMESTAMP 9999 WHERE a = 1 and b = 1");
+         cfs.forceBlockingFlush();
 -        SSTableMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(10000, metadata.maxTimestamp);
+         assertEquals(Integer.MAX_VALUE, metadata.maxLocalDeletionTime, 5);
+         cfs.forceMajorCompaction();
+         metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(10000, metadata.maxTimestamp);
+         assertEquals(Integer.MAX_VALUE, metadata.maxLocalDeletionTime, 5);
+     }
+ 
+     @Test
+     public void testMinMaxtimestampRow() throws Throwable
+     {
 -        createTable("CREATE TABLE %s.minmaxtsrow (a int, b int, c text, PRIMARY KEY (a, b))");
 -        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore("minmaxtsrow");
 -        execute("INSERT INTO %s.minmaxtsrow (a,b,c) VALUES (1,1,'1') using timestamp 10000");
 -        execute("DELETE FROM %s.minmaxtsrow USING TIMESTAMP 9999 WHERE a = 1");
++        createTable("CREATE TABLE %s (a int, b int, c text, PRIMARY KEY (a, b))");
++        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable());
++        execute("INSERT INTO %s (a,b,c) VALUES (1,1,'1') using timestamp 10000");
++        execute("DELETE FROM %s USING TIMESTAMP 9999 WHERE a = 1");
+         cfs.forceBlockingFlush();
 -        SSTableMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(10000, metadata.maxTimestamp);
+         assertEquals(Integer.MAX_VALUE, metadata.maxLocalDeletionTime, 5);
+         cfs.forceMajorCompaction();
+         metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(10000, metadata.maxTimestamp);
+         assertEquals(Integer.MAX_VALUE, metadata.maxLocalDeletionTime, 5);
+     }
+ 
+ 
+     @Test
+     public void testTrackMetadata_rangeTombstone() throws Throwable
+     {
 -        createTable("CREATE TABLE %s.rangetombstone (a int, b int, c text, PRIMARY KEY (a, b)) WITH gc_grace_seconds = 10000");
 -        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore("rangetombstone");
 -        execute("DELETE FROM %s.rangetombstone USING TIMESTAMP 9999 WHERE a = 1 and b = 1");
++        createTable("CREATE TABLE %s (a int, b int, c text, PRIMARY KEY (a, b)) WITH gc_grace_seconds = 10000");
++        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable());
++        execute("DELETE FROM %s USING TIMESTAMP 9999 WHERE a = 1 and b = 1");
+         cfs.forceBlockingFlush();
+         assertEquals(1, cfs.getSSTables().size());
 -        SSTableMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(9999, metadata.maxTimestamp);
+         assertEquals(System.currentTimeMillis()/1000, metadata.maxLocalDeletionTime, 5);
+         cfs.forceMajorCompaction();
 -        SSTableMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(metadata.maxLocalDeletionTime, metadata2.maxLocalDeletionTime);
+         assertEquals(metadata.minTimestamp, metadata2.minTimestamp);
+         assertEquals(metadata.maxTimestamp, metadata2.maxTimestamp);
+     }
+ 
+     @Test
+     public void testTrackMetadata_rowTombstone() throws Throwable
+     {
 -        createTable("CREATE TABLE %s.rowtombstone (a int, b int, c text, PRIMARY KEY (a, b))");
 -        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore("rowtombstone");
 -        execute("DELETE FROM %s.rowtombstone USING TIMESTAMP 9999 WHERE a = 1");
++        createTable("CREATE TABLE %s (a int, b int, c text, PRIMARY KEY (a, b))");
++        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable());
++        execute("DELETE FROM %s USING TIMESTAMP 9999 WHERE a = 1");
+ 
+         cfs.forceBlockingFlush();
+         assertEquals(1, cfs.getSSTables().size());
 -        SSTableMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(9999, metadata.maxTimestamp);
+         assertEquals(System.currentTimeMillis()/1000, metadata.maxLocalDeletionTime, 5);
+         cfs.forceMajorCompaction();
 -        SSTableMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(metadata.maxLocalDeletionTime, metadata2.maxLocalDeletionTime);
+         assertEquals(metadata.minTimestamp, metadata2.minTimestamp);
+         assertEquals(metadata.maxTimestamp, metadata2.maxTimestamp);
+     }
+ 
+     @Test
+     public void testTrackMetadata_rowMarker() throws Throwable
+     {
 -        createTable("CREATE TABLE %s.rowmarker (a int, PRIMARY KEY (a))");
 -        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore("rowmarker");
 -        execute("INSERT INTO %s.rowmarker (a) VALUES (1) USING TIMESTAMP 9999");
++        createTable("CREATE TABLE %s (a int, PRIMARY KEY (a))");
++        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable());
++        execute("INSERT INTO %s (a) VALUES (1) USING TIMESTAMP 9999");
+ 
+         cfs.forceBlockingFlush();
+         assertEquals(1, cfs.getSSTables().size());
 -        SSTableMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(9999, metadata.maxTimestamp);
+         assertEquals(Integer.MAX_VALUE, metadata.maxLocalDeletionTime);
+         cfs.forceMajorCompaction();
 -        SSTableMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(metadata.maxLocalDeletionTime, metadata2.maxLocalDeletionTime);
+         assertEquals(metadata.minTimestamp, metadata2.minTimestamp);
+         assertEquals(metadata.maxTimestamp, metadata2.maxTimestamp);
+     }
+     @Test
+     public void testTrackMetadata_rowMarkerDelete() throws Throwable
+     {
 -        createTable("CREATE TABLE %s.rowmarkerdel (a int, PRIMARY KEY (a))");
 -        ColumnFamilyStore cfs = Keyspace.open(keyspace).getColumnFamilyStore("rowmarkerdel");
 -        execute("DELETE FROM %s.rowmarkerdel USING TIMESTAMP 9999 WHERE a=1");
++        createTable("CREATE TABLE %s (a int, PRIMARY KEY (a))");
++        ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(currentTable());
++        execute("DELETE FROM %s USING TIMESTAMP 9999 WHERE a=1");
+         cfs.forceBlockingFlush();
+         assertEquals(1, cfs.getSSTables().size());
 -        SSTableMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(9999, metadata.minTimestamp);
+         assertEquals(9999, metadata.maxTimestamp);
+         assertEquals(System.currentTimeMillis()/1000, metadata.maxLocalDeletionTime, 5);
+         cfs.forceMajorCompaction();
 -        SSTableMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
++        StatsMetadata metadata2 = cfs.getSSTables().iterator().next().getSSTableMetadata();
+         assertEquals(metadata.maxLocalDeletionTime, metadata2.maxLocalDeletionTime);
+         assertEquals(metadata.minTimestamp, metadata2.minTimestamp);
+         assertEquals(metadata.maxTimestamp, metadata2.maxTimestamp);
+     }
 -
 -    @AfterClass
 -    public static void stopGossiper()
 -    {
 -        Gossiper.instance.stop();
 -    }
 -
 -    private static void createKeyspace(String query) throws Throwable
 -    {
 -        try
 -        {
 -            process(String.format(query, keyspace), ConsistencyLevel.ONE);
 -        } catch (RuntimeException exc)
 -        {
 -            throw exc.getCause();
 -        }
 -    }
 -
 -
 -    private void createTable(String query) throws Throwable
 -    {
 -        try
 -        {
 -            process(String.format(query, keyspace), ConsistencyLevel.ONE);
 -        } catch (RuntimeException exc)
 -        {
 -            throw exc.getCause();
 -        }
 -    }
 -
 -    private UntypedResultSet execute(String query) throws Throwable
 -    {
 -        try
 -        {
 -            return processInternal(String.format(query, keyspace));
 -        } catch (RuntimeException exc)
 -        {
 -            if (exc.getCause() != null)
 -                throw exc.getCause();
 -            throw exc;
 -        }
 -    }
+ }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/97b1d9f6/test/unit/org/apache/cassandra/db/RangeTombstoneTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/db/RangeTombstoneTest.java
index 45fd61f,4dc7c0b..e266bf6
--- a/test/unit/org/apache/cassandra/db/RangeTombstoneTest.java
+++ b/test/unit/org/apache/cassandra/db/RangeTombstoneTest.java
@@@ -29,33 -24,24 +29,34 @@@ import java.util.TreeSet
  import java.util.concurrent.ExecutionException;
  
  import com.google.common.collect.ImmutableMap;
 +import com.google.common.collect.Iterators;
 +import org.junit.Test;
 +
 +import org.apache.cassandra.SchemaLoader;
 +import org.apache.cassandra.Util;
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.config.DatabaseDescriptor;
 +import org.apache.cassandra.config.IndexType;
  import org.apache.cassandra.db.columniterator.OnDiskAtomIterator;
 +import org.apache.cassandra.db.compaction.CompactionManager;
  import org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy;
 -import org.apache.cassandra.db.index.*;
 +import org.apache.cassandra.db.composites.CellName;
 +import org.apache.cassandra.db.composites.CellNames;
 +import org.apache.cassandra.db.composites.Composites;
 +import org.apache.cassandra.db.filter.ColumnSlice;
 +import org.apache.cassandra.db.filter.IDiskAtomFilter;
 +import org.apache.cassandra.db.filter.QueryFilter;
 +import org.apache.cassandra.db.filter.SliceQueryFilter;
 +import org.apache.cassandra.db.index.PerColumnSecondaryIndex;
 +import org.apache.cassandra.db.index.SecondaryIndex;
 +import org.apache.cassandra.db.index.SecondaryIndexSearcher;
 +import org.apache.cassandra.db.marshal.Int32Type;
  import org.apache.cassandra.exceptions.ConfigurationException;
 -import org.apache.cassandra.io.sstable.SSTableMetadata;
  import org.apache.cassandra.io.sstable.SSTableReader;
 -import org.apache.cassandra.thrift.IndexType;
 -
 -import org.junit.Ignore;
 -import org.junit.Test;
 -
 -import org.apache.cassandra.SchemaLoader;
 -import org.apache.cassandra.Util;
 -import org.apache.cassandra.db.compaction.CompactionManager;
 -import org.apache.cassandra.db.filter.*;
++import org.apache.cassandra.io.sstable.metadata.StatsMetadata;
  import org.apache.cassandra.utils.ByteBufferUtil;
 +import org.apache.cassandra.utils.concurrent.OpOrder;
 +import org.apache.cassandra.utils.memory.MemtableAllocator;
  
  import static org.apache.cassandra.Util.dk;
  import static org.junit.Assert.assertEquals;
@@@ -120,112 -106,104 +121,205 @@@ public class RangeTombstoneTest extend
          cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(7), b(30), false, Integer.MAX_VALUE, System.currentTimeMillis()));
  
          for (int i : new int[]{ 7, 8, 9, 11, 13, 15, 17, 28, 29, 30 })
 -            assert isLive(cf, cf.getColumn(b(i))) : "Column " + i + " should be live";
 +            assert isLive(cf, cf.getColumn(b(i))) : "Cell " + i + " should be live";
          for (int i : new int[]{ 10, 12, 14, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 })
 -            assert !isLive(cf, cf.getColumn(b(i))) : "Column " + i + " shouldn't be live";
 +            assert !isLive(cf, cf.getColumn(b(i))) : "Cell " + i + " shouldn't be live";
 +    }
 +
 +    @Test
 +    public void rangeTombstoneFilteringTest() throws Exception
 +    {
 +        CompactionManager.instance.disableAutoCompaction();
 +        Keyspace keyspace = Keyspace.open(KSNAME);
 +        ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CFNAME);
 +
 +        // Inserting data
 +        String key = "k111";
 +        Mutation rm;
 +        ColumnFamily cf;
 +
 +        rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
 +        for (int i = 0; i < 40; i += 2)
 +            add(rm, i, 0);
 +        rm.apply();
 +
 +        rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
 +        cf = rm.addOrGet(CFNAME);
 +        delete(cf, 5, 10, 1);
 +        rm.apply();
 +
 +        rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
 +        cf = rm.addOrGet(CFNAME);
 +        delete(cf, 15, 20, 2);
 +        rm.apply();
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(11), b(14), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        Collection<RangeTombstone> rt = rangeTombstones(cf);
 +        assertEquals(0, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(11), b(15), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(20), b(25), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(12), b(25), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(25), b(35), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(0, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(1), b(40), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(2, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(7), b(17), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(2, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(5), b(20), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(2, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(5), b(15), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(2, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(1), b(2), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(0, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(1), b(5), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(1), b(10), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(5), b(6), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(17), b(20), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        cf = cfs.getColumnFamily(QueryFilter.getSliceFilter(dk(key), CFNAME, b(17), b(18), false, Integer.MAX_VALUE, System.currentTimeMillis()));
 +        rt = rangeTombstones(cf);
 +        assertEquals(1, rt.size());
 +
 +        ColumnSlice[] slices = new ColumnSlice[]{new ColumnSlice( b(1), b(10)), new ColumnSlice( b(16), b(20))};
 +        IDiskAtomFilter sqf = new SliceQueryFilter(slices, false, Integer.MAX_VALUE);
 +        cf = cfs.getColumnFamily( new QueryFilter(dk(key), CFNAME, sqf, System.currentTimeMillis()) );
 +        rt = rangeTombstones(cf);
 +        assertEquals(2, rt.size());
 +    }
 +
 +    private Collection<RangeTombstone> rangeTombstones(ColumnFamily cf)
 +    {
 +        List<RangeTombstone> tombstones = new ArrayList<RangeTombstone>();
 +        Iterators.addAll(tombstones, cf.deletionInfo().rangeIterator());
 +        return tombstones;
      }
  
+     @Test
+     public void testTrackTimesRowTombstone() throws ExecutionException, InterruptedException
+     {
+         Keyspace ks = Keyspace.open(KSNAME);
+         ColumnFamilyStore cfs = ks.getColumnFamilyStore(CFNAME);
+         cfs.truncateBlocking();
+         String key = "rt_times";
 -        RowMutation rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key));
++        Mutation rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
+         ColumnFamily cf = rm.addOrGet(CFNAME);
+         long timestamp = System.currentTimeMillis();
+         cf.delete(new DeletionInfo(1000, (int)(timestamp/1000)));
+         rm.apply();
+         cfs.forceBlockingFlush();
+         SSTableReader sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 1000, 1000, (int)(timestamp/1000));
+         cfs.forceMajorCompaction();
+         sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 1000, 1000, (int)(timestamp/1000));
+     }
+ 
+     @Test
+     public void testTrackTimesRowTombstoneWithData() throws ExecutionException, InterruptedException
+     {
+         Keyspace ks = Keyspace.open(KSNAME);
+         ColumnFamilyStore cfs = ks.getColumnFamilyStore(CFNAME);
+         cfs.truncateBlocking();
+         String key = "rt_times";
 -        RowMutation rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key));
++        Mutation rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
+         add(rm, 5, 999);
+         rm.apply();
+         key = "rt_times2";
 -        rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key));
++        rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
+         ColumnFamily cf = rm.addOrGet(CFNAME);
+         int timestamp = (int)(System.currentTimeMillis()/1000);
+         cf.delete(new DeletionInfo(1000, timestamp));
+         rm.apply();
+         cfs.forceBlockingFlush();
+         SSTableReader sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 999, 1000, Integer.MAX_VALUE);
+         cfs.forceMajorCompaction();
+         sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 999, 1000, Integer.MAX_VALUE);
+     }
+     @Test
+     public void testTrackTimesRangeTombstone() throws ExecutionException, InterruptedException
+     {
+         Keyspace ks = Keyspace.open(KSNAME);
+         ColumnFamilyStore cfs = ks.getColumnFamilyStore(CFNAME);
+         cfs.truncateBlocking();
+         String key = "rt_times";
 -        RowMutation rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key));
++        Mutation rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
+         ColumnFamily cf = rm.addOrGet(CFNAME);
+         long timestamp = System.currentTimeMillis();
+         cf.delete(new DeletionInfo(b(1), b(2), cfs.getComparator(), 1000, (int)(timestamp/1000)));
+         rm.apply();
+         cfs.forceBlockingFlush();
+         SSTableReader sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 1000, 1000, (int)(timestamp/1000));
+         cfs.forceMajorCompaction();
+         sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 1000, 1000, (int)(timestamp/1000));
+     }
+ 
+     @Test
+     public void testTrackTimesRangeTombstoneWithData() throws ExecutionException, InterruptedException
+     {
+         Keyspace ks = Keyspace.open(KSNAME);
+         ColumnFamilyStore cfs = ks.getColumnFamilyStore(CFNAME);
+         cfs.truncateBlocking();
+         String key = "rt_times";
 -        RowMutation rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key));
++        Mutation rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
+         add(rm, 5, 999);
+         rm.apply();
+         key = "rt_times2";
 -        rm = new RowMutation(KSNAME, ByteBufferUtil.bytes(key));
++        rm = new Mutation(KSNAME, ByteBufferUtil.bytes(key));
+         ColumnFamily cf = rm.addOrGet(CFNAME);
+         int timestamp = (int)(System.currentTimeMillis()/1000);
+         cf.delete(new DeletionInfo(b(1), b(2), cfs.getComparator(), 1000, timestamp));
+         rm.apply();
+         cfs.forceBlockingFlush();
+         SSTableReader sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 999, 1000, Integer.MAX_VALUE);
+         cfs.forceMajorCompaction();
+         sstable = cfs.getSSTables().iterator().next();
+         assertTimes(sstable.getSSTableMetadata(), 999, 1000, Integer.MAX_VALUE);
+     }
+ 
 -    private void assertTimes(SSTableMetadata metadata, long min, long max, int localDeletionTime)
++    private void assertTimes(StatsMetadata metadata, long min, long max, int localDeletionTime)
+     {
+         assertEquals(min, metadata.minTimestamp);
+         assertEquals(max, metadata.maxTimestamp);
+         assertEquals(localDeletionTime, metadata.maxLocalDeletionTime);
+     }
  
      @Test
      public void test7810() throws ExecutionException, InterruptedException, IOException