diff --git a/build.xml b/build.xml index 5e15f4923d..590e523fce 100644 --- a/build.xml +++ b/build.xml @@ -1227,6 +1227,8 @@ + + diff --git a/src/java/org/apache/cassandra/db/Memtable.java b/src/java/org/apache/cassandra/db/Memtable.java index ae8b8d3a74..9cfcb2f830 100644 --- a/src/java/org/apache/cassandra/db/Memtable.java +++ b/src/java/org/apache/cassandra/db/Memtable.java @@ -66,6 +66,7 @@ public class Memtable implements Comparable private static final Logger logger = LoggerFactory.getLogger(Memtable.class); public static final MemtablePool MEMORY_POOL = createMemtableAllocatorPool(); + public static final long NO_MIN_TIMESTAMP = -1; private static MemtablePool createMemtableAllocatorPool() { @@ -161,6 +162,16 @@ public class Memtable implements Comparable this.columnsCollector = new ColumnsCollector(metadata.partitionColumns()); } + @VisibleForTesting + public Memtable(CFMetaData metadata, long minTimestamp) + { + this.initialComparator = metadata.comparator; + this.cfs = null; + this.allocator = null; + this.columnsCollector = new ColumnsCollector(metadata.partitionColumns()); + this.minTimestamp = minTimestamp; + } + public MemtableAllocator getAllocator() { return allocator; @@ -387,9 +398,16 @@ public class Memtable implements Comparable return partitions.get(key); } + /** + * Returns the minTS if one available, otherwise NO_MIN_TIMESTAMP. + * + * EncodingStats uses a synthetic epoch TS at 2015. We don't want to leak that (CASSANDRA-18118) so we return NO_MIN_TIMESTAMP instead. + * + * @return The minTS or NO_MIN_TIMESTAMP if none available + */ public long getMinTimestamp() { - return minTimestamp; + return minTimestamp != EncodingStats.NO_STATS.minTimestamp ? minTimestamp : NO_MIN_TIMESTAMP; } /** diff --git a/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java b/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java index a97243be2f..dfdcc03014 100644 --- a/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java +++ b/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java @@ -713,7 +713,8 @@ public class SinglePartitionReadCommand extends ReadCommand if (partition == null) continue; - minTimestamp = Math.min(minTimestamp, memtable.getMinTimestamp()); + if (memtable.getMinTimestamp() != Memtable.NO_MIN_TIMESTAMP) + minTimestamp = Math.min(minTimestamp, memtable.getMinTimestamp()); @SuppressWarnings("resource") // 'iter' is added to iterators which is closed on exception, or through the closing of the final merged iterator UnfilteredRowIterator iter = filter.getUnfilteredRowIterator(columnFilter(), partition); diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionController.java b/src/java/org/apache/cassandra/db/compaction/CompactionController.java index 84aac09052..19318ff1a9 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionController.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionController.java @@ -207,7 +207,10 @@ public class CompactionController implements AutoCloseable } for (Memtable memtable : cfStore.getTracker().getView().getAllMemtables()) - minTimestamp = Math.min(minTimestamp, memtable.getMinTimestamp()); + { + if (memtable.getMinTimestamp() != Memtable.NO_MIN_TIMESTAMP) + minTimestamp = Math.min(minTimestamp, memtable.getMinTimestamp()); + } // At this point, minTimestamp denotes the lowest timestamp of any relevant // SSTable or Memtable that contains a constructive value. candidates contains all the @@ -281,11 +284,14 @@ public class CompactionController implements AutoCloseable for (Memtable memtable : memtables) { - Partition partition = memtable.getPartition(key); - if (partition != null) + if (memtable.getMinTimestamp() != Memtable.NO_MIN_TIMESTAMP) { - minTimestampSeen = Math.min(minTimestampSeen, partition.stats().minTimestamp); - hasTimestamp = true; + Partition partition = memtable.getPartition(key); + if (partition != null) + { + minTimestampSeen = Math.min(minTimestampSeen, partition.stats().minTimestamp); + hasTimestamp = true; + } } } diff --git a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java index 3987a29f2d..8e8e9c94b7 100644 --- a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java +++ b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java @@ -98,6 +98,15 @@ public class ColumnFamilyStoreTest Keyspace.open(KEYSPACE2).getColumnFamilyStore(CF_STANDARD1).truncateBlocking(); } + @Test + public void testMemtableTimestamp() throws Throwable + { + assertEquals(Memtable.NO_MIN_TIMESTAMP, + (new Memtable(Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_STANDARD1).metadata, + EncodingStats.NO_STATS.minTimestamp)) + .getMinTimestamp()); + } + @Test // create two sstables, and verify that we only deserialize data from the most recent one public void testTimeSortedQuery()