| /* |
| * 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.db; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.CharacterCodingException; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| import java.util.UUID; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| |
| import com.google.common.base.Function; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Sets; |
| |
| import org.apache.cassandra.db.index.PerRowSecondaryIndexTest; |
| import org.apache.cassandra.io.sstable.*; |
| import org.apache.cassandra.io.sstable.format.SSTableReader; |
| import org.apache.cassandra.io.sstable.format.SSTableWriter; |
| import org.apache.commons.lang3.ArrayUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.junit.Assume; |
| 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.*; |
| import org.apache.cassandra.cql3.Operator; |
| import org.apache.cassandra.db.columniterator.IdentityQueryFilter; |
| import org.apache.cassandra.db.composites.CellName; |
| import org.apache.cassandra.db.composites.CellNameType; |
| 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.NamesQueryFilter; |
| import org.apache.cassandra.db.filter.QueryFilter; |
| import org.apache.cassandra.db.filter.SliceQueryFilter; |
| import org.apache.cassandra.db.index.SecondaryIndex; |
| import org.apache.cassandra.db.marshal.IntegerType; |
| import org.apache.cassandra.db.marshal.LexicalUUIDType; |
| import org.apache.cassandra.db.marshal.LongType; |
| import org.apache.cassandra.db.marshal.UTF8Type; |
| import org.apache.cassandra.dht.Bounds; |
| import org.apache.cassandra.dht.ExcludingBounds; |
| import org.apache.cassandra.dht.IPartitioner; |
| import org.apache.cassandra.dht.IncludingExcludingBounds; |
| import org.apache.cassandra.dht.Range; |
| import org.apache.cassandra.exceptions.ConfigurationException; |
| import org.apache.cassandra.io.sstable.metadata.MetadataCollector; |
| import org.apache.cassandra.io.util.FileUtils; |
| import org.apache.cassandra.locator.SimpleStrategy; |
| import org.apache.cassandra.metrics.ClearableHistogram; |
| import org.apache.cassandra.service.ActiveRepairService; |
| import org.apache.cassandra.service.StorageService; |
| import org.apache.cassandra.thrift.SlicePredicate; |
| import org.apache.cassandra.thrift.SliceRange; |
| import org.apache.cassandra.thrift.ThriftValidation; |
| import org.apache.cassandra.utils.ByteBufferUtil; |
| import org.apache.cassandra.utils.FBUtilities; |
| import org.apache.cassandra.utils.Pair; |
| import org.apache.cassandra.utils.WrappedRunnable; |
| import org.apache.thrift.TException; |
| |
| import static org.apache.cassandra.Util.cellname; |
| import static org.apache.cassandra.Util.column; |
| import static org.apache.cassandra.Util.dk; |
| import static org.apache.cassandra.Util.rp; |
| import static org.apache.cassandra.utils.ByteBufferUtil.bytes; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertTrue; |
| |
| @RunWith(OrderedJUnit4ClassRunner.class) |
| public class ColumnFamilyStoreTest |
| { |
| static byte[] bytes1, bytes2; |
| public static final String KEYSPACE1 = "ColumnFamilyStoreTest1"; |
| public static final String KEYSPACE2 = "ColumnFamilyStoreTest2"; |
| public static final String KEYSPACE3 = "ColumnFamilyStoreTest3"; |
| public static final String KEYSPACE4 = "PerRowSecondaryIndex"; |
| public static final String CF_STANDARD1 = "Standard1"; |
| public static final String CF_STANDARD2 = "Standard2"; |
| public static final String CF_STANDARD3 = "Standard3"; |
| public static final String CF_STANDARD4 = "Standard4"; |
| public static final String CF_STANDARD5 = "Standard5"; |
| public static final String CF_STANDARDINT = "StandardInteger1"; |
| public static final String CF_SUPER1 = "Super1"; |
| public static final String CF_SUPER6 = "Super6"; |
| public static final String CF_INDEX1 = "Indexed1"; |
| public static final String CF_INDEX2 = "Indexed2"; |
| public static final String CF_INDEX3 = "Indexed3"; |
| |
| static |
| { |
| Random random = new Random(); |
| bytes1 = new byte[1024]; |
| bytes2 = new byte[128]; |
| random.nextBytes(bytes1); |
| random.nextBytes(bytes2); |
| } |
| |
| @BeforeClass |
| public static void defineSchema() throws ConfigurationException |
| { |
| SchemaLoader.prepareServer(); |
| SchemaLoader.createKeyspace(KEYSPACE1, |
| SimpleStrategy.class, |
| KSMetaData.optsWithRF(1), |
| SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARD1), |
| SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARD2), |
| SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARD3), |
| SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARD4), |
| SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARD5), |
| SchemaLoader.indexCFMD(KEYSPACE1, CF_INDEX1, true), |
| SchemaLoader.indexCFMD(KEYSPACE1, CF_INDEX2, false), |
| SchemaLoader.superCFMD(KEYSPACE1, CF_SUPER1, LongType.instance), |
| SchemaLoader.superCFMD(KEYSPACE1, CF_SUPER6, LexicalUUIDType.instance, UTF8Type.instance), |
| SchemaLoader.standardCFMD(KEYSPACE1, CF_STANDARDINT, IntegerType.instance)); |
| SchemaLoader.createKeyspace(KEYSPACE2, |
| SimpleStrategy.class, |
| KSMetaData.optsWithRF(1), |
| SchemaLoader.standardCFMD(KEYSPACE2, CF_STANDARD1), |
| SchemaLoader.indexCFMD(KEYSPACE2, CF_INDEX1, true), |
| SchemaLoader.compositeIndexCFMD(KEYSPACE2, CF_INDEX2, true), |
| SchemaLoader.compositeIndexCFMD(KEYSPACE2, CF_INDEX3, true).gcGraceSeconds(0)); |
| SchemaLoader.createKeyspace(KEYSPACE3, |
| SimpleStrategy.class, |
| KSMetaData.optsWithRF(5), |
| SchemaLoader.indexCFMD(KEYSPACE3, CF_INDEX1, true)); |
| SchemaLoader.createKeyspace(KEYSPACE4, |
| SimpleStrategy.class, |
| KSMetaData.optsWithRF(1), |
| SchemaLoader.perRowIndexedCFMD(KEYSPACE4, "Indexed1")); |
| } |
| |
| @Test |
| // create two sstables, and verify that we only deserialize data from the most recent one |
| public void testTimeSortedQuery() |
| { |
| Keyspace keyspace = Keyspace.open(KEYSPACE1); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_STANDARD1); |
| cfs.truncateBlocking(); |
| |
| Mutation rm; |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("key1")); |
| rm.add(CF_STANDARD1, cellname("Column1"), ByteBufferUtil.bytes("asdf"), 0); |
| rm.applyUnsafe(); |
| cfs.forceBlockingFlush(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("key1")); |
| rm.add(CF_STANDARD1, cellname("Column1"), ByteBufferUtil.bytes("asdf"), 1); |
| rm.applyUnsafe(); |
| cfs.forceBlockingFlush(); |
| |
| ((ClearableHistogram)cfs.metric.sstablesPerReadHistogram.cf).clear(); // resets counts |
| cfs.getColumnFamily(Util.namesQueryFilter(cfs, Util.dk("key1"), "Column1")); |
| assertEquals(1, cfs.metric.sstablesPerReadHistogram.cf.getCount()); |
| } |
| |
| @Test |
| public void testGetColumnWithWrongBF() |
| { |
| Keyspace keyspace = Keyspace.open(KEYSPACE1); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_STANDARD1); |
| cfs.truncateBlocking(); |
| |
| List<Mutation> rms = new LinkedList<>(); |
| Mutation rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("key1")); |
| rm.add(CF_STANDARD1, cellname("Column1"), ByteBufferUtil.bytes("asdf"), 0); |
| rm.add(CF_STANDARD1, cellname("Column2"), ByteBufferUtil.bytes("asdf"), 0); |
| rms.add(rm); |
| Util.writeColumnFamily(rms); |
| |
| List<SSTableReader> ssTables = keyspace.getAllSSTables(); |
| assertEquals(1, ssTables.size()); |
| ssTables.get(0).forceFilterFailures(); |
| ColumnFamily cf = cfs.getColumnFamily(QueryFilter.getIdentityFilter(Util.dk("key2"), CF_STANDARD1, System.currentTimeMillis())); |
| assertNull(cf); |
| } |
| |
| @Test |
| public void testEmptyRow() throws Exception |
| { |
| Keyspace keyspace = Keyspace.open(KEYSPACE1); |
| final ColumnFamilyStore store = keyspace.getColumnFamilyStore(CF_STANDARD2); |
| Mutation rm; |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("key1")); |
| rm.delete(CF_STANDARD2, System.currentTimeMillis()); |
| rm.applyUnsafe(); |
| |
| Runnable r = new WrappedRunnable() |
| { |
| public void runMayThrow() throws IOException |
| { |
| QueryFilter sliceFilter = QueryFilter.getSliceFilter(Util.dk("key1"), CF_STANDARD2, Composites.EMPTY, Composites.EMPTY, false, 1, System.currentTimeMillis()); |
| ColumnFamily cf = store.getColumnFamily(sliceFilter); |
| assertTrue(cf.isMarkedForDelete()); |
| assertFalse(cf.hasColumns()); |
| |
| QueryFilter namesFilter = Util.namesQueryFilter(store, Util.dk("key1"), "a"); |
| cf = store.getColumnFamily(namesFilter); |
| assertTrue(cf.isMarkedForDelete()); |
| assertFalse(cf.hasColumns()); |
| } |
| }; |
| |
| KeyspaceTest.reTest(store, r); |
| } |
| |
| @Test |
| public void testSkipStartKey() |
| { |
| ColumnFamilyStore cfs = insertKey1Key2(); |
| |
| IPartitioner p = StorageService.getPartitioner(); |
| List<Row> result = cfs.getRangeSlice(Util.range(p, "key1", "key2"), |
| null, |
| Util.namesFilter(cfs, "asdf"), |
| 10); |
| assertEquals(1, result.size()); |
| assert result.get(0).key.getKey().equals(ByteBufferUtil.bytes("key2")); |
| } |
| |
| @Test |
| public void testIndexScan() |
| { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_INDEX1); |
| Mutation rm; |
| CellName nobirthdate = cellname("notbirthdate"); |
| CellName birthdate = cellname("birthdate"); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.applyUnsafe(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("k2")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(2L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(2L), 0); |
| rm.applyUnsafe(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("k3")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(2L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.applyUnsafe(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("k4aaaa")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(2L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(3L), 0); |
| rm.applyUnsafe(); |
| |
| // basic single-expression query |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(1L)); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = cfs.search(range, clause, filter, 100); |
| |
| assert rows != null; |
| assert rows.size() == 2 : StringUtils.join(rows, ","); |
| |
| String key = new String(rows.get(0).key.getKey().array(), rows.get(0).key.getKey().position(), rows.get(0).key.getKey().remaining()); |
| assert "k1".equals( key ) : key; |
| |
| key = new String(rows.get(1).key.getKey().array(), rows.get(1).key.getKey().position(), rows.get(1).key.getKey().remaining()); |
| assert "k3".equals(key) : key; |
| |
| assert ByteBufferUtil.bytes(1L).equals( rows.get(0).cf.getColumn(birthdate).value()); |
| assert ByteBufferUtil.bytes(1L).equals( rows.get(1).cf.getColumn(birthdate).value()); |
| |
| // add a second expression |
| IndexExpression expr2 = new IndexExpression(ByteBufferUtil.bytes("notbirthdate"), Operator.GTE, ByteBufferUtil.bytes(2L)); |
| clause = Arrays.asList(expr, expr2); |
| rows = cfs.search(range, clause, filter, 100); |
| |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| key = new String(rows.get(0).key.getKey().array(), rows.get(0).key.getKey().position(), rows.get(0).key.getKey().remaining()); |
| assert "k3".equals( key ); |
| |
| // same query again, but with resultset not including the subordinate expression |
| rows = cfs.search(range, clause, Util.namesFilter(cfs, "birthdate"), 100); |
| |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| key = new String(rows.get(0).key.getKey().array(), rows.get(0).key.getKey().position(), rows.get(0).key.getKey().remaining()); |
| assert "k3".equals( key ); |
| |
| assert rows.get(0).cf.getColumnCount() == 1 : rows.get(0).cf; |
| |
| // once more, this time with a slice rowset that needs to be expanded |
| SliceQueryFilter emptyFilter = new SliceQueryFilter(Composites.EMPTY, Composites.EMPTY, false, 0); |
| rows = cfs.search(range, clause, emptyFilter, 100); |
| |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| key = new String(rows.get(0).key.getKey().array(), rows.get(0).key.getKey().position(), rows.get(0).key.getKey().remaining()); |
| assert "k3".equals( key ); |
| |
| assertFalse(rows.get(0).cf.hasColumns()); |
| |
| // query with index hit but rejected by secondary clause, with a small enough count that just checking count |
| // doesn't tell the scan loop that it's done |
| IndexExpression expr3 = new IndexExpression(ByteBufferUtil.bytes("notbirthdate"), Operator.EQ, ByteBufferUtil.bytes(-1L)); |
| clause = Arrays.asList(expr, expr3); |
| rows = cfs.search(range, clause, filter, 100); |
| |
| assert rows.isEmpty(); |
| } |
| |
| @Test |
| public void testLargeScan() |
| { |
| Mutation rm; |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_INDEX1); |
| for (int i = 0; i < 100; i++) |
| { |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("key" + i)); |
| rm.add(CF_INDEX1, cellname("birthdate"), ByteBufferUtil.bytes(34L), 0); |
| rm.add(CF_INDEX1, cellname("notbirthdate"), ByteBufferUtil.bytes((long) (i % 2)), 0); |
| rm.applyUnsafe(); |
| } |
| |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(34L)); |
| IndexExpression expr2 = new IndexExpression(ByteBufferUtil.bytes("notbirthdate"), Operator.EQ, ByteBufferUtil.bytes(1L)); |
| List<IndexExpression> clause = Arrays.asList(expr, expr2); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = cfs.search(range, clause, filter, 100); |
| |
| assert rows != null; |
| assert rows.size() == 50 : rows.size(); |
| Set<DecoratedKey> keys = new HashSet<DecoratedKey>(); |
| // extra check that there are no duplicate results -- see https://issues.apache.org/jira/browse/CASSANDRA-2406 |
| for (Row row : rows) |
| keys.add(row.key); |
| assert rows.size() == keys.size(); |
| } |
| |
| @Test |
| public void testIndexDeletions() throws IOException |
| { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE3).getColumnFamilyStore(CF_INDEX1); |
| Mutation rm; |
| |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, cellname("birthdate"), ByteBufferUtil.bytes(1L), 0); |
| rm.applyUnsafe(); |
| |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(1L)); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = cfs.search(range, clause, filter, 100); |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| String key = ByteBufferUtil.string(rows.get(0).key.getKey()); |
| assert "k1".equals( key ); |
| |
| // delete the column directly |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.delete(CF_INDEX1, cellname("birthdate"), 1); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.isEmpty(); |
| |
| // verify that it's not being indexed under the deletion column value either |
| Cell deletion = rm.getColumnFamilies().iterator().next().iterator().next(); |
| ByteBuffer deletionLong = ByteBufferUtil.bytes((long) ByteBufferUtil.toInt(deletion.value())); |
| IndexExpression expr0 = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, deletionLong); |
| List<IndexExpression> clause0 = Arrays.asList(expr0); |
| rows = cfs.search(range, clause0, filter, 100); |
| assert rows.isEmpty(); |
| |
| // resurrect w/ a newer timestamp |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, cellname("birthdate"), ByteBufferUtil.bytes(1L), 2); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| key = ByteBufferUtil.string(rows.get(0).key.getKey()); |
| assert "k1".equals( key ); |
| |
| // verify that row and delete w/ older timestamp does nothing |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.delete(CF_INDEX1, 1); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| key = ByteBufferUtil.string(rows.get(0).key.getKey()); |
| assert "k1".equals( key ); |
| |
| // similarly, column delete w/ older timestamp should do nothing |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.delete(CF_INDEX1, cellname("birthdate"), 1); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| key = ByteBufferUtil.string(rows.get(0).key.getKey()); |
| assert "k1".equals( key ); |
| |
| // delete the entire row (w/ newer timestamp this time) |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.delete(CF_INDEX1, 3); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.isEmpty() : StringUtils.join(rows, ","); |
| |
| // make sure obsolete mutations don't generate an index entry |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, cellname("birthdate"), ByteBufferUtil.bytes(1L), 3); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.isEmpty() : StringUtils.join(rows, ","); |
| |
| // try insert followed by row delete in the same mutation |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, cellname("birthdate"), ByteBufferUtil.bytes(1L), 1); |
| rm.delete(CF_INDEX1, 2); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.isEmpty() : StringUtils.join(rows, ","); |
| |
| // try row delete followed by insert in the same mutation |
| rm = new Mutation(KEYSPACE3, ByteBufferUtil.bytes("k1")); |
| rm.delete(CF_INDEX1, 3); |
| rm.add(CF_INDEX1, cellname("birthdate"), ByteBufferUtil.bytes(1L), 4); |
| rm.applyUnsafe(); |
| rows = cfs.search(range, clause, filter, 100); |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| key = ByteBufferUtil.string(rows.get(0).key.getKey()); |
| assert "k1".equals( key ); |
| } |
| |
| @Test |
| public void testIndexUpdate() throws IOException |
| { |
| Keyspace keyspace = Keyspace.open(KEYSPACE2); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_INDEX1); |
| CellName birthdate = cellname("birthdate"); |
| |
| // create a row and update the birthdate value, test that the index query fetches the new version |
| Mutation rm; |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(1L), 1); |
| rm.applyUnsafe(); |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(2L), 2); |
| rm.applyUnsafe(); |
| |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(1L)); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = cfs.search(range, clause, filter, 100); |
| assert rows.size() == 0; |
| |
| expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(2L)); |
| clause = Arrays.asList(expr); |
| rows = keyspace.getColumnFamilyStore(CF_INDEX1).search(range, clause, filter, 100); |
| String key = ByteBufferUtil.string(rows.get(0).key.getKey()); |
| assert "k1".equals( key ); |
| |
| // update the birthdate value with an OLDER timestamp, and test that the index ignores this |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(3L), 0); |
| rm.applyUnsafe(); |
| |
| rows = keyspace.getColumnFamilyStore(CF_INDEX1).search(range, clause, filter, 100); |
| key = ByteBufferUtil.string(rows.get(0).key.getKey()); |
| assert "k1".equals( key ); |
| |
| } |
| |
| @Test |
| public void testIndexUpdateOverwritingExpiringColumns() throws Exception |
| { |
| // see CASSANDRA-7268 |
| Keyspace keyspace = Keyspace.open(KEYSPACE2); |
| |
| // create a row and update the birthdate value with an expiring column |
| Mutation rm; |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("k100")); |
| rm.add("Indexed1", cellname("birthdate"), ByteBufferUtil.bytes(100L), 1, 1000); |
| rm.applyUnsafe(); |
| |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(100L)); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = keyspace.getColumnFamilyStore("Indexed1").search(range, clause, filter, 100); |
| assertEquals(1, rows.size()); |
| |
| // requires a 1s sleep because we calculate local expiry time as (now() / 1000) + ttl |
| TimeUnit.SECONDS.sleep(1); |
| |
| // now overwrite with the same name/value/ttl, but the local expiry time will be different |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("k100")); |
| rm.add("Indexed1", cellname("birthdate"), ByteBufferUtil.bytes(100L), 1, 1000); |
| rm.applyUnsafe(); |
| |
| rows = keyspace.getColumnFamilyStore("Indexed1").search(range, clause, filter, 100); |
| assertEquals(1, rows.size()); |
| |
| // check that modifying the indexed value using the same timestamp behaves as expected |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("k101")); |
| rm.add("Indexed1", cellname("birthdate"), ByteBufferUtil.bytes(101L), 1, 1000); |
| rm.applyUnsafe(); |
| |
| expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(101L)); |
| clause = Arrays.asList(expr); |
| rows = keyspace.getColumnFamilyStore("Indexed1").search(range, clause, filter, 100); |
| assertEquals(1, rows.size()); |
| |
| TimeUnit.SECONDS.sleep(1); |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("k101")); |
| rm.add("Indexed1", cellname("birthdate"), ByteBufferUtil.bytes(102L), 1, 1000); |
| rm.applyUnsafe(); |
| // search for the old value |
| rows = keyspace.getColumnFamilyStore("Indexed1").search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| // and for the new |
| expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(102L)); |
| clause = Arrays.asList(expr); |
| rows = keyspace.getColumnFamilyStore("Indexed1").search(range, clause, filter, 100); |
| assertEquals(1, rows.size()); |
| } |
| |
| @Test |
| public void testDeleteOfInconsistentValuesInKeysIndex() throws Exception |
| { |
| String keySpace = KEYSPACE2; |
| String cfName = CF_INDEX1; |
| |
| Keyspace keyspace = Keyspace.open(keySpace); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.truncateBlocking(); |
| |
| ByteBuffer rowKey = ByteBufferUtil.bytes("k1"); |
| CellName colName = cellname("birthdate"); |
| ByteBuffer val1 = ByteBufferUtil.bytes(1L); |
| ByteBuffer val2 = ByteBufferUtil.bytes(2L); |
| |
| // create a row and update the "birthdate" value, test that the index query fetches this version |
| Mutation rm; |
| rm = new Mutation(keySpace, rowKey); |
| rm.add(cfName, colName, val1, 0); |
| rm.applyUnsafe(); |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, val1); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(1, rows.size()); |
| |
| // force a flush, so our index isn't being read from a memtable |
| keyspace.getColumnFamilyStore(cfName).forceBlockingFlush(); |
| |
| // now apply another update, but force the index update to be skipped |
| rm = new Mutation(keySpace, rowKey); |
| rm.add(cfName, colName, val2, 1); |
| keyspace.apply(rm, true, false); |
| |
| // Now searching the index for either the old or new value should return 0 rows |
| // because the new value was not indexed and the old value should be ignored |
| // (and in fact purged from the index cf). |
| // first check for the old value |
| rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| // now check for the updated value |
| expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, val2); |
| clause = Arrays.asList(expr); |
| filter = new IdentityQueryFilter(); |
| range = Util.range("", ""); |
| rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| |
| // now, reset back to the original value, still skipping the index update, to |
| // make sure the value was expunged from the index when it was discovered to be inconsistent |
| rm = new Mutation(keySpace, rowKey); |
| rm.add(cfName, colName, ByteBufferUtil.bytes(1L), 3); |
| keyspace.apply(rm, true, false); |
| |
| expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(1L)); |
| clause = Arrays.asList(expr); |
| filter = new IdentityQueryFilter(); |
| range = Util.range("", ""); |
| rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| } |
| |
| @Test |
| public void testDeleteOfInconsistentValuesFromCompositeIndex() throws Exception |
| { |
| String keySpace = KEYSPACE2; |
| String cfName = CF_INDEX2; |
| |
| Keyspace keyspace = Keyspace.open(keySpace); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.truncateBlocking(); |
| |
| ByteBuffer rowKey = ByteBufferUtil.bytes("k1"); |
| ByteBuffer clusterKey = ByteBufferUtil.bytes("ck1"); |
| ByteBuffer colName = ByteBufferUtil.bytes("col1"); |
| |
| CellNameType baseComparator = cfs.getComparator(); |
| CellName compositeName = baseComparator.makeCellName(clusterKey, colName); |
| |
| ByteBuffer val1 = ByteBufferUtil.bytes("v1"); |
| ByteBuffer val2 = ByteBufferUtil.bytes("v2"); |
| |
| // create a row and update the author value |
| Mutation rm; |
| rm = new Mutation(keySpace, rowKey); |
| rm.add(cfName, compositeName, val1, 0); |
| rm.applyUnsafe(); |
| |
| // test that the index query fetches this version |
| IndexExpression expr = new IndexExpression(colName, Operator.EQ, val1); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(1, rows.size()); |
| |
| // force a flush and retry the query, so our index isn't being read from a memtable |
| keyspace.getColumnFamilyStore(cfName).forceBlockingFlush(); |
| rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(1, rows.size()); |
| |
| // now apply another update, but force the index update to be skipped |
| rm = new Mutation(keySpace, rowKey); |
| rm.add(cfName, compositeName, val2, 1); |
| keyspace.apply(rm, true, false); |
| |
| // Now searching the index for either the old or new value should return 0 rows |
| // because the new value was not indexed and the old value should be ignored |
| // (and in fact purged from the index cf). |
| // first check for the old value |
| rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| // now check for the updated value |
| expr = new IndexExpression(colName, Operator.EQ, val2); |
| clause = Arrays.asList(expr); |
| filter = new IdentityQueryFilter(); |
| range = Util.range("", ""); |
| rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| |
| // now, reset back to the original value, still skipping the index update, to |
| // make sure the value was expunged from the index when it was discovered to be inconsistent |
| rm = new Mutation(keySpace, rowKey); |
| rm.add(cfName, compositeName, val1, 2); |
| keyspace.apply(rm, true, false); |
| |
| expr = new IndexExpression(colName, Operator.EQ, val1); |
| clause = Arrays.asList(expr); |
| filter = new IdentityQueryFilter(); |
| range = Util.range("", ""); |
| rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| } |
| |
| // See CASSANDRA-6098 |
| @Test |
| public void testDeleteCompositeIndex() throws Exception |
| { |
| String keySpace = KEYSPACE2; |
| String cfName = CF_INDEX3; // has gcGrace 0 |
| |
| Keyspace keyspace = Keyspace.open(keySpace); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.truncateBlocking(); |
| |
| ByteBuffer rowKey = ByteBufferUtil.bytes("k1"); |
| ByteBuffer clusterKey = ByteBufferUtil.bytes("ck1"); |
| ByteBuffer colName = ByteBufferUtil.bytes("col1"); |
| |
| CellNameType baseComparator = cfs.getComparator(); |
| CellName compositeName = baseComparator.makeCellName(clusterKey, colName); |
| |
| ByteBuffer val1 = ByteBufferUtil.bytes("v2"); |
| |
| // Insert indexed value. |
| Mutation rm; |
| rm = new Mutation(keySpace, rowKey); |
| rm.add(cfName, compositeName, val1, 0); |
| rm.applyUnsafe(); |
| |
| // Now delete the value and flush too. |
| rm = new Mutation(keySpace, rowKey); |
| rm.delete(cfName, 1); |
| rm.applyUnsafe(); |
| |
| // We want the data to be gcable, but even if gcGrace == 0, we still need to wait 1 second |
| // since we won't gc on a tie. |
| try { Thread.sleep(1000); } catch (Exception e) {} |
| |
| // Read the index and we check we do get no value (and no NPE) |
| // Note: the index will return the entry because it hasn't been deleted (we |
| // haven't read yet nor compacted) but the data read itself will return null |
| IndexExpression expr = new IndexExpression(colName, Operator.EQ, val1); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = keyspace.getColumnFamilyStore(cfName).search(range, clause, filter, 100); |
| assertEquals(0, rows.size()); |
| } |
| |
| // See CASSANDRA-2628 |
| @Test |
| public void testIndexScanWithLimitOne() |
| { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_INDEX1); |
| Mutation rm; |
| |
| CellName nobirthdate = cellname("notbirthdate"); |
| CellName birthdate = cellname("birthdate"); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("kk1")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.applyUnsafe(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("kk2")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(2L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.applyUnsafe(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("kk3")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(2L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.applyUnsafe(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("kk4")); |
| rm.add(CF_INDEX1, nobirthdate, ByteBufferUtil.bytes(2L), 0); |
| rm.add(CF_INDEX1, birthdate, ByteBufferUtil.bytes(1L), 0); |
| rm.applyUnsafe(); |
| |
| // basic single-expression query |
| IndexExpression expr1 = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(1L)); |
| IndexExpression expr2 = new IndexExpression(ByteBufferUtil.bytes("notbirthdate"), Operator.GT, ByteBufferUtil.bytes(1L)); |
| List<IndexExpression> clause = Arrays.asList(expr1, expr2); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| Range<RowPosition> range = Util.range("", ""); |
| List<Row> rows = cfs.search(range, clause, filter, 1); |
| |
| assert rows != null; |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| } |
| |
| @Test |
| public void testIndexCreate() throws IOException, InterruptedException, ExecutionException |
| { |
| Keyspace keyspace = Keyspace.open(KEYSPACE1); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_INDEX2); |
| |
| // create a row and update the birthdate value, test that the index query fetches the new version |
| Mutation rm; |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("k1")); |
| rm.add(CF_INDEX2, cellname("birthdate"), ByteBufferUtil.bytes(1L), 1); |
| rm.applyUnsafe(); |
| |
| ColumnDefinition old = cfs.metadata.getColumnDefinition(ByteBufferUtil.bytes("birthdate")); |
| ColumnDefinition cd = ColumnDefinition.regularDef(cfs.metadata, old.name.bytes, old.type, null).setIndex("birthdate_index", IndexType.KEYS, null); |
| Future<?> future = cfs.indexManager.addIndexedColumn(cd); |
| future.get(); |
| // we had a bug (CASSANDRA-2244) where index would get created but not flushed -- check for that |
| assert cfs.indexManager.getIndexForColumn(cd.name.bytes).getIndexCfs().getSSTables().size() > 0; |
| |
| queryBirthdate(keyspace); |
| |
| // validate that drop clears it out & rebuild works (CASSANDRA-2320) |
| SecondaryIndex indexedCfs = cfs.indexManager.getIndexForColumn(ByteBufferUtil.bytes("birthdate")); |
| cfs.indexManager.removeIndexedColumn(ByteBufferUtil.bytes("birthdate")); |
| assert !indexedCfs.isIndexBuilt(ByteBufferUtil.bytes("birthdate")); |
| |
| // rebuild & re-query |
| future = cfs.indexManager.addIndexedColumn(cd); |
| future.get(); |
| queryBirthdate(keyspace); |
| } |
| |
| private void queryBirthdate(Keyspace keyspace) throws CharacterCodingException |
| { |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, ByteBufferUtil.bytes(1L)); |
| List<IndexExpression> clause = Arrays.asList(expr); |
| IDiskAtomFilter filter = new IdentityQueryFilter(); |
| List<Row> rows = keyspace.getColumnFamilyStore(CF_INDEX2).search(Util.range("", ""), clause, filter, 100); |
| assert rows.size() == 1 : StringUtils.join(rows, ","); |
| assertEquals("k1", ByteBufferUtil.string(rows.get(0).key.getKey())); |
| } |
| |
| @Test |
| public void testCassandra6778() throws CharacterCodingException |
| { |
| String cfname = CF_STANDARDINT; |
| Keyspace keyspace = Keyspace.open(KEYSPACE1); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfname); |
| |
| // insert two columns that represent the same integer but have different binary forms (the |
| // second one is padded with extra zeros) |
| Mutation rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("k1")); |
| CellName column1 = cellname(ByteBuffer.wrap(new byte[]{1})); |
| rm.add(cfname, column1, ByteBufferUtil.bytes("data1"), 1); |
| rm.applyUnsafe(); |
| cfs.forceBlockingFlush(); |
| |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("k1")); |
| CellName column2 = cellname(ByteBuffer.wrap(new byte[]{0, 0, 1})); |
| rm.add(cfname, column2, ByteBufferUtil.bytes("data2"), 2); |
| rm.applyUnsafe(); |
| cfs.forceBlockingFlush(); |
| |
| // fetch by the first column name; we should get the second version of the column value |
| SliceByNamesReadCommand cmd = new SliceByNamesReadCommand( |
| KEYSPACE1, ByteBufferUtil.bytes("k1"), cfname, System.currentTimeMillis(), |
| new NamesQueryFilter(FBUtilities.singleton(column1, cfs.getComparator()))); |
| |
| ColumnFamily cf = cmd.getRow(keyspace).cf; |
| assertEquals(1, cf.getColumnCount()); |
| Cell cell = cf.getColumn(column1); |
| assertEquals("data2", ByteBufferUtil.string(cell.value())); |
| assertEquals(column2, cell.name()); |
| |
| // fetch by the second column name; we should get the second version of the column value |
| cmd = new SliceByNamesReadCommand( |
| KEYSPACE1, ByteBufferUtil.bytes("k1"), cfname, System.currentTimeMillis(), |
| new NamesQueryFilter(FBUtilities.singleton(column2, cfs.getComparator()))); |
| |
| cf = cmd.getRow(keyspace).cf; |
| assertEquals(1, cf.getColumnCount()); |
| cell = cf.getColumn(column2); |
| assertEquals("data2", ByteBufferUtil.string(cell.value())); |
| assertEquals(column2, cell.name()); |
| } |
| |
| @Test |
| public void testInclusiveBounds() |
| { |
| ColumnFamilyStore cfs = insertKey1Key2(); |
| |
| List<Row> result = cfs.getRangeSlice(Util.bounds("key1", "key2"), |
| null, |
| Util.namesFilter(cfs, "asdf"), |
| 10); |
| assertEquals(2, result.size()); |
| assert result.get(0).key.getKey().equals(ByteBufferUtil.bytes("key1")); |
| } |
| |
| @Test |
| public void testDeleteSuperRowSticksAfterFlush() throws Throwable |
| { |
| String keyspaceName = KEYSPACE1; |
| String cfName= CF_SUPER1; |
| ByteBuffer scfName = ByteBufferUtil.bytes("SuperDuper"); |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| DecoratedKey key = Util.dk("flush-resurrection"); |
| |
| // create an isolated sstable. |
| putColsSuper(cfs, key, scfName, |
| new BufferCell(cellname(1L), ByteBufferUtil.bytes("val1"), 1), |
| new BufferCell(cellname(2L), ByteBufferUtil.bytes("val2"), 1), |
| new BufferCell(cellname(3L), ByteBufferUtil.bytes("val3"), 1)); |
| cfs.forceBlockingFlush(); |
| |
| // insert, don't flush. |
| putColsSuper(cfs, key, scfName, |
| new BufferCell(cellname(4L), ByteBufferUtil.bytes("val4"), 1), |
| new BufferCell(cellname(5L), ByteBufferUtil.bytes("val5"), 1), |
| new BufferCell(cellname(6L), ByteBufferUtil.bytes("val6"), 1)); |
| |
| // verify insert. |
| final SlicePredicate sp = new SlicePredicate(); |
| sp.setSlice_range(new SliceRange()); |
| sp.getSlice_range().setCount(100); |
| sp.getSlice_range().setStart(ArrayUtils.EMPTY_BYTE_ARRAY); |
| sp.getSlice_range().setFinish(ArrayUtils.EMPTY_BYTE_ARRAY); |
| |
| assertRowAndColCount(1, 6, false, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, scfName), 100)); |
| |
| // delete |
| Mutation rm = new Mutation(keyspace.getName(), key.getKey()); |
| rm.deleteRange(cfName, SuperColumns.startOf(scfName), SuperColumns.endOf(scfName), 2); |
| rm.applyUnsafe(); |
| |
| // verify delete. |
| assertRowAndColCount(1, 0, false, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, scfName), 100)); |
| |
| // flush |
| cfs.forceBlockingFlush(); |
| |
| // re-verify delete. |
| assertRowAndColCount(1, 0, false, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, scfName), 100)); |
| |
| // late insert. |
| putColsSuper(cfs, key, scfName, |
| new BufferCell(cellname(4L), ByteBufferUtil.bytes("val4"), 1L), |
| new BufferCell(cellname(7L), ByteBufferUtil.bytes("val7"), 1L)); |
| |
| // re-verify delete. |
| assertRowAndColCount(1, 0, false, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, scfName), 100)); |
| |
| // make sure new writes are recognized. |
| putColsSuper(cfs, key, scfName, |
| new BufferCell(cellname(3L), ByteBufferUtil.bytes("val3"), 3), |
| new BufferCell(cellname(8L), ByteBufferUtil.bytes("val8"), 3), |
| new BufferCell(cellname(9L), ByteBufferUtil.bytes("val9"), 3)); |
| assertRowAndColCount(1, 3, false, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, scfName), 100)); |
| } |
| |
| private static void assertRowAndColCount(int rowCount, int colCount, boolean isDeleted, Collection<Row> rows) throws CharacterCodingException |
| { |
| assert rows.size() == rowCount : "rowcount " + rows.size(); |
| for (Row row : rows) |
| { |
| assert row.cf != null : "cf was null"; |
| assert row.cf.getColumnCount() == colCount : "colcount " + row.cf.getColumnCount() + "|" + str(row.cf); |
| if (isDeleted) |
| assert row.cf.isMarkedForDelete() : "cf not marked for delete"; |
| } |
| } |
| |
| private static String str(ColumnFamily cf) throws CharacterCodingException |
| { |
| StringBuilder sb = new StringBuilder(); |
| for (Cell col : cf.getSortedColumns()) |
| sb.append(String.format("(%s,%s,%d),", ByteBufferUtil.string(col.name().toByteBuffer()), ByteBufferUtil.string(col.value()), col.timestamp())); |
| return sb.toString(); |
| } |
| |
| private static void putColsSuper(ColumnFamilyStore cfs, DecoratedKey key, ByteBuffer scfName, Cell... cols) throws Throwable |
| { |
| ColumnFamily cf = ArrayBackedSortedColumns.factory.create(cfs.keyspace.getName(), cfs.name); |
| for (Cell col : cols) |
| cf.addColumn(col.withUpdatedName(CellNames.compositeDense(scfName, col.name().toByteBuffer()))); |
| Mutation rm = new Mutation(cfs.keyspace.getName(), key.getKey(), cf); |
| rm.applyUnsafe(); |
| } |
| |
| private static void putColsStandard(ColumnFamilyStore cfs, DecoratedKey key, Cell... cols) throws Throwable |
| { |
| ColumnFamily cf = ArrayBackedSortedColumns.factory.create(cfs.keyspace.getName(), cfs.name); |
| for (Cell col : cols) |
| cf.addColumn(col); |
| Mutation rm = new Mutation(cfs.keyspace.getName(), key.getKey(), cf); |
| rm.applyUnsafe(); |
| } |
| |
| @Test |
| public void testDeleteStandardRowSticksAfterFlush() throws Throwable |
| { |
| // test to make sure flushing after a delete doesn't resurrect delted cols. |
| String keyspaceName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| DecoratedKey key = Util.dk("f-flush-resurrection"); |
| |
| SlicePredicate sp = new SlicePredicate(); |
| sp.setSlice_range(new SliceRange()); |
| sp.getSlice_range().setCount(100); |
| sp.getSlice_range().setStart(ArrayUtils.EMPTY_BYTE_ARRAY); |
| sp.getSlice_range().setFinish(ArrayUtils.EMPTY_BYTE_ARRAY); |
| |
| // insert |
| putColsStandard(cfs, key, column("col1", "val1", 1), column("col2", "val2", 1)); |
| assertRowAndColCount(1, 2, false, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, null), 100)); |
| |
| // flush. |
| cfs.forceBlockingFlush(); |
| |
| // insert, don't flush |
| putColsStandard(cfs, key, column("col3", "val3", 1), column("col4", "val4", 1)); |
| assertRowAndColCount(1, 4, false, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, null), 100)); |
| |
| // delete (from sstable and memtable) |
| Mutation rm = new Mutation(keyspace.getName(), key.getKey()); |
| rm.delete(cfs.name, 2); |
| rm.applyUnsafe(); |
| |
| // verify delete |
| assertRowAndColCount(1, 0, true, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, null), 100)); |
| |
| // flush |
| cfs.forceBlockingFlush(); |
| |
| // re-verify delete. // first breakage is right here because of CASSANDRA-1837. |
| assertRowAndColCount(1, 0, true, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, null), 100)); |
| |
| // simulate a 'late' insertion that gets put in after the deletion. should get inserted, but fail on read. |
| putColsStandard(cfs, key, column("col5", "val5", 1), column("col2", "val2", 1)); |
| |
| // should still be nothing there because we deleted this row. 2nd breakage, but was undetected because of 1837. |
| assertRowAndColCount(1, 0, true, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, null), 100)); |
| |
| // make sure that new writes are recognized. |
| putColsStandard(cfs, key, column("col6", "val6", 3), column("col7", "val7", 3)); |
| assertRowAndColCount(1, 2, true, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, null), 100)); |
| |
| // and it remains so after flush. (this wasn't failing before, but it's good to check.) |
| cfs.forceBlockingFlush(); |
| assertRowAndColCount(1, 2, true, cfs.getRangeSlice(Util.range("f", "g"), null, ThriftValidation.asIFilter(sp, cfs.metadata, null), 100)); |
| } |
| |
| |
| private ColumnFamilyStore insertKey1Key2() |
| { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE2).getColumnFamilyStore(CF_STANDARD1); |
| List<Mutation> rms = new LinkedList<>(); |
| Mutation rm; |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("key1")); |
| rm.add(CF_STANDARD1, cellname("Column1"), ByteBufferUtil.bytes("asdf"), 0); |
| rms.add(rm); |
| Util.writeColumnFamily(rms); |
| |
| rm = new Mutation(KEYSPACE2, ByteBufferUtil.bytes("key2")); |
| rm.add(CF_STANDARD1, cellname("Column1"), ByteBufferUtil.bytes("asdf"), 0); |
| rms.add(rm); |
| return Util.writeColumnFamily(rms); |
| } |
| |
| @Test |
| public void testBackupAfterFlush() throws Throwable |
| { |
| ColumnFamilyStore cfs = insertKey1Key2(); |
| |
| for (int version = 1; version <= 2; ++version) |
| { |
| Descriptor existing = new Descriptor(cfs.directories.getDirectoryForNewSSTables(), KEYSPACE2, CF_STANDARD1, version, Descriptor.Type.FINAL); |
| Descriptor desc = new Descriptor(Directories.getBackupsDirectory(existing), KEYSPACE2, CF_STANDARD1, version, Descriptor.Type.FINAL); |
| for (Component c : new Component[]{ Component.DATA, Component.PRIMARY_INDEX, Component.FILTER, Component.STATS }) |
| assertTrue("can not find backedup file:" + desc.filenameFor(c), new File(desc.filenameFor(c)).exists()); |
| } |
| } |
| |
| // CASSANDRA-3467. the key here is that supercolumn and subcolumn comparators are different |
| @Test |
| public void testSliceByNamesCommandOnUUIDTypeSCF() throws Throwable |
| { |
| String keyspaceName = KEYSPACE1; |
| String cfName = CF_SUPER6; |
| ByteBuffer superColName = LexicalUUIDType.instance.fromString("a4ed3562-0e8e-4b41-bdfd-c45a2774682d"); |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| DecoratedKey key = Util.dk("slice-get-uuid-type"); |
| |
| // Insert a row with one supercolumn and multiple subcolumns |
| putColsSuper(cfs, key, superColName, new BufferCell(cellname("a"), ByteBufferUtil.bytes("A"), 1), |
| new BufferCell(cellname("b"), ByteBufferUtil.bytes("B"), 1)); |
| |
| // Get the entire supercolumn like normal |
| ColumnFamily cfGet = cfs.getColumnFamily(QueryFilter.getIdentityFilter(key, cfName, System.currentTimeMillis())); |
| assertEquals(ByteBufferUtil.bytes("A"), cfGet.getColumn(CellNames.compositeDense(superColName, ByteBufferUtil.bytes("a"))).value()); |
| assertEquals(ByteBufferUtil.bytes("B"), cfGet.getColumn(CellNames.compositeDense(superColName, ByteBufferUtil.bytes("b"))).value()); |
| |
| // Now do the SliceByNamesCommand on the supercolumn, passing both subcolumns in as columns to get |
| SortedSet<CellName> sliceColNames = new TreeSet<CellName>(cfs.metadata.comparator); |
| sliceColNames.add(CellNames.compositeDense(superColName, ByteBufferUtil.bytes("a"))); |
| sliceColNames.add(CellNames.compositeDense(superColName, ByteBufferUtil.bytes("b"))); |
| SliceByNamesReadCommand cmd = new SliceByNamesReadCommand(keyspaceName, key.getKey(), cfName, System.currentTimeMillis(), new NamesQueryFilter(sliceColNames)); |
| ColumnFamily cfSliced = cmd.getRow(keyspace).cf; |
| |
| // Make sure the slice returns the same as the straight get |
| assertEquals(ByteBufferUtil.bytes("A"), cfSliced.getColumn(CellNames.compositeDense(superColName, ByteBufferUtil.bytes("a"))).value()); |
| assertEquals(ByteBufferUtil.bytes("B"), cfSliced.getColumn(CellNames.compositeDense(superColName, ByteBufferUtil.bytes("b"))).value()); |
| } |
| |
| @Test |
| public void testSliceByNamesCommandOldMetadata() throws Throwable |
| { |
| String keyspaceName = KEYSPACE1; |
| String cfName= CF_STANDARD1; |
| DecoratedKey key = Util.dk("slice-name-old-metadata"); |
| CellName cname = cellname("c1"); |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.truncateBlocking(); |
| |
| // Create a cell a 'high timestamp' |
| putColsStandard(cfs, key, new BufferCell(cname, ByteBufferUtil.bytes("a"), 2)); |
| cfs.forceBlockingFlush(); |
| |
| // Nuke the metadata and reload that sstable |
| Collection<SSTableReader> ssTables = cfs.getSSTables(); |
| assertEquals(1, ssTables.size()); |
| cfs.clearUnsafe(); |
| assertEquals(0, cfs.getSSTables().size()); |
| |
| new File(ssTables.iterator().next().descriptor.filenameFor(Component.STATS)).delete(); |
| cfs.loadNewSSTables(); |
| |
| // Add another cell with a lower timestamp |
| putColsStandard(cfs, key, new BufferCell(cname, ByteBufferUtil.bytes("b"), 1)); |
| |
| // Test fetching the cell by name returns the first cell |
| SliceByNamesReadCommand cmd = new SliceByNamesReadCommand(keyspaceName, key.getKey(), cfName, System.currentTimeMillis(), new NamesQueryFilter(FBUtilities.singleton(cname, cfs.getComparator()))); |
| ColumnFamily cf = cmd.getRow(keyspace).cf; |
| Cell cell = cf.getColumn(cname); |
| assert cell.value().equals(ByteBufferUtil.bytes("a")) : "expecting a, got " + ByteBufferUtil.string(cell.value()); |
| |
| Keyspace.clear(KEYSPACE1); // CASSANDRA-7195 |
| } |
| |
| private static void assertTotalColCount(Collection<Row> rows, int expectedCount) |
| { |
| int columns = 0; |
| for (Row row : rows) |
| { |
| columns += row.getLiveCount(new SliceQueryFilter(ColumnSlice.ALL_COLUMNS_ARRAY, false, expectedCount), System.currentTimeMillis()); |
| } |
| assert columns == expectedCount : "Expected " + expectedCount + " live columns but got " + columns + ": " + rows; |
| } |
| |
| |
| @Test |
| public void testRangeSliceColumnsLimit() throws Throwable |
| { |
| String keyspaceName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| Cell[] cols = new Cell[5]; |
| for (int i = 0; i < 5; i++) |
| cols[i] = column("c" + i, "value", 1); |
| |
| putColsStandard(cfs, Util.dk("a"), cols[0], cols[1], cols[2], cols[3], cols[4]); |
| putColsStandard(cfs, Util.dk("b"), cols[0], cols[1]); |
| putColsStandard(cfs, Util.dk("c"), cols[0], cols[1], cols[2], cols[3]); |
| cfs.forceBlockingFlush(); |
| |
| SlicePredicate sp = new SlicePredicate(); |
| sp.setSlice_range(new SliceRange()); |
| sp.getSlice_range().setCount(1); |
| sp.getSlice_range().setStart(ArrayUtils.EMPTY_BYTE_ARRAY); |
| sp.getSlice_range().setFinish(ArrayUtils.EMPTY_BYTE_ARRAY); |
| |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 3, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 3); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 5, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 5); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 8, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 8); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 10, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 10); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 100, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 11); |
| |
| // Check that when querying by name, we always include all names for a |
| // gien row even if it means returning more columns than requested (this is necesseray for CQL) |
| sp = new SlicePredicate(); |
| sp.setColumn_names(Arrays.asList( |
| ByteBufferUtil.bytes("c0"), |
| ByteBufferUtil.bytes("c1"), |
| ByteBufferUtil.bytes("c2") |
| )); |
| |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 1, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 3); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 4, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 5); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 5, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 5); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 6, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 8); |
| assertTotalColCount(cfs.getRangeSlice(Util.range("", ""), |
| null, |
| ThriftValidation.asIFilter(sp, cfs.metadata, null), |
| 100, |
| System.currentTimeMillis(), |
| true, |
| false), |
| 8); |
| } |
| |
| @Test |
| public void testRangeSlicePaging() throws Throwable |
| { |
| String keyspaceName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| Cell[] cols = new Cell[4]; |
| for (int i = 0; i < 4; i++) |
| cols[i] = column("c" + i, "value", 1); |
| |
| DecoratedKey ka = Util.dk("a"); |
| DecoratedKey kb = Util.dk("b"); |
| DecoratedKey kc = Util.dk("c"); |
| |
| RowPosition min = Util.rp(""); |
| |
| putColsStandard(cfs, ka, cols[0], cols[1], cols[2], cols[3]); |
| putColsStandard(cfs, kb, cols[0], cols[1], cols[2]); |
| putColsStandard(cfs, kc, cols[0], cols[1], cols[2], cols[3]); |
| cfs.forceBlockingFlush(); |
| |
| SlicePredicate sp = new SlicePredicate(); |
| sp.setSlice_range(new SliceRange()); |
| sp.getSlice_range().setCount(1); |
| sp.getSlice_range().setStart(ArrayUtils.EMPTY_BYTE_ARRAY); |
| sp.getSlice_range().setFinish(ArrayUtils.EMPTY_BYTE_ARRAY); |
| |
| Collection<Row> rows; |
| Row row, row1, row2; |
| IDiskAtomFilter filter = ThriftValidation.asIFilter(sp, cfs.metadata, null); |
| |
| rows = cfs.getRangeSlice(cfs.makeExtendedFilter(Util.range("", ""), filter, null, 3, true, true, System.currentTimeMillis())); |
| assert rows.size() == 1 : "Expected 1 row, got " + toString(rows); |
| row = rows.iterator().next(); |
| assertColumnNames(row, "c0", "c1", "c2"); |
| |
| sp.getSlice_range().setStart(ByteBufferUtil.getArray(ByteBufferUtil.bytes("c2"))); |
| filter = ThriftValidation.asIFilter(sp, cfs.metadata, null); |
| rows = cfs.getRangeSlice(cfs.makeExtendedFilter(new Bounds<RowPosition>(ka, min), filter, null, 3, true, true, System.currentTimeMillis())); |
| assert rows.size() == 2 : "Expected 2 rows, got " + toString(rows); |
| Iterator<Row> iter = rows.iterator(); |
| row1 = iter.next(); |
| row2 = iter.next(); |
| assertColumnNames(row1, "c2", "c3"); |
| assertColumnNames(row2, "c0"); |
| |
| sp.getSlice_range().setStart(ByteBufferUtil.getArray(ByteBufferUtil.bytes("c0"))); |
| filter = ThriftValidation.asIFilter(sp, cfs.metadata, null); |
| rows = cfs.getRangeSlice(cfs.makeExtendedFilter(new Bounds<RowPosition>(row2.key, min), filter, null, 3, true, true, System.currentTimeMillis())); |
| assert rows.size() == 1 : "Expected 1 row, got " + toString(rows); |
| row = rows.iterator().next(); |
| assertColumnNames(row, "c0", "c1", "c2"); |
| |
| sp.getSlice_range().setStart(ByteBufferUtil.getArray(ByteBufferUtil.bytes("c2"))); |
| filter = ThriftValidation.asIFilter(sp, cfs.metadata, null); |
| rows = cfs.getRangeSlice(cfs.makeExtendedFilter(new Bounds<RowPosition>(row.key, min), filter, null, 3, true, true, System.currentTimeMillis())); |
| assert rows.size() == 2 : "Expected 2 rows, got " + toString(rows); |
| iter = rows.iterator(); |
| row1 = iter.next(); |
| row2 = iter.next(); |
| assertColumnNames(row1, "c2"); |
| assertColumnNames(row2, "c0", "c1"); |
| |
| // Paging within bounds |
| SliceQueryFilter sf = new SliceQueryFilter(cellname("c1"), |
| cellname("c2"), |
| false, |
| 0); |
| rows = cfs.getRangeSlice(cfs.makeExtendedFilter(new Bounds<RowPosition>(ka, kc), sf, cellname("c2"), cellname("c1"), null, 2, true, System.currentTimeMillis())); |
| assert rows.size() == 2 : "Expected 2 rows, got " + toString(rows); |
| iter = rows.iterator(); |
| row1 = iter.next(); |
| row2 = iter.next(); |
| assertColumnNames(row1, "c2"); |
| assertColumnNames(row2, "c1"); |
| |
| rows = cfs.getRangeSlice(cfs.makeExtendedFilter(new Bounds<RowPosition>(kb, kc), sf, cellname("c1"), cellname("c1"), null, 10, true, System.currentTimeMillis())); |
| assert rows.size() == 2 : "Expected 2 rows, got " + toString(rows); |
| iter = rows.iterator(); |
| row1 = iter.next(); |
| row2 = iter.next(); |
| assertColumnNames(row1, "c1", "c2"); |
| assertColumnNames(row2, "c1"); |
| } |
| |
| private static String toString(Collection<Row> rows) |
| { |
| try |
| { |
| StringBuilder sb = new StringBuilder(); |
| for (Row row : rows) |
| { |
| sb.append("{"); |
| sb.append(ByteBufferUtil.string(row.key.getKey())); |
| sb.append(":"); |
| if (row.cf != null && !row.cf.isEmpty()) |
| { |
| for (Cell c : row.cf) |
| sb.append(" ").append(row.cf.getComparator().getString(c.name())); |
| } |
| sb.append("} "); |
| } |
| return sb.toString(); |
| } |
| catch (Exception e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private static void assertColumnNames(Row row, String ... columnNames) throws Exception |
| { |
| if (row == null || row.cf == null) |
| throw new AssertionError("The row should not be empty"); |
| |
| Iterator<Cell> columns = row.cf.getSortedColumns().iterator(); |
| Iterator<String> names = Arrays.asList(columnNames).iterator(); |
| |
| while (columns.hasNext()) |
| { |
| Cell c = columns.next(); |
| assert names.hasNext() : "Got more columns that expected (first unexpected column: " + ByteBufferUtil.string(c.name().toByteBuffer()) + ")"; |
| String n = names.next(); |
| assert c.name().toByteBuffer().equals(ByteBufferUtil.bytes(n)) : "Expected " + n + ", got " + ByteBufferUtil.string(c.name().toByteBuffer()); |
| } |
| assert !names.hasNext() : "Missing expected column " + names.next(); |
| } |
| |
| private static DecoratedKey idk(int i) |
| { |
| return Util.dk(String.valueOf(i)); |
| } |
| |
| @Test |
| public void testRangeSliceInclusionExclusion() throws Throwable |
| { |
| String keyspaceName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| Cell[] cols = new Cell[5]; |
| for (int i = 0; i < 5; i++) |
| cols[i] = column("c" + i, "value", 1); |
| |
| for (int i = 0; i <= 9; i++) |
| { |
| putColsStandard(cfs, idk(i), column("name", "value", 1)); |
| } |
| cfs.forceBlockingFlush(); |
| |
| SlicePredicate sp = new SlicePredicate(); |
| sp.setSlice_range(new SliceRange()); |
| sp.getSlice_range().setCount(1); |
| sp.getSlice_range().setStart(ArrayUtils.EMPTY_BYTE_ARRAY); |
| sp.getSlice_range().setFinish(ArrayUtils.EMPTY_BYTE_ARRAY); |
| IDiskAtomFilter qf = ThriftValidation.asIFilter(sp, cfs.metadata, null); |
| |
| List<Row> rows; |
| |
| // Start and end inclusive |
| rows = cfs.getRangeSlice(new Bounds<RowPosition>(rp("2"), rp("7")), null, qf, 100); |
| assert rows.size() == 6; |
| assert rows.get(0).key.equals(idk(2)); |
| assert rows.get(rows.size() - 1).key.equals(idk(7)); |
| |
| // Start and end excluded |
| rows = cfs.getRangeSlice(new ExcludingBounds<RowPosition>(rp("2"), rp("7")), null, qf, 100); |
| assert rows.size() == 4; |
| assert rows.get(0).key.equals(idk(3)); |
| assert rows.get(rows.size() - 1).key.equals(idk(6)); |
| |
| // Start excluded, end included |
| rows = cfs.getRangeSlice(new Range<RowPosition>(rp("2"), rp("7")), null, qf, 100); |
| assert rows.size() == 5; |
| assert rows.get(0).key.equals(idk(3)); |
| assert rows.get(rows.size() - 1).key.equals(idk(7)); |
| |
| // Start included, end excluded |
| rows = cfs.getRangeSlice(new IncludingExcludingBounds<RowPosition>(rp("2"), rp("7")), null, qf, 100); |
| assert rows.size() == 5; |
| assert rows.get(0).key.equals(idk(2)); |
| assert rows.get(rows.size() - 1).key.equals(idk(6)); |
| } |
| |
| @Test |
| public void testKeysSearcher() throws Exception |
| { |
| // Create secondary index and flush to disk |
| Keyspace keyspace = Keyspace.open(KEYSPACE1); |
| ColumnFamilyStore store = keyspace.getColumnFamilyStore(CF_INDEX1); |
| |
| store.truncateBlocking(); |
| |
| for (int i = 0; i < 10; i++) |
| { |
| ByteBuffer key = ByteBufferUtil.bytes(String.valueOf("k" + i)); |
| Mutation rm = new Mutation(KEYSPACE1, key); |
| rm.add(CF_INDEX1, cellname("birthdate"), LongType.instance.decompose(1L), System.currentTimeMillis()); |
| rm.applyUnsafe(); |
| } |
| |
| store.forceBlockingFlush(); |
| |
| IndexExpression expr = new IndexExpression(ByteBufferUtil.bytes("birthdate"), Operator.EQ, LongType.instance.decompose(1L)); |
| // explicitly tell to the KeysSearcher to use column limiting for rowsPerQuery to trigger bogus columnsRead--; (CASSANDRA-3996) |
| List<Row> rows = store.search(store.makeExtendedFilter(Util.range("", ""), new IdentityQueryFilter(), Arrays.asList(expr), 10, true, false, System.currentTimeMillis())); |
| |
| assert rows.size() == 10; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testMultiRangeSomeEmptyNoIndex() throws Throwable |
| { |
| // in order not to change thrift interfaces at this stage we build SliceQueryFilter |
| // directly instead of using QueryFilter to build it for us |
| ColumnSlice[] ranges = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colA")), |
| new ColumnSlice(cellname("colC"), cellname("colE")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colI"), Composites.EMPTY) }; |
| |
| ColumnSlice[] rangesReversed = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colI")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colE"), cellname("colC")), |
| new ColumnSlice(cellname("colA"), Composites.EMPTY) }; |
| |
| String tableName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace table = Keyspace.open(tableName); |
| ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| String[] letters = new String[] { "a", "b", "c", "d", "i" }; |
| Cell[] cols = new Cell[letters.length]; |
| for (int i = 0; i < cols.length; i++) |
| { |
| cols[i] = new BufferCell(cellname("col" + letters[i].toUpperCase()), |
| ByteBuffer.wrap(new byte[1]), 1); |
| } |
| |
| putColsStandard(cfs, dk("a"), cols); |
| |
| cfs.forceBlockingFlush(); |
| |
| SliceQueryFilter multiRangeForward = new SliceQueryFilter(ranges, false, 100); |
| SliceQueryFilter multiRangeForwardWithCounting = new SliceQueryFilter(ranges, false, 3); |
| SliceQueryFilter multiRangeReverse = new SliceQueryFilter(rangesReversed, true, 100); |
| SliceQueryFilter multiRangeReverseWithCounting = new SliceQueryFilter(rangesReversed, true, 3); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForward, "a", "colA", "colC", "colD", "colI"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForwardWithCounting, "a", "colA", "colC", "colD"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverse, "a", "colI", "colD", "colC", "colA"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverseWithCounting, "a", "colI", "colD", "colC"); |
| } |
| |
| @Test |
| public void testClearEphemeralSnapshots() throws Throwable |
| { |
| // We don't do snapshot-based repair on Windows so we don't have ephemeral snapshots from repair that need clearing. |
| // This test will fail as we'll revert to the WindowsFailedSnapshotTracker and counts will be off, but since we |
| // don't do snapshot-based repair on Windows, we just skip this test. |
| Assume.assumeTrue(!FBUtilities.isWindows()); |
| |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE1).getColumnFamilyStore(CF_INDEX1); |
| |
| //cleanup any previous test gargbage |
| cfs.clearSnapshot(""); |
| |
| Mutation rm; |
| for (int i = 0; i < 100; i++) |
| { |
| rm = new Mutation(KEYSPACE1, ByteBufferUtil.bytes("key" + i)); |
| rm.add(CF_INDEX1, cellname("birthdate"), ByteBufferUtil.bytes(34L), 0); |
| rm.add(CF_INDEX1, cellname("notbirthdate"), ByteBufferUtil.bytes((long) (i % 2)), 0); |
| rm.applyUnsafe(); |
| } |
| |
| cfs.snapshot("nonEphemeralSnapshot", null, false); |
| cfs.snapshot("ephemeralSnapshot", null, true); |
| |
| Map<String, Pair<Long, Long>> snapshotDetails = cfs.getSnapshotDetails(); |
| assertEquals(2, snapshotDetails.size()); |
| assertTrue(snapshotDetails.containsKey("ephemeralSnapshot")); |
| assertTrue(snapshotDetails.containsKey("nonEphemeralSnapshot")); |
| |
| ColumnFamilyStore.clearEphemeralSnapshots(cfs.directories); |
| |
| snapshotDetails = cfs.getSnapshotDetails(); |
| assertEquals(1, snapshotDetails.size()); |
| assertTrue(snapshotDetails.containsKey("nonEphemeralSnapshot")); |
| |
| //test cleanup |
| cfs.clearSnapshot(""); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testMultiRangeSomeEmptyIndexed() throws Throwable |
| { |
| // in order not to change thrift interfaces at this stage we build SliceQueryFilter |
| // directly instead of using QueryFilter to build it for us |
| ColumnSlice[] ranges = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colA")), |
| new ColumnSlice(cellname("colC"), cellname("colE")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colI"), Composites.EMPTY) }; |
| |
| ColumnSlice[] rangesReversed = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colI")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colE"), cellname("colC")), |
| new ColumnSlice(cellname("colA"), Composites.EMPTY) }; |
| |
| String tableName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace table = Keyspace.open(tableName); |
| ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| String[] letters = new String[] { "a", "b", "c", "d", "i" }; |
| Cell[] cols = new Cell[letters.length]; |
| for (int i = 0; i < cols.length; i++) |
| { |
| cols[i] = new BufferCell(cellname("col" + letters[i].toUpperCase()), |
| ByteBuffer.wrap(new byte[1366]), 1); |
| } |
| |
| putColsStandard(cfs, dk("a"), cols); |
| |
| cfs.forceBlockingFlush(); |
| |
| SliceQueryFilter multiRangeForward = new SliceQueryFilter(ranges, false, 100); |
| SliceQueryFilter multiRangeForwardWithCounting = new SliceQueryFilter(ranges, false, 3); |
| SliceQueryFilter multiRangeReverse = new SliceQueryFilter(rangesReversed, true, 100); |
| SliceQueryFilter multiRangeReverseWithCounting = new SliceQueryFilter(rangesReversed, true, 3); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForward, "a", "colA", "colC", "colD", "colI"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForwardWithCounting, "a", "colA", "colC", "colD"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverse, "a", "colI", "colD", "colC", "colA"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverseWithCounting, "a", "colI", "colD", "colC"); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testMultiRangeContiguousNoIndex() throws Throwable |
| { |
| // in order not to change thrift interfaces at this stage we build SliceQueryFilter |
| // directly instead of using QueryFilter to build it for us |
| ColumnSlice[] ranges = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colA")), |
| new ColumnSlice(cellname("colC"), cellname("colE")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colI"), Composites.EMPTY) }; |
| |
| ColumnSlice[] rangesReversed = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colI")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colE"), cellname("colC")), |
| new ColumnSlice(cellname("colA"), Composites.EMPTY) }; |
| |
| String tableName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace table = Keyspace.open(tableName); |
| ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| String[] letters = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i" }; |
| Cell[] cols = new Cell[letters.length]; |
| for (int i = 0; i < cols.length; i++) |
| { |
| cols[i] = new BufferCell(cellname("col" + letters[i].toUpperCase()), |
| ByteBuffer.wrap(new byte[1]), 1); |
| } |
| |
| putColsStandard(cfs, dk("a"), cols); |
| |
| cfs.forceBlockingFlush(); |
| |
| SliceQueryFilter multiRangeForward = new SliceQueryFilter(ranges, false, 100); |
| SliceQueryFilter multiRangeForwardWithCounting = new SliceQueryFilter(ranges, false, 3); |
| SliceQueryFilter multiRangeReverse = new SliceQueryFilter(rangesReversed, true, 100); |
| SliceQueryFilter multiRangeReverseWithCounting = new SliceQueryFilter(rangesReversed, true, 3); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForward, "a", "colA", "colC", "colD", "colE", "colF", "colG", "colI"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForwardWithCounting, "a", "colA", "colC", "colD"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverse, "a", "colI", "colG", "colF", "colE", "colD", "colC", "colA"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverseWithCounting, "a", "colI", "colG", "colF"); |
| |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testMultiRangeContiguousIndexed() throws Throwable |
| { |
| // in order not to change thrift interfaces at this stage we build SliceQueryFilter |
| // directly instead of using QueryFilter to build it for us |
| ColumnSlice[] ranges = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colA")), |
| new ColumnSlice(cellname("colC"), cellname("colE")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colI"), Composites.EMPTY) }; |
| |
| ColumnSlice[] rangesReversed = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colI")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colF"), cellname("colF")), |
| new ColumnSlice(cellname("colE"), cellname("colC")), |
| new ColumnSlice(cellname("colA"), Composites.EMPTY) }; |
| |
| String tableName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace table = Keyspace.open(tableName); |
| ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| String[] letters = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i" }; |
| Cell[] cols = new Cell[letters.length]; |
| for (int i = 0; i < cols.length; i++) |
| { |
| cols[i] = new BufferCell(cellname("col" + letters[i].toUpperCase()), |
| ByteBuffer.wrap(new byte[1366]), 1); |
| } |
| |
| putColsStandard(cfs, dk("a"), cols); |
| |
| cfs.forceBlockingFlush(); |
| |
| SliceQueryFilter multiRangeForward = new SliceQueryFilter(ranges, false, 100); |
| SliceQueryFilter multiRangeForwardWithCounting = new SliceQueryFilter(ranges, false, 3); |
| SliceQueryFilter multiRangeReverse = new SliceQueryFilter(rangesReversed, true, 100); |
| SliceQueryFilter multiRangeReverseWithCounting = new SliceQueryFilter(rangesReversed, true, 3); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForward, "a", "colA", "colC", "colD", "colE", "colF", "colG", "colI"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForwardWithCounting, "a", "colA", "colC", "colD"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverse, "a", "colI", "colG", "colF", "colE", "colD", "colC", "colA"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverseWithCounting, "a", "colI", "colG", "colF"); |
| |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testMultiRangeIndexed() throws Throwable |
| { |
| // in order not to change thrift interfaces at this stage we build SliceQueryFilter |
| // directly instead of using QueryFilter to build it for us |
| ColumnSlice[] ranges = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colA")), |
| new ColumnSlice(cellname("colC"), cellname("colE")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colI"), Composites.EMPTY) }; |
| |
| ColumnSlice[] rangesReversed = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colI")), |
| new ColumnSlice(cellname("colG"), cellname("colG")), |
| new ColumnSlice(cellname("colE"), cellname("colC")), |
| new ColumnSlice(cellname("colA"), Composites.EMPTY) }; |
| |
| String keyspaceName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| String[] letters = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i" }; |
| Cell[] cols = new Cell[letters.length]; |
| for (int i = 0; i < cols.length; i++) |
| { |
| cols[i] = new BufferCell(cellname("col" + letters[i].toUpperCase()), |
| // use 1366 so that three cols make an index segment |
| ByteBuffer.wrap(new byte[1366]), 1); |
| } |
| |
| putColsStandard(cfs, dk("a"), cols); |
| |
| cfs.forceBlockingFlush(); |
| |
| // this setup should generate the following row (assuming indexes are of 4Kb each): |
| // [colA, colB, colC, colD, colE, colF, colG, colH, colI] |
| // indexed as: |
| // index0 [colA, colC] |
| // index1 [colD, colF] |
| // index2 [colG, colI] |
| // and we're looking for the ranges: |
| // range0 [____, colA] |
| // range1 [colC, colE] |
| // range2 [colG, ColG] |
| // range3 [colI, ____] |
| |
| SliceQueryFilter multiRangeForward = new SliceQueryFilter(ranges, false, 100); |
| SliceQueryFilter multiRangeForwardWithCounting = new SliceQueryFilter(ranges, false, 3); |
| SliceQueryFilter multiRangeReverse = new SliceQueryFilter(rangesReversed, true, 100); |
| SliceQueryFilter multiRangeReverseWithCounting = new SliceQueryFilter(rangesReversed, true, 3); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForward, "a", "colA", "colC", "colD", "colE", "colG", "colI"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeForwardWithCounting, "a", "colA", "colC", "colD"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverse, "a", "colI", "colG", "colE", "colD", "colC", "colA"); |
| findRowGetSlicesAndAssertColsFound(cfs, multiRangeReverseWithCounting, "a", "colI", "colG", "colE"); |
| |
| } |
| |
| @Test |
| public void testMultipleRangesSlicesNoIndexedColumns() throws Throwable |
| { |
| // small values so that cols won't be indexed |
| testMultiRangeSlicesBehavior(prepareMultiRangeSlicesTest(10, true)); |
| } |
| |
| @Test |
| public void testMultipleRangesSlicesWithIndexedColumns() throws Throwable |
| { |
| // min val size before cols are indexed is 4kb while testing so lets make sure cols are indexed |
| testMultiRangeSlicesBehavior(prepareMultiRangeSlicesTest(1024, true)); |
| } |
| |
| @Test |
| public void testMultipleRangesSlicesInMemory() throws Throwable |
| { |
| // small values so that cols won't be indexed |
| testMultiRangeSlicesBehavior(prepareMultiRangeSlicesTest(10, false)); |
| } |
| |
| @Test |
| public void testRemoveUnfinishedCompactionLeftovers() throws Throwable |
| { |
| String ks = KEYSPACE1; |
| String cf = CF_STANDARD3; // should be empty |
| |
| final CFMetaData cfmeta = Schema.instance.getCFMetaData(ks, cf); |
| Directories dir = new Directories(cfmeta); |
| ByteBuffer key = bytes("key"); |
| |
| // 1st sstable |
| SSTableSimpleWriter writer = new SSTableSimpleWriter(dir.getDirectoryForNewSSTables(), cfmeta, StorageService.getPartitioner()); |
| writer.newRow(key); |
| writer.addColumn(bytes("col"), bytes("val"), 1); |
| writer.close(); |
| |
| Map<Descriptor, Set<Component>> sstables = dir.sstableLister().list(); |
| assertEquals(1, sstables.size()); |
| |
| Map.Entry<Descriptor, Set<Component>> sstableToOpen = sstables.entrySet().iterator().next(); |
| final SSTableReader sstable1 = SSTableReader.open(sstableToOpen.getKey()); |
| |
| // simulate incomplete compaction |
| writer = new SSTableSimpleWriter(dir.getDirectoryForNewSSTables(), |
| cfmeta, StorageService.getPartitioner()) |
| { |
| protected SSTableWriter getWriter() |
| { |
| MetadataCollector collector = new MetadataCollector(cfmeta.comparator); |
| collector.addAncestor(sstable1.descriptor.generation); // add ancestor from previously written sstable |
| return SSTableWriter.create(createDescriptor(directory, metadata.ksName, metadata.cfName, DatabaseDescriptor.getSSTableFormat()), |
| 0L, |
| ActiveRepairService.UNREPAIRED_SSTABLE, |
| metadata, |
| DatabaseDescriptor.getPartitioner(), |
| collector); |
| } |
| }; |
| writer.newRow(key); |
| writer.addColumn(bytes("col"), bytes("val"), 1); |
| writer.close(); |
| |
| // should have 2 sstables now |
| sstables = dir.sstableLister().list(); |
| assertEquals(2, sstables.size()); |
| |
| SSTableReader sstable2 = SSTableReader.open(sstable1.descriptor); |
| UUID compactionTaskID = SystemKeyspace.startCompaction( |
| Keyspace.open(ks).getColumnFamilyStore(cf), |
| Collections.singleton(sstable2)); |
| |
| Map<Integer, UUID> unfinishedCompaction = new HashMap<>(); |
| unfinishedCompaction.put(sstable1.descriptor.generation, compactionTaskID); |
| ColumnFamilyStore.removeUnfinishedCompactionLeftovers(cfmeta, unfinishedCompaction); |
| |
| // 2nd sstable should be removed (only 1st sstable exists in set of size 1) |
| sstables = dir.sstableLister().list(); |
| assertEquals(1, sstables.size()); |
| assertTrue(sstables.containsKey(sstable1.descriptor)); |
| |
| Map<Pair<String, String>, Map<Integer, UUID>> unfinished = SystemKeyspace.getUnfinishedCompactions(); |
| assertTrue(unfinished.isEmpty()); |
| sstable1.selfRef().release(); |
| sstable2.selfRef().release(); |
| } |
| |
| /** |
| * @see <a href="https://issues.apache.org/jira/browse/CASSANDRA-6086">CASSANDRA-6086</a> |
| */ |
| @Test |
| public void testFailedToRemoveUnfinishedCompactionLeftovers() throws Throwable |
| { |
| final String ks = KEYSPACE1; |
| final String cf = CF_STANDARD4; // should be empty |
| |
| final CFMetaData cfmeta = Schema.instance.getCFMetaData(ks, cf); |
| Directories dir = new Directories(cfmeta); |
| ByteBuffer key = bytes("key"); |
| |
| // Write SSTable generation 3 that has ancestors 1 and 2 |
| final Set<Integer> ancestors = Sets.newHashSet(1, 2); |
| SSTableSimpleWriter writer = new SSTableSimpleWriter(dir.getDirectoryForNewSSTables(), |
| cfmeta, StorageService.getPartitioner()) |
| { |
| protected SSTableWriter getWriter() |
| { |
| MetadataCollector collector = new MetadataCollector(cfmeta.comparator); |
| for (int ancestor : ancestors) |
| collector.addAncestor(ancestor); |
| String file = new Descriptor(directory, ks, cf, 3, Descriptor.Type.TEMP).filenameFor(Component.DATA); |
| return SSTableWriter.create(Descriptor.fromFilename(file), |
| 0L, |
| ActiveRepairService.UNREPAIRED_SSTABLE, |
| metadata, |
| StorageService.getPartitioner(), |
| collector); |
| } |
| }; |
| writer.newRow(key); |
| writer.addColumn(bytes("col"), bytes("val"), 1); |
| writer.close(); |
| |
| Map<Descriptor, Set<Component>> sstables = dir.sstableLister().list(); |
| assert sstables.size() == 1; |
| |
| Map.Entry<Descriptor, Set<Component>> sstableToOpen = sstables.entrySet().iterator().next(); |
| final SSTableReader sstable1 = SSTableReader.open(sstableToOpen.getKey()); |
| |
| // simulate we don't have generation in compaction_history |
| Map<Integer, UUID> unfinishedCompactions = new HashMap<>(); |
| UUID compactionTaskID = UUID.randomUUID(); |
| for (Integer ancestor : ancestors) |
| unfinishedCompactions.put(ancestor, compactionTaskID); |
| ColumnFamilyStore.removeUnfinishedCompactionLeftovers(cfmeta, unfinishedCompactions); |
| |
| // SSTable should not be deleted |
| sstables = dir.sstableLister().list(); |
| assert sstables.size() == 1; |
| assert sstables.containsKey(sstable1.descriptor); |
| } |
| |
| @Test |
| public void testLoadNewSSTablesAvoidsOverwrites() throws Throwable |
| { |
| String ks = KEYSPACE1; |
| String cf = CF_STANDARD5; |
| ColumnFamilyStore cfs = Keyspace.open(ks).getColumnFamilyStore(cf); |
| cfs.truncateBlocking(); |
| SSTableDeletingTask.waitForDeletions(); |
| |
| final CFMetaData cfmeta = Schema.instance.getCFMetaData(ks, cf); |
| Directories dir = new Directories(cfs.metadata); |
| |
| // clear old SSTables (probably left by CFS.clearUnsafe() calls in other tests) |
| for (Map.Entry<Descriptor, Set<Component>> entry : dir.sstableLister().list().entrySet()) |
| { |
| for (Component component : entry.getValue()) |
| { |
| FileUtils.delete(entry.getKey().filenameFor(component)); |
| } |
| } |
| |
| // sanity check |
| int existingSSTables = dir.sstableLister().list().keySet().size(); |
| assert existingSSTables == 0 : String.format("%d SSTables unexpectedly exist", existingSSTables); |
| |
| ByteBuffer key = bytes("key"); |
| |
| SSTableSimpleWriter writer = new SSTableSimpleWriter(dir.getDirectoryForNewSSTables(), |
| cfmeta, StorageService.getPartitioner()) |
| { |
| @Override |
| protected SSTableWriter getWriter() |
| { |
| // hack for reset generation |
| generation.set(0); |
| return super.getWriter(); |
| } |
| }; |
| writer.newRow(key); |
| writer.addColumn(bytes("col"), bytes("val"), 1); |
| writer.close(); |
| |
| writer = new SSTableSimpleWriter(dir.getDirectoryForNewSSTables(), |
| cfmeta, StorageService.getPartitioner()); |
| writer.newRow(key); |
| writer.addColumn(bytes("col"), bytes("val"), 1); |
| writer.close(); |
| |
| Set<Integer> generations = new HashSet<>(); |
| for (Descriptor descriptor : dir.sstableLister().list().keySet()) |
| generations.add(descriptor.generation); |
| |
| // we should have two generations: [1, 2] |
| assertEquals(2, generations.size()); |
| assertTrue(generations.contains(1)); |
| assertTrue(generations.contains(2)); |
| |
| assertEquals(0, cfs.getSSTables().size()); |
| |
| // start the generation counter at 1 again (other tests have incremented it already) |
| cfs.resetFileIndexGenerator(); |
| |
| boolean incrementalBackupsEnabled = DatabaseDescriptor.isIncrementalBackupsEnabled(); |
| try |
| { |
| // avoid duplicate hardlinks to incremental backups |
| DatabaseDescriptor.setIncrementalBackupsEnabled(false); |
| cfs.loadNewSSTables(); |
| } |
| finally |
| { |
| DatabaseDescriptor.setIncrementalBackupsEnabled(incrementalBackupsEnabled); |
| } |
| |
| assertEquals(2, cfs.getSSTables().size()); |
| generations = new HashSet<>(); |
| for (Descriptor descriptor : dir.sstableLister().list().keySet()) |
| generations.add(descriptor.generation); |
| |
| // normally they would get renamed to generations 1 and 2, but since those filenames already exist, |
| // they get skipped and we end up with generations 3 and 4 |
| assertEquals(2, generations.size()); |
| assertTrue(generations.contains(3)); |
| assertTrue(generations.contains(4)); |
| } |
| |
| private ColumnFamilyStore prepareMultiRangeSlicesTest(int valueSize, boolean flush) throws Throwable |
| { |
| String keyspaceName = KEYSPACE1; |
| String cfName = CF_STANDARD1; |
| Keyspace keyspace = Keyspace.open(keyspaceName); |
| ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(cfName); |
| cfs.clearUnsafe(); |
| |
| String[] letters = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" }; |
| Cell[] cols = new Cell[12]; |
| for (int i = 0; i < cols.length; i++) |
| { |
| cols[i] = new BufferCell(cellname("col" + letters[i]), ByteBuffer.wrap(new byte[valueSize]), 1); |
| } |
| |
| for (int i = 0; i < 12; i++) |
| { |
| putColsStandard(cfs, dk(letters[i]), Arrays.copyOfRange(cols, 0, i + 1)); |
| } |
| |
| if (flush) |
| { |
| cfs.forceBlockingFlush(); |
| } |
| else |
| { |
| // The intent is to validate memtable code, so check we really didn't flush |
| assert cfs.getSSTables().isEmpty(); |
| } |
| |
| return cfs; |
| } |
| |
| private void testMultiRangeSlicesBehavior(ColumnFamilyStore cfs) |
| { |
| // in order not to change thrift interfaces at this stage we build SliceQueryFilter |
| // directly instead of using QueryFilter to build it for us |
| ColumnSlice[] startMiddleAndEndRanges = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colc")), |
| new ColumnSlice(cellname("colf"), cellname("colg")), |
| new ColumnSlice(cellname("colj"), Composites.EMPTY) }; |
| |
| ColumnSlice[] startMiddleAndEndRangesReversed = new ColumnSlice[] { |
| new ColumnSlice(Composites.EMPTY, cellname("colj")), |
| new ColumnSlice(cellname("colg"), cellname("colf")), |
| new ColumnSlice(cellname("colc"), Composites.EMPTY) }; |
| |
| ColumnSlice[] startOnlyRange = |
| new ColumnSlice[] { new ColumnSlice(Composites.EMPTY, cellname("colc")) }; |
| |
| ColumnSlice[] startOnlyRangeReversed = |
| new ColumnSlice[] { new ColumnSlice(cellname("colc"), Composites.EMPTY) }; |
| |
| ColumnSlice[] middleOnlyRanges = |
| new ColumnSlice[] { new ColumnSlice(cellname("colf"), cellname("colg")) }; |
| |
| ColumnSlice[] middleOnlyRangesReversed = |
| new ColumnSlice[] { new ColumnSlice(cellname("colg"), cellname("colf")) }; |
| |
| ColumnSlice[] endOnlyRanges = |
| new ColumnSlice[] { new ColumnSlice(cellname("colj"), Composites.EMPTY) }; |
| |
| ColumnSlice[] endOnlyRangesReversed = |
| new ColumnSlice[] { new ColumnSlice(Composites.EMPTY, cellname("colj")) }; |
| |
| SliceQueryFilter startOnlyFilter = new SliceQueryFilter(startOnlyRange, false, |
| Integer.MAX_VALUE); |
| SliceQueryFilter startOnlyFilterReversed = new SliceQueryFilter(startOnlyRangeReversed, true, |
| Integer.MAX_VALUE); |
| SliceQueryFilter startOnlyFilterWithCounting = new SliceQueryFilter(startOnlyRange, false, 1); |
| SliceQueryFilter startOnlyFilterReversedWithCounting = new SliceQueryFilter(startOnlyRangeReversed, |
| true, 1); |
| |
| SliceQueryFilter middleOnlyFilter = new SliceQueryFilter(middleOnlyRanges, |
| false, |
| Integer.MAX_VALUE); |
| SliceQueryFilter middleOnlyFilterReversed = new SliceQueryFilter(middleOnlyRangesReversed, true, |
| Integer.MAX_VALUE); |
| SliceQueryFilter middleOnlyFilterWithCounting = new SliceQueryFilter(middleOnlyRanges, false, 1); |
| SliceQueryFilter middleOnlyFilterReversedWithCounting = new SliceQueryFilter(middleOnlyRangesReversed, |
| true, 1); |
| |
| SliceQueryFilter endOnlyFilter = new SliceQueryFilter(endOnlyRanges, false, |
| Integer.MAX_VALUE); |
| SliceQueryFilter endOnlyReversed = new SliceQueryFilter(endOnlyRangesReversed, true, |
| Integer.MAX_VALUE); |
| SliceQueryFilter endOnlyWithCounting = new SliceQueryFilter(endOnlyRanges, false, 1); |
| SliceQueryFilter endOnlyWithReversedCounting = new SliceQueryFilter(endOnlyRangesReversed, |
| true, 1); |
| |
| SliceQueryFilter startMiddleAndEndFilter = new SliceQueryFilter(startMiddleAndEndRanges, false, |
| Integer.MAX_VALUE); |
| SliceQueryFilter startMiddleAndEndFilterReversed = new SliceQueryFilter(startMiddleAndEndRangesReversed, true, |
| Integer.MAX_VALUE); |
| SliceQueryFilter startMiddleAndEndFilterWithCounting = new SliceQueryFilter(startMiddleAndEndRanges, false, |
| 1); |
| SliceQueryFilter startMiddleAndEndFilterReversedWithCounting = new SliceQueryFilter( |
| startMiddleAndEndRangesReversed, true, |
| 1); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilter, "a", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversed, "a", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterWithCounting, "a", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversedWithCounting, "a", "cola"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilter, "a", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversed, "a", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterWithCounting, "a", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversedWithCounting, "a", new String[] {}); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyFilter, "a", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyReversed, "a", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithCounting, "a", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithReversedCounting, "a", new String[] {}); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilter, "a", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversed, "a", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterWithCounting, "a", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversedWithCounting, "a", "cola"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilter, "c", "cola", "colb", "colc"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversed, "c", "colc", "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterWithCounting, "c", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversedWithCounting, "c", "colc"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilter, "c", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversed, "c", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterWithCounting, "c", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversedWithCounting, "c", new String[] {}); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyFilter, "c", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyReversed, "c", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithCounting, "c", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithReversedCounting, "c", new String[] {}); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilter, "c", "cola", "colb", "colc"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversed, "c", "colc", "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterWithCounting, "c", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversedWithCounting, "c", "colc"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilter, "f", "cola", "colb", "colc"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversed, "f", "colc", "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterWithCounting, "f", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversedWithCounting, "f", "colc"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilter, "f", "colf"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversed, "f", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterWithCounting, "f", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversedWithCounting, "f", "colf"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyFilter, "f", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyReversed, "f", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithCounting, "f", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithReversedCounting, "f", new String[] {}); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilter, "f", "cola", "colb", "colc", "colf"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversed, "f", "colf", "colc", "colb", |
| "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterWithCounting, "f", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversedWithCounting, "f", "colf"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilter, "h", "cola", "colb", "colc"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversed, "h", "colc", "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterWithCounting, "h", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversedWithCounting, "h", "colc"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilter, "h", "colf", "colg"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversed, "h", "colg", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterWithCounting, "h", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversedWithCounting, "h", "colg"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyFilter, "h", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyReversed, "h", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithCounting, "h", new String[] {}); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithReversedCounting, "h", new String[] {}); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilter, "h", "cola", "colb", "colc", "colf", |
| "colg"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversed, "h", "colg", "colf", "colc", "colb", |
| "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterWithCounting, "h", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversedWithCounting, "h", "colg"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilter, "j", "cola", "colb", "colc"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversed, "j", "colc", "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterWithCounting, "j", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversedWithCounting, "j", "colc"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilter, "j", "colf", "colg"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversed, "j", "colg", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterWithCounting, "j", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversedWithCounting, "j", "colg"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyFilter, "j", "colj"); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyReversed, "j", "colj"); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithCounting, "j", "colj"); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithReversedCounting, "j", "colj"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilter, "j", "cola", "colb", "colc", "colf", "colg", |
| "colj"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversed, "j", "colj", "colg", "colf", "colc", |
| "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterWithCounting, "j", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversedWithCounting, "j", "colj"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilter, "l", "cola", "colb", "colc"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversed, "l", "colc", "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterWithCounting, "l", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startOnlyFilterReversedWithCounting, "l", "colc"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilter, "l", "colf", "colg"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversed, "l", "colg", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterWithCounting, "l", "colf"); |
| findRowGetSlicesAndAssertColsFound(cfs, middleOnlyFilterReversedWithCounting, "l", "colg"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyFilter, "l", "colj", "colk", "coll"); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyReversed, "l", "coll", "colk", "colj"); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithCounting, "l", "colj"); |
| findRowGetSlicesAndAssertColsFound(cfs, endOnlyWithReversedCounting, "l", "coll"); |
| |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilter, "l", "cola", "colb", "colc", "colf", "colg", |
| "colj", "colk", "coll"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversed, "l", "coll", "colk", "colj", "colg", |
| "colf", "colc", "colb", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterWithCounting, "l", "cola"); |
| findRowGetSlicesAndAssertColsFound(cfs, startMiddleAndEndFilterReversedWithCounting, "l", "coll"); |
| } |
| |
| private void findRowGetSlicesAndAssertColsFound(ColumnFamilyStore cfs, SliceQueryFilter filter, String rowKey, |
| String... colNames) |
| { |
| List<Row> rows = cfs.getRangeSlice(new Bounds<RowPosition>(rp(rowKey), rp(rowKey)), |
| null, |
| filter, |
| Integer.MAX_VALUE, |
| System.currentTimeMillis(), |
| false, |
| false); |
| assertSame("unexpected number of rows ", 1, rows.size()); |
| Row row = rows.get(0); |
| Collection<Cell> cols = !filter.isReversed() ? row.cf.getSortedColumns() : row.cf.getReverseSortedColumns(); |
| // printRow(cfs, new String(row.key.key.array()), cols); |
| String[] returnedColsNames = Iterables.toArray(Iterables.transform(cols, new Function<Cell, String>() |
| { |
| public String apply(Cell arg0) |
| { |
| return Util.string(arg0.name().toByteBuffer()); |
| } |
| }), String.class); |
| |
| assertTrue( |
| "Columns did not match. Expected: " + Arrays.toString(colNames) + " but got:" |
| + Arrays.toString(returnedColsNames), Arrays.equals(colNames, returnedColsNames)); |
| int i = 0; |
| for (Cell col : cols) |
| { |
| assertEquals(colNames[i++], Util.string(col.name().toByteBuffer())); |
| } |
| } |
| |
| private void printRow(ColumnFamilyStore cfs, String rowKey, Collection<Cell> cols) |
| { |
| DecoratedKey ROW = Util.dk(rowKey); |
| System.err.println("Original:"); |
| ColumnFamily cf = cfs.getColumnFamily(QueryFilter.getIdentityFilter(ROW, CF_STANDARD1, System.currentTimeMillis())); |
| System.err.println("Row key: " + rowKey + " Cols: " |
| + Iterables.transform(cf.getSortedColumns(), new Function<Cell, String>() |
| { |
| public String apply(Cell arg0) |
| { |
| return Util.string(arg0.name().toByteBuffer()); |
| } |
| })); |
| System.err.println("Filtered:"); |
| Iterable<String> transformed = Iterables.transform(cols, new Function<Cell, String>() |
| { |
| public String apply(Cell arg0) |
| { |
| return Util.string(arg0.name().toByteBuffer()); |
| } |
| }); |
| System.err.println("Row key: " + rowKey + " Cols: " + transformed); |
| } |
| |
| @Test |
| public void testRebuildSecondaryIndex() throws IOException |
| { |
| CellName indexedCellName = cellname("indexed"); |
| Mutation rm; |
| |
| rm = new Mutation(KEYSPACE4, ByteBufferUtil.bytes("k1")); |
| rm.add("Indexed1", indexedCellName, ByteBufferUtil.bytes("foo"), 1); |
| |
| rm.apply(); |
| assertTrue(Arrays.equals("k1".getBytes(), PerRowSecondaryIndexTest.TestIndex.LAST_INDEXED_KEY.array())); |
| |
| Keyspace.open("PerRowSecondaryIndex").getColumnFamilyStore("Indexed1").forceBlockingFlush(); |
| |
| PerRowSecondaryIndexTest.TestIndex.reset(); |
| |
| ColumnFamilyStore.rebuildSecondaryIndex("PerRowSecondaryIndex", "Indexed1", PerRowSecondaryIndexTest.TestIndex.class.getSimpleName()); |
| assertTrue(Arrays.equals("k1".getBytes(), PerRowSecondaryIndexTest.TestIndex.LAST_INDEXED_KEY.array())); |
| |
| PerRowSecondaryIndexTest.TestIndex.reset(); |
| PerRowSecondaryIndexTest.TestIndex.ACTIVE = false; |
| ColumnFamilyStore.rebuildSecondaryIndex("PerRowSecondaryIndex", "Indexed1", PerRowSecondaryIndexTest.TestIndex.class.getSimpleName()); |
| assertNull(PerRowSecondaryIndexTest.TestIndex.LAST_INDEXED_KEY); |
| |
| PerRowSecondaryIndexTest.TestIndex.reset(); |
| } |
| } |