Return-Path: X-Original-To: apmail-cassandra-commits-archive@www.apache.org Delivered-To: apmail-cassandra-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 0C019184CD for ; Thu, 11 Feb 2016 23:55:14 +0000 (UTC) Received: (qmail 13358 invoked by uid 500); 11 Feb 2016 23:55:13 -0000 Delivered-To: apmail-cassandra-commits-archive@cassandra.apache.org Received: (qmail 13318 invoked by uid 500); 11 Feb 2016 23:55:13 -0000 Mailing-List: contact commits-help@cassandra.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cassandra.apache.org Delivered-To: mailing list commits@cassandra.apache.org Received: (qmail 13297 invoked by uid 99); 11 Feb 2016 23:55:13 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 11 Feb 2016 23:55:13 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 99F4BE0248; Thu, 11 Feb 2016 23:55:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: tylerhobbs@apache.org To: commits@cassandra.apache.org Date: Thu, 11 Feb 2016 23:55:14 -0000 Message-Id: In-Reply-To: <4d77a8cb65f640e7b86363c4c63c646b@git.apache.org> References: <4d77a8cb65f640e7b86363c4c63c646b@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [2/3] cassandra git commit: Merge branch 'cassandra-2.2' into cassandra-3.0 Merge branch 'cassandra-2.2' into cassandra-3.0 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d4e6f08d Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d4e6f08d Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d4e6f08d Branch: refs/heads/trunk Commit: d4e6f08d48317796bfda8691f85c038fcd264769 Parents: 604c9df d5c83f4 Author: Tyler Hobbs Authored: Thu Feb 11 17:54:22 2016 -0600 Committer: Tyler Hobbs Committed: Thu Feb 11 17:54:22 2016 -0600 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../io/sstable/format/SSTableReader.java | 13 ++- .../cassandra/io/sstable/SSTableReaderTest.java | 113 ++++++++++++++----- 3 files changed, 93 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/d4e6f08d/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index 7a0357f,fa25980..5156b0c --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,15 -1,5 +1,16 @@@ -2.2.6 +3.0.4 + * Properly handle hinted handoff after topology changes (CASSANDRA-5902) + * AssertionError when listing sstable files on inconsistent disk state (CASSANDRA-11156) + * Fix wrong rack counting and invalid conditions check for TokenAllocation + (CASSANDRA-11139) + * Avoid creating empty hint files (CASSANDRA-11090) + * Fix leak detection strong reference loop using weak reference (CASSANDRA-11120) + * Configurie BatchlogManager to stop delayed tasks on shutdown (CASSANDRA-11062) + * Hadoop integration is incompatible with Cassandra Driver 3.0.0 (CASSANDRA-11001) + * Add dropped_columns to the list of schema table so it gets handled + properly (CASSANDRA-11050) +Merged from 2.2: + * Always persist upsampled index summaries (CASSANDRA-10512) * (cqlsh) Fix inconsistent auto-complete (CASSANDRA-10733) * Make SELECT JSON and toJson() threadsafe (CASSANDRA-11048) * Fix SELECT on tuple relations for mixed ASC/DESC clustering order (CASSANDRA-7281) http://git-wip-us.apache.org/repos/asf/cassandra/blob/d4e6f08d/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java index 1618516,e81e4e9..691bf45 --- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java +++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java @@@ -1175,13 -1154,7 +1175,7 @@@ public abstract class SSTableReader ext else if (samplingLevel < indexSummary.getSamplingLevel()) { // we can use the existing index summary to make a smaller one - newSummary = IndexSummaryBuilder.downsample(indexSummary, samplingLevel, minIndexInterval, partitioner); + newSummary = IndexSummaryBuilder.downsample(indexSummary, samplingLevel, minIndexInterval, getPartitioner()); - - try(SegmentedFile.Builder ibuilder = SegmentedFile.getBuilder(DatabaseDescriptor.getIndexAccessMode(), false); - SegmentedFile.Builder dbuilder = SegmentedFile.getBuilder(DatabaseDescriptor.getDiskAccessMode(), compression)) - { - saveSummary(ibuilder, dbuilder, newSummary); - } } else { @@@ -1189,6 -1162,18 +1183,13 @@@ "no adjustments to min/max_index_interval"); } + //Always save the resampled index + try(SegmentedFile.Builder ibuilder = SegmentedFile.getBuilder(DatabaseDescriptor.getIndexAccessMode(), false); + SegmentedFile.Builder dbuilder = SegmentedFile.getBuilder(DatabaseDescriptor.getDiskAccessMode(), compression)) + { - for (long boundry : dfile.copyReadableBounds()) - dbuilder.addPotentialBoundary(boundry); - for (long boundry : ifile.copyReadableBounds()) - ibuilder.addPotentialBoundary(boundry); - + saveSummary(ibuilder, dbuilder, newSummary); + } + long newSize = bytesOnDisk(); StorageMetrics.load.inc(newSize - oldSize); parent.metric.liveDiskSpaceUsed.inc(newSize - oldSize); http://git-wip-us.apache.org/repos/asf/cassandra/blob/d4e6f08d/test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java ---------------------------------------------------------------------- diff --cc test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java index c7f3c36,0000000..640b68b mode 100644,000000..100644 --- a/test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/SSTableReaderTest.java @@@ -1,567 -1,0 +1,624 @@@ +/* + * 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.io.sstable; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.*; + +import com.google.common.collect.Sets; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.apache.cassandra.OrderedJUnit4ClassRunner; +import org.apache.cassandra.SchemaLoader; +import org.apache.cassandra.Util; +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.Operator; +import org.apache.cassandra.db.*; +import org.apache.cassandra.db.compaction.CompactionManager; +import org.apache.cassandra.db.compaction.OperationType; +import org.apache.cassandra.db.lifecycle.LifecycleTransaction; +import org.apache.cassandra.db.partitions.UnfilteredPartitionIterators; +import org.apache.cassandra.db.rows.Row; +import org.apache.cassandra.dht.IPartitioner; +import org.apache.cassandra.dht.LocalPartitioner.LocalToken; +import org.apache.cassandra.dht.Range; +import org.apache.cassandra.dht.Token; +import org.apache.cassandra.index.Index; +import org.apache.cassandra.io.sstable.format.SSTableReader; +import org.apache.cassandra.io.util.FileDataInput; +import org.apache.cassandra.io.util.MmappedRegions; +import org.apache.cassandra.io.util.MmappedSegmentedFile; +import org.apache.cassandra.io.util.SegmentedFile; +import org.apache.cassandra.schema.CachingParams; +import org.apache.cassandra.schema.KeyspaceParams; +import org.apache.cassandra.service.CacheService; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.utils.Pair; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(OrderedJUnit4ClassRunner.class) +public class SSTableReaderTest +{ + public static final String KEYSPACE1 = "SSTableReaderTest"; + public static final String CF_STANDARD = "Standard1"; + public static final String CF_STANDARD2 = "Standard2"; + public static final String CF_INDEXED = "Indexed1"; + public static final String CF_STANDARDLOWINDEXINTERVAL = "StandardLowIndexInterval"; + + private IPartitioner partitioner; + + Token t(int i) + { + return partitioner.getToken(ByteBufferUtil.bytes(String.valueOf(i))); + } + + @BeforeClass + public static void defineSchema() throws Exception + { + SchemaLoader.prepareServer(); + SchemaLoader.createKeyspace(KEYSPACE1, + KeyspaceParams.simple(1), + SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARD), + SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARD2), + SchemaLoader.compositeIndexCFMD(KEYSPACE1, CF_INDEXED, true), + SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARDLOWINDEXINTERVAL) + .minIndexInterval(8) + .maxIndexInterval(256) + .caching(CachingParams.CACHE_NOTHING)); + } + + @Test + public void testGetPositionsForRanges() + { + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard2"); + partitioner = store.getPartitioner(); + + // insert data and compact to a single sstable + CompactionManager.instance.disableAutoCompaction(); + for (int j = 0; j < 10; j++) + { + new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) + .clustering("0") + .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) + .build() + .applyUnsafe(); + } + store.forceBlockingFlush(); + CompactionManager.instance.performMaximal(store, false); + + List> ranges = new ArrayList>(); + // 1 key + ranges.add(new Range<>(t(0), t(1))); + // 2 keys + ranges.add(new Range<>(t(2), t(4))); + // wrapping range from key to end + ranges.add(new Range<>(t(6), partitioner.getMinimumToken())); + // empty range (should be ignored) + ranges.add(new Range<>(t(9), t(91))); + + // confirm that positions increase continuously + SSTableReader sstable = store.getLiveSSTables().iterator().next(); + long previous = -1; + for (Pair section : sstable.getPositionsForRanges(ranges)) + { + assert previous <= section.left : previous + " ! < " + section.left; + assert section.left < section.right : section.left + " ! < " + section.right; + previous = section.right; + } + } + + @Test + public void testSpannedIndexPositions() throws IOException + { ++ int originalMaxSegmentSize = MmappedRegions.MAX_SEGMENT_SIZE; + MmappedRegions.MAX_SEGMENT_SIZE = 40; // each index entry is ~11 bytes, so this will generate lots of segments + - Keyspace keyspace = Keyspace.open(KEYSPACE1); - ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard1"); - partitioner = store.getPartitioner(); - - // insert a bunch of data and compact to a single sstable - CompactionManager.instance.disableAutoCompaction(); - for (int j = 0; j < 100; j += 2) ++ try + { - new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) - .clustering("0") - .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) - .build() - .applyUnsafe(); - } - store.forceBlockingFlush(); - CompactionManager.instance.performMaximal(store, false); ++ Keyspace keyspace = Keyspace.open(KEYSPACE1); ++ ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard1"); ++ partitioner = store.getPartitioner(); + - // check that all our keys are found correctly - SSTableReader sstable = store.getLiveSSTables().iterator().next(); - for (int j = 0; j < 100; j += 2) - { - DecoratedKey dk = Util.dk(String.valueOf(j)); - FileDataInput file = sstable.getFileDataInput(sstable.getPosition(dk, SSTableReader.Operator.EQ).position); - DecoratedKey keyInDisk = sstable.decorateKey(ByteBufferUtil.readWithShortLength(file)); - assert keyInDisk.equals(dk) : String.format("%s != %s in %s", keyInDisk, dk, file.getPath()); - } ++ // insert a bunch of data and compact to a single sstable ++ CompactionManager.instance.disableAutoCompaction(); ++ for (int j = 0; j < 100; j += 2) ++ { ++ new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) ++ .clustering("0") ++ .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) ++ .build() ++ .applyUnsafe(); ++ } ++ store.forceBlockingFlush(); ++ CompactionManager.instance.performMaximal(store, false); ++ ++ // check that all our keys are found correctly ++ SSTableReader sstable = store.getLiveSSTables().iterator().next(); ++ for (int j = 0; j < 100; j += 2) ++ { ++ DecoratedKey dk = Util.dk(String.valueOf(j)); ++ FileDataInput file = sstable.getFileDataInput(sstable.getPosition(dk, SSTableReader.Operator.EQ).position); ++ DecoratedKey keyInDisk = sstable.decorateKey(ByteBufferUtil.readWithShortLength(file)); ++ assert keyInDisk.equals(dk) : String.format("%s != %s in %s", keyInDisk, dk, file.getPath()); ++ } + - // check no false positives - for (int j = 1; j < 110; j += 2) ++ // check no false positives ++ for (int j = 1; j < 110; j += 2) ++ { ++ DecoratedKey dk = Util.dk(String.valueOf(j)); ++ assert sstable.getPosition(dk, SSTableReader.Operator.EQ) == null; ++ } ++ } ++ finally + { - DecoratedKey dk = Util.dk(String.valueOf(j)); - assert sstable.getPosition(dk, SSTableReader.Operator.EQ) == null; ++ MmappedRegions.MAX_SEGMENT_SIZE = originalMaxSegmentSize; + } + } + + @Test + public void testPersistentStatistics() + { + + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard1"); + partitioner = store.getPartitioner(); + + for (int j = 0; j < 100; j += 2) + { + new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) + .clustering("0") + .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) + .build() + .applyUnsafe(); + } + store.forceBlockingFlush(); + + clearAndLoad(store); + assert store.metric.maxPartitionSize.getValue() != 0; + } + + private void clearAndLoad(ColumnFamilyStore cfs) + { + cfs.clearUnsafe(); + cfs.loadNewSSTables(); + } + + @Test + public void testReadRateTracking() + { + // try to make sure CASSANDRA-8239 never happens again + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard1"); + partitioner = store.getPartitioner(); + + for (int j = 0; j < 10; j++) + { + new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) + .clustering("0") + .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) + .build() + .applyUnsafe(); + } + + store.forceBlockingFlush(); + + SSTableReader sstable = store.getLiveSSTables().iterator().next(); + assertEquals(0, sstable.getReadMeter().count()); + + DecoratedKey key = sstable.decorateKey(ByteBufferUtil.bytes("4")); + Util.getAll(Util.cmd(store, key).build()); + assertEquals(1, sstable.getReadMeter().count()); + + Util.getAll(Util.cmd(store, key).includeRow("0").build()); + assertEquals(2, sstable.getReadMeter().count()); + } + + @Test + public void testGetPositionsForRangesWithKeyCache() + { + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard2"); + partitioner = store.getPartitioner(); + CacheService.instance.keyCache.setCapacity(100); + + // insert data and compact to a single sstable + CompactionManager.instance.disableAutoCompaction(); + for (int j = 0; j < 10; j++) + { + + new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) + .clustering("0") + .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) + .build() + .applyUnsafe(); + + } + store.forceBlockingFlush(); + CompactionManager.instance.performMaximal(store, false); + + SSTableReader sstable = store.getLiveSSTables().iterator().next(); + long p2 = sstable.getPosition(k(2), SSTableReader.Operator.EQ).position; + long p3 = sstable.getPosition(k(3), SSTableReader.Operator.EQ).position; + long p6 = sstable.getPosition(k(6), SSTableReader.Operator.EQ).position; + long p7 = sstable.getPosition(k(7), SSTableReader.Operator.EQ).position; + + Pair p = sstable.getPositionsForRanges(makeRanges(t(2), t(6))).get(0); + + // range are start exclusive so we should start at 3 + assert p.left == p3; + + // to capture 6 we have to stop at the start of 7 + assert p.right == p7; + } + + @Test + public void testPersistentStatisticsWithSecondaryIndex() + { + // Create secondary index and flush to disk + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore(CF_INDEXED); + partitioner = store.getPartitioner(); + + new RowUpdateBuilder(store.metadata, System.currentTimeMillis(), "k1") + .clustering("0") + .add("birthdate", 1L) + .build() + .applyUnsafe(); + + store.forceBlockingFlush(); + + // check if opening and querying works + assertIndexQueryWorks(store); + } + public void testGetPositionsKeyCacheStats() + { + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard2"); + partitioner = store.getPartitioner(); + CacheService.instance.keyCache.setCapacity(1000); + + // insert data and compact to a single sstable + CompactionManager.instance.disableAutoCompaction(); + for (int j = 0; j < 10; j++) + { + new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) + .clustering("0") + .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) + .build() + .applyUnsafe(); + } + store.forceBlockingFlush(); + CompactionManager.instance.performMaximal(store, false); + + SSTableReader sstable = store.getLiveSSTables().iterator().next(); + sstable.getPosition(k(2), SSTableReader.Operator.EQ); + assertEquals(0, sstable.getKeyCacheHit()); + assertEquals(1, sstable.getBloomFilterTruePositiveCount()); + sstable.getPosition(k(2), SSTableReader.Operator.EQ); + assertEquals(1, sstable.getKeyCacheHit()); + assertEquals(2, sstable.getBloomFilterTruePositiveCount()); + sstable.getPosition(k(15), SSTableReader.Operator.EQ); + assertEquals(1, sstable.getKeyCacheHit()); + assertEquals(2, sstable.getBloomFilterTruePositiveCount()); + + } + + + @Test + public void testOpeningSSTable() throws Exception + { + String ks = KEYSPACE1; + String cf = "Standard1"; + + // clear and create just one sstable for this test + Keyspace keyspace = Keyspace.open(ks); + ColumnFamilyStore store = keyspace.getColumnFamilyStore(cf); + store.clearUnsafe(); + store.disableAutoCompaction(); + + DecoratedKey firstKey = null, lastKey = null; + long timestamp = System.currentTimeMillis(); + for (int i = 0; i < store.metadata.params.minIndexInterval; i++) + { + DecoratedKey key = Util.dk(String.valueOf(i)); + if (firstKey == null) + firstKey = key; + if (lastKey == null) + lastKey = key; + if (store.metadata.getKeyValidator().compare(lastKey.getKey(), key.getKey()) < 0) + lastKey = key; + + + new RowUpdateBuilder(store.metadata, timestamp, key.getKey()) + .clustering("col") + .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) + .build() + .applyUnsafe(); + + } + store.forceBlockingFlush(); + + SSTableReader sstable = store.getLiveSSTables().iterator().next(); + Descriptor desc = sstable.descriptor; + + // test to see if sstable can be opened as expected + SSTableReader target = SSTableReader.open(desc); + Assert.assertEquals(target.getIndexSummarySize(), 1); + Assert.assertArrayEquals(ByteBufferUtil.getArray(firstKey.getKey()), target.getIndexSummaryKey(0)); + assert target.first.equals(firstKey); + assert target.last.equals(lastKey); + target.selfRef().release(); + } + + @Test + public void testLoadingSummaryUsesCorrectPartitioner() throws Exception + { + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Indexed1"); + + new RowUpdateBuilder(store.metadata, System.currentTimeMillis(), "k1") + .clustering("0") + .add("birthdate", 1L) + .build() + .applyUnsafe(); + + store.forceBlockingFlush(); + + for(ColumnFamilyStore indexCfs : store.indexManager.getAllIndexColumnFamilyStores()) + { + assert indexCfs.isIndex(); + SSTableReader sstable = indexCfs.getLiveSSTables().iterator().next(); + assert sstable.first.getToken() instanceof LocalToken; + + try (SegmentedFile.Builder ibuilder = SegmentedFile.getBuilder(DatabaseDescriptor.getIndexAccessMode(), + false); + SegmentedFile.Builder dbuilder = SegmentedFile.getBuilder(DatabaseDescriptor.getDiskAccessMode(), + sstable.compression)) + { + sstable.saveSummary(ibuilder, dbuilder); + } + SSTableReader reopened = SSTableReader.open(sstable.descriptor); + assert reopened.first.getToken() instanceof LocalToken; + reopened.selfRef().release(); + } + } + + /** see CASSANDRA-5407 */ + @Test + public void testGetScannerForNoIntersectingRanges() throws Exception + { + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard1"); + partitioner = store.getPartitioner(); + + new RowUpdateBuilder(store.metadata, 0, "k1") + .clustering("xyz") + .add("val", "abc") + .build() + .applyUnsafe(); + + store.forceBlockingFlush(); + boolean foundScanner = false; + for (SSTableReader s : store.getLiveSSTables()) + { + try (ISSTableScanner scanner = s.getScanner(new Range(t(0), t(1)), null)) + { + scanner.next(); // throws exception pre 5407 + foundScanner = true; + } + } + assertTrue(foundScanner); + } + + @Test + public void testGetPositionsForRangesFromTableOpenedForBulkLoading() throws IOException + { + Keyspace keyspace = Keyspace.open(KEYSPACE1); + ColumnFamilyStore store = keyspace.getColumnFamilyStore("Standard2"); + partitioner = store.getPartitioner(); + + // insert data and compact to a single sstable. The + // number of keys inserted is greater than index_interval + // to ensure multiple segments in the index file + CompactionManager.instance.disableAutoCompaction(); + for (int j = 0; j < 130; j++) + { + + new RowUpdateBuilder(store.metadata, j, String.valueOf(j)) + .clustering("0") + .add("val", ByteBufferUtil.EMPTY_BYTE_BUFFER) + .build() + .applyUnsafe(); + + } + store.forceBlockingFlush(); + CompactionManager.instance.performMaximal(store, false); + + // construct a range which is present in the sstable, but whose + // keys are not found in the first segment of the index. + List> ranges = new ArrayList>(); + ranges.add(new Range(t(98), t(99))); + + SSTableReader sstable = store.getLiveSSTables().iterator().next(); + List> sections = sstable.getPositionsForRanges(ranges); + assert sections.size() == 1 : "Expected to find range in sstable" ; + + // re-open the same sstable as it would be during bulk loading + Set components = Sets.newHashSet(Component.DATA, Component.PRIMARY_INDEX); + if (sstable.components.contains(Component.COMPRESSION_INFO)) + components.add(Component.COMPRESSION_INFO); + SSTableReader bulkLoaded = SSTableReader.openForBatch(sstable.descriptor, components, store.metadata); + sections = bulkLoaded.getPositionsForRanges(ranges); + assert sections.size() == 1 : "Expected to find range in sstable opened for bulk loading"; + bulkLoaded.selfRef().release(); + } + + @Test + public void testIndexSummaryReplacement() throws IOException, ExecutionException, InterruptedException + { + Keyspace keyspace = Keyspace.open(KEYSPACE1); + final ColumnFamilyStore store = keyspace.getColumnFamilyStore("StandardLowIndexInterval"); // index interval of 8, no key caching + CompactionManager.instance.disableAutoCompaction(); + + final int NUM_PARTITIONS = 512; + for (int j = 0; j < NUM_PARTITIONS; j++) + { + new RowUpdateBuilder(store.metadata, j, String.format("%3d", j)) + .clustering("0") + .add("val", String.format("%3d", j)) + .build() + .applyUnsafe(); + + } + store.forceBlockingFlush(); + CompactionManager.instance.performMaximal(store, false); + + Collection sstables = store.getLiveSSTables(); + assert sstables.size() == 1; + final SSTableReader sstable = sstables.iterator().next(); + + ThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5); + List futures = new ArrayList<>(NUM_PARTITIONS * 2); + for (int i = 0; i < NUM_PARTITIONS; i++) + { + final ByteBuffer key = ByteBufferUtil.bytes(String.format("%3d", i)); + final int index = i; + + futures.add(executor.submit(new Runnable() + { + public void run() + { + Row row = Util.getOnlyRowUnfiltered(Util.cmd(store, key).build()); + assertEquals(0, ByteBufferUtil.compare(String.format("%3d", index).getBytes(), row.cells().iterator().next().value())); + } + })); + + futures.add(executor.submit(new Runnable() + { + public void run() + { + Iterable results = store.keySamples( + new Range<>(sstable.getPartitioner().getMinimumToken(), sstable.getPartitioner().getToken(key))); + assertTrue(results.iterator().hasNext()); + } + })); + } + + SSTableReader replacement; + try (LifecycleTransaction txn = store.getTracker().tryModify(Arrays.asList(sstable), OperationType.UNKNOWN)) + { + replacement = sstable.cloneWithNewSummarySamplingLevel(store, 1); + txn.update(replacement, true); + txn.finish(); + } + for (Future future : futures) + future.get(); + + assertEquals(sstable.estimatedKeys(), replacement.estimatedKeys(), 1); + } + ++ @Test ++ public void testIndexSummaryUpsampleAndReload() throws Exception ++ { ++ int originalMaxSegmentSize = MmappedRegions.MAX_SEGMENT_SIZE; ++ MmappedRegions.MAX_SEGMENT_SIZE = 40; // each index entry is ~11 bytes, so this will generate lots of segments ++ ++ try ++ { ++ testIndexSummaryUpsampleAndReload0(); ++ } ++ finally ++ { ++ MmappedRegions.MAX_SEGMENT_SIZE = originalMaxSegmentSize; ++ } ++ } ++ ++ private void testIndexSummaryUpsampleAndReload0() throws Exception ++ { ++ Keyspace keyspace = Keyspace.open(KEYSPACE1); ++ final ColumnFamilyStore store = keyspace.getColumnFamilyStore("StandardLowIndexInterval"); // index interval of 8, no key caching ++ CompactionManager.instance.disableAutoCompaction(); ++ ++ final int NUM_PARTITIONS = 512; ++ for (int j = 0; j < NUM_PARTITIONS; j++) ++ { ++ new RowUpdateBuilder(store.metadata, j, String.format("%3d", j)) ++ .clustering("0") ++ .add("val", String.format("%3d", j)) ++ .build() ++ .applyUnsafe(); ++ ++ } ++ store.forceBlockingFlush(); ++ CompactionManager.instance.performMaximal(store, false); ++ ++ Collection sstables = store.getLiveSSTables(); ++ assert sstables.size() == 1; ++ final SSTableReader sstable = sstables.iterator().next(); ++ ++ try (LifecycleTransaction txn = store.getTracker().tryModify(Arrays.asList(sstable), OperationType.UNKNOWN)) ++ { ++ SSTableReader replacement = sstable.cloneWithNewSummarySamplingLevel(store, sstable.getIndexSummarySamplingLevel() + 1); ++ txn.update(replacement, true); ++ txn.finish(); ++ } ++ SSTableReader reopen = SSTableReader.open(sstable.descriptor); ++ assert reopen.getIndexSummarySamplingLevel() == sstable.getIndexSummarySamplingLevel() + 1; ++ } ++ + private void assertIndexQueryWorks(ColumnFamilyStore indexedCFS) + { + assert "Indexed1".equals(indexedCFS.name); + + // make sure all sstables including 2ary indexes load from disk + for (ColumnFamilyStore cfs : indexedCFS.concatWithIndexes()) + clearAndLoad(cfs); + + + // query using index to see if sstable for secondary index opens + ReadCommand rc = Util.cmd(indexedCFS).fromKeyIncl("k1").toKeyIncl("k3") + .columns("birthdate") + .filterOn("birthdate", Operator.EQ, 1L) + .build(); + Index.Searcher searcher = indexedCFS.indexManager.getBestIndexFor(rc).searcherFor(rc); + assertNotNull(searcher); + try (ReadOrderGroup orderGroup = ReadOrderGroup.forCommand(rc)) + { + assertEquals(1, Util.size(UnfilteredPartitionIterators.filter(searcher.search(orderGroup), rc.nowInSec()))); + } + } + + private List> makeRanges(Token left, Token right) + { + return Arrays.asList(new Range<>(left, right)); + } + + private DecoratedKey k(int i) + { + return new BufferDecoratedKey(t(i), ByteBufferUtil.bytes(String.valueOf(i))); + } +}