mirror of https://github.com/apache/cassandra
Merge branch 'cassandra-3.11' into trunk
This commit is contained in:
commit
264e2a3aaa
|
|
@ -323,6 +323,8 @@
|
|||
|
||||
3.11.4
|
||||
Merged from 3.0:
|
||||
* Dropped columns can cause reverse sstable iteration to return prematurely (CASSANDRA-14838)
|
||||
* Legacy sstables with multi block range tombstones create invalid bound sequences (CASSANDRA-14823)
|
||||
* Expand range tombstone validation checks to multiple interim request stages (CASSANDRA-14824)
|
||||
* Reverse order reads can return incomplete results (CASSANDRA-14803)
|
||||
* Avoid calling iter.next() in a loop when notifying indexers about range tombstones (CASSANDRA-14794)
|
||||
|
|
|
|||
|
|
@ -325,18 +325,31 @@ public class SSTableReversedIterator extends AbstractSSTableIterator
|
|||
if (super.hasNextInternal())
|
||||
return true;
|
||||
|
||||
// We have nothing more for our current block, move the next one (so the one before on disk).
|
||||
int nextBlockIdx = indexState.currentBlockIdx() - 1;
|
||||
if (nextBlockIdx < 0 || nextBlockIdx < lastBlockIdx)
|
||||
return false;
|
||||
while (true)
|
||||
{
|
||||
// We have nothing more for our current block, move the next one (so the one before on disk).
|
||||
int nextBlockIdx = indexState.currentBlockIdx() - 1;
|
||||
if (nextBlockIdx < 0 || nextBlockIdx < lastBlockIdx)
|
||||
return false;
|
||||
|
||||
// The slice start can be in
|
||||
indexState.setToBlock(nextBlockIdx);
|
||||
readCurrentBlock(true, nextBlockIdx != lastBlockIdx);
|
||||
// since that new block is within the bounds we've computed in setToSlice(), we know there will
|
||||
// always be something matching the slice unless we're on the lastBlockIdx (in which case there
|
||||
// may or may not be results, but if there isn't, we're done for the slice).
|
||||
return iterator.hasNext();
|
||||
// The slice start can be in
|
||||
indexState.setToBlock(nextBlockIdx);
|
||||
readCurrentBlock(true, nextBlockIdx != lastBlockIdx);
|
||||
|
||||
// If an indexed block only contains data for a dropped column, the iterator will be empty, even
|
||||
// though we may still have data to read in subsequent blocks
|
||||
|
||||
// also, for pre-3.0 storage formats, index blocks that only contain a single row and that row crosses
|
||||
// index boundaries, the iterator will be empty even though we haven't read everything we're intending
|
||||
// to read. In that case, we want to read the next index block. This shouldn't be possible in 3.0+
|
||||
// formats (see next comment)
|
||||
if (!iterator.hasNext() && nextBlockIdx > lastBlockIdx)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return iterator.hasNext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.columniterator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.SchemaLoader;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.cql3.UntypedResultSet;
|
||||
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||
import org.apache.cassandra.db.DecoratedKey;
|
||||
import org.apache.cassandra.db.Keyspace;
|
||||
import org.apache.cassandra.db.RowIndexEntry;
|
||||
import org.apache.cassandra.db.marshal.Int32Type;
|
||||
import org.apache.cassandra.io.sstable.format.SSTableReader;
|
||||
import org.apache.cassandra.schema.KeyspaceParams;
|
||||
|
||||
public class SSTableReverseIteratorTest
|
||||
{
|
||||
private static final String KEYSPACE = "ks";
|
||||
private Random random;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupClass()
|
||||
{
|
||||
SchemaLoader.prepareServer();
|
||||
SchemaLoader.createKeyspace(KEYSPACE, KeyspaceParams.simple(1));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
random = new Random(0);
|
||||
}
|
||||
|
||||
private ByteBuffer bytes(int size)
|
||||
{
|
||||
byte[] b = new byte[size];
|
||||
random.nextBytes(b);
|
||||
return ByteBuffer.wrap(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* SSTRI shouldn't bail out if it encounters empty blocks (due to dropped columns)
|
||||
*/
|
||||
@Test
|
||||
public void emptyBlockTolerance()
|
||||
{
|
||||
String table = "empty_block_tolerance";
|
||||
QueryProcessor.executeInternal(String.format("CREATE TABLE %s.%s (k INT, c int, v1 blob, v2 blob, primary key (k, c))", KEYSPACE, table));
|
||||
ColumnFamilyStore tbl = Keyspace.open(KEYSPACE).getColumnFamilyStore(table);
|
||||
assert tbl != null;
|
||||
|
||||
int key = 100;
|
||||
|
||||
QueryProcessor.executeInternal(String.format("UPDATE %s.%s SET v1=?, v2=? WHERE k=? AND c=?", KEYSPACE, table), bytes(8), bytes(8), key, 0);
|
||||
QueryProcessor.executeInternal(String.format("UPDATE %s.%s SET v1=? WHERE k=? AND c=?", KEYSPACE, table), bytes(0x20000), key, 1);
|
||||
QueryProcessor.executeInternal(String.format("UPDATE %s.%s SET v1=? WHERE k=? AND c=?", KEYSPACE, table), bytes(0x20000), key, 2);
|
||||
QueryProcessor.executeInternal(String.format("UPDATE %s.%s SET v1=? WHERE k=? AND c=?", KEYSPACE, table), bytes(0x20000), key, 3);
|
||||
|
||||
tbl.forceBlockingFlush();
|
||||
SSTableReader sstable = Iterables.getOnlyElement(tbl.getLiveSSTables());
|
||||
DecoratedKey dk = tbl.getPartitioner().decorateKey(Int32Type.instance.decompose(key));
|
||||
RowIndexEntry indexEntry = sstable.getPosition(dk, SSTableReader.Operator.EQ);
|
||||
Assert.assertTrue(indexEntry.isIndexed());
|
||||
Assert.assertTrue(indexEntry.columnsIndexCount() > 2);
|
||||
|
||||
// drop v1 so the first 2 index blocks only contain empty unfiltereds
|
||||
QueryProcessor.executeInternal(String.format("ALTER TABLE %s.%s DROP v1", KEYSPACE, table));
|
||||
|
||||
UntypedResultSet result = QueryProcessor.executeInternal(String.format("SELECT v2 FROM %s.%s WHERE k=? ORDER BY c DESC", KEYSPACE, table), key);
|
||||
Assert.assertEquals(1, result.size());
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue