Merge branch 'cassandra-3.0' into cassandra-3.11

This commit is contained in:
Marcus Eriksson 2019-10-30 15:10:41 +01:00
commit 2d90e3c2a4
18 changed files with 144 additions and 15 deletions

View File

@ -1,7 +1,10 @@
3.11.6
Merged from 2.2
Merged from 3.0:
* Ensure legacy rows have primary key livenessinfo when they contain illegal cells (CASSANDRA-15365)
Merged from 2.2:
* In-JVM DTest: Set correct internode message version for upgrade test (CASSANDRA-15371)
3.11.5
* Fix SASI non-literal string comparisons (range operators) (CASSANDRA-15169)
* Make sure user defined compaction transactions are always closed (CASSANDRA-15123)

View File

@ -1292,6 +1292,22 @@ public abstract class LegacyLayout
private LegacyRangeTombstone rowDeletion;
private LegacyRangeTombstone collectionDeletion;
/**
* Used to track if we need to add pk liveness info (row marker) when removing invalid legacy cells.
*
* In 2.1 these invalid cells existed but were not queryable, in this case specifically because they
* represented values for clustering key columns that were written as data cells.
*
* However, the presence (or not) of such cells on an otherwise empty CQL row (or partition) would decide
* if an empty result row were returned for the CQL row (or partition). To maintain this behaviour we
* insert a row marker containing the liveness info of these invalid cells iff we have no other data
* on the row.
*
* See also CASSANDRA-15365
*/
private boolean hasValidCells = false;
private LivenessInfo invalidLivenessInfo = null;
public CellGrouper(CFMetaData metadata, SerializationHelper helper)
{
this(metadata, helper, false);
@ -1318,6 +1334,8 @@ public abstract class LegacyLayout
this.clustering = null;
this.rowDeletion = null;
this.collectionDeletion = null;
this.invalidLivenessInfo = null;
this.hasValidCells = false;
}
public boolean addAtom(LegacyAtom atom)
@ -1327,7 +1345,7 @@ public abstract class LegacyLayout
: addRangeTombstone(atom.asRangeTombstone());
}
public boolean addCell(LegacyCell cell)
private boolean addCell(LegacyCell cell)
{
if (clustering == null)
{
@ -1361,21 +1379,38 @@ public abstract class LegacyLayout
builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(cell.timestamp, cell.localDeletionTime)));
else
builder.addPrimaryKeyLivenessInfo(LivenessInfo.create(cell.timestamp, FAKE_TTL, cell.localDeletionTime));
hasValidCells = true;
}
else if (column.isPrimaryKeyColumn() && metadata.isCQLTable())
{
// SSTables generated offline and side-loaded may include invalid cells which have the column name
// of a primary key column. So that we don't fail when encountering these cells, we treat them the
// same way as 2.1 did, namely we include their clusterings in the new CQL row, but drop the invalid
// column part of the cell
noSpamLogger.warn("Illegal cell name for CQL3 table {}.{}. {} is defined as a primary key column",
metadata.ksName, metadata.cfName, column.name);
if (invalidLivenessInfo != null)
{
// when we have several invalid cells we follow the logic in LivenessInfo#supersedes when picking the PKLI to keep:
LivenessInfo newInvalidLiveness = LivenessInfo.create(cell.timestamp, cell.isTombstone() ? FAKE_TTL : cell.ttl, cell.localDeletionTime);
if (newInvalidLiveness.supersedes(invalidLivenessInfo))
invalidLivenessInfo = newInvalidLiveness;
}
else
{
invalidLivenessInfo = LivenessInfo.create(cell.timestamp, cell.isTombstone() ? FAKE_TTL : cell.ttl, cell.localDeletionTime);
}
return true;
}
else
{
if (collectionDeletion != null && collectionDeletion.start.collectionName.name.equals(column.name) && collectionDeletion.deletionTime.deletes(cell.timestamp))
return true;
if (column.isPrimaryKeyColumn() && metadata.isCQLTable())
{
noSpamLogger.warn("Illegal cell name for CQL3 table {}.{}. {} is defined as a primary key column",
metadata.ksName, metadata.cfName, column.name);
return true;
}
if (helper.includes(column))
{
hasValidCells = true;
CellPath path = null;
if (column.isComplex())
{
@ -1424,6 +1459,7 @@ public abstract class LegacyLayout
{
builder.addRowDeletion(Row.Deletion.regular(tombstone.deletionTime));
rowDeletion = tombstone;
hasValidCells = true;
}
return true;
}
@ -1436,6 +1472,7 @@ public abstract class LegacyLayout
builder.newRow(clustering);
builder.addRowDeletion(Row.Deletion.regular(tombstone.deletionTime));
rowDeletion = tombstone;
hasValidCells = true;
return true;
}
@ -1466,6 +1503,7 @@ public abstract class LegacyLayout
builder.addComplexDeletion(tombstone.start.collectionName, tombstone.deletionTime);
if (rowDeletion == null || tombstone.deletionTime.supersedes(rowDeletion.deletionTime))
collectionDeletion = tombstone;
hasValidCells = true;
return true;
}
@ -1490,6 +1528,8 @@ public abstract class LegacyLayout
public Row getRow()
{
if (!hasValidCells && invalidLivenessInfo != null)
builder.addPrimaryKeyLivenessInfo(invalidLivenessInfo);
return builder.build();
}
}

View File

@ -0,0 +1,8 @@
Digest.sha1
Data.db
Filter.db
Summary.db
Index.db
TOC.txt
CompressionInfo.db
Statistics.db

View File

@ -0,0 +1,8 @@
Data.db
TOC.txt
Filter.db
Summary.db
CompressionInfo.db
Statistics.db
Digest.sha1
Index.db

View File

@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.google.common.collect.Iterables;
import org.junit.After;
import org.junit.Assert;
import org.junit.BeforeClass;
@ -42,13 +43,17 @@ import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.LivenessInfo;
import org.apache.cassandra.db.SinglePartitionSliceCommandTest;
import org.apache.cassandra.db.compaction.Verifier;
import org.apache.cassandra.db.lifecycle.SSTableSet;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.SetType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.rows.RangeTombstoneMarker;
import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.db.rows.Unfiltered;
import org.apache.cassandra.db.rows.UnfilteredRowIterator;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
@ -63,11 +68,11 @@ import org.apache.cassandra.streaming.StreamPlan;
import org.apache.cassandra.streaming.StreamSession;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import static org.apache.cassandra.cql3.CQLTester.assertRows;
import static org.apache.cassandra.cql3.CQLTester.row;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Tests backwards compatibility for SSTables
@ -448,10 +453,15 @@ public class LegacySSTableTest
* ["b:bb:pk","00000002",1555000750634000],
* ["b:bb:v1","bbb",1555000750634000]]}
* ]
* and an extra sstable with only the invalid cell name
* [
* {"key": "3",
* "cells": [["a:aa:pk","68656c6c6f30",1570466358949]]}
* ]
*
*/
QueryProcessor.executeInternal("CREATE TABLE legacy_tables.legacy_ka_with_illegal_cell_names (" +
String table = "legacy_ka_with_illegal_cell_names";
QueryProcessor.executeInternal("CREATE TABLE legacy_tables." + table + " (" +
" pk int," +
" c1 text," +
" c2 text," +
@ -459,10 +469,68 @@ public class LegacySSTableTest
" PRIMARY KEY(pk, c1, c2))");
loadLegacyTable("legacy_%s_with_illegal_cell_names%s", "ka", "");
UntypedResultSet results =
QueryProcessor.executeOnceInternal(
String.format("SELECT * FROM legacy_tables.legacy_ka_with_illegal_cell_names"));
QueryProcessor.executeOnceInternal("SELECT * FROM legacy_tables."+table);
assertRows(results, row(1, "a", "aa", "aaa"), row(2, "b", "bb", "bbb"));
assertRows(results, row(1, "a", "aa", "aaa"), row(2, "b", "bb", "bbb"), row (3, "a", "aa", null));
Keyspace.open("legacy_tables").getColumnFamilyStore(table).forceMajorCompaction();
}
@Test
public void testReadingLegacyTablesWithIllegalCellNamesPKLI() throws Exception {
/**
*
* Makes sure we grab the correct PKLI when we have illegal columns
*
* sstable looks like this:
* [
* {"key": "3",
* "cells": [["a:aa:","",100],
* ["a:aa:pk","6d656570",200]]}
* ]
*/
/*
this generates the stable on 2.1:
CFMetaData metadata = CFMetaData.compile("create table legacy_tables.legacy_ka_with_illegal_cell_names_2 (pk int, c1 text, c2 text, v1 text, primary key (pk, c1, c2))", "legacy_tables");
try (SSTableSimpleUnsortedWriter writer = new SSTableSimpleUnsortedWriter(new File("/tmp/sstable21"),
metadata,
new ByteOrderedPartitioner(),
10))
{
writer.newRow(bytes(3));
writer.addColumn(new BufferCell(Util.cellname("a", "aa", ""), bytes(""), 100));
writer.addColumn(new BufferCell(Util.cellname("a", "aa", "pk"), bytes("meep"), 200));
}
*/
String table = "legacy_ka_with_illegal_cell_names_2";
QueryProcessor.executeInternal("CREATE TABLE legacy_tables." + table + " (" +
" pk int," +
" c1 text," +
" c2 text," +
" v1 text," +
" PRIMARY KEY(pk, c1, c2))");
loadLegacyTable("legacy_%s_with_illegal_cell_names_2%s", "ka", "");
ColumnFamilyStore cfs = Keyspace.open("legacy_tables").getColumnFamilyStore(table);
assertEquals(1, Iterables.size(cfs.getSSTables(SSTableSet.CANONICAL)));
cfs.forceMajorCompaction();
assertEquals(1, Iterables.size(cfs.getSSTables(SSTableSet.CANONICAL)));
SSTableReader sstable = Iterables.getFirst(cfs.getSSTables(SSTableSet.CANONICAL), null);
LivenessInfo livenessInfo = null;
try (ISSTableScanner scanner = sstable.getScanner())
{
while (scanner.hasNext())
{
try (UnfilteredRowIterator iter = scanner.next())
{
while (iter.hasNext())
{
Unfiltered uf = iter.next();
livenessInfo = ((Row)uf).primaryKeyLivenessInfo();
}
}
}
}
assertNotNull(livenessInfo);
assertEquals(100, livenessInfo.timestamp());
}
@Test