This is an automated email from the ASF dual-hosted git repository.
frankgh pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new ce53702764 ninja trunk patch for CASSANDRA-21587
ce53702764 is described below
commit ce5370276452db4e5981af48839861ef4911cbe2
Author: Francisco Guerrero <[email protected]>
AuthorDate: Thu Sep 24 00:12:07 2026 +0000
ninja trunk patch for CASSANDRA-21587
this diff was supposed to be included in
0691eb3319e5fc33a8d393f48120bb7b3ca901da
Avoid megamorphic List.get in ClusteringPrefix.Deserializer
Carry clustering types as AbstractType<?>[] instead of List, so the hot
path does an array load with no virtual dispatch and ClusteringComparator
needs no defensive copy.
patch by Taejin Koo and Francisco Guerrero; reviewed by Taejin Koo, Dmitry
Konstantinov for CASSANDRA-21587
Co-authored-by: koo.taejin <[email protected]>
---
src/java/org/apache/cassandra/db/Clustering.java | 25 ++-
.../cassandra/db/ClusteringBoundOrBoundary.java | 11 +-
.../apache/cassandra/db/ClusteringComparator.java | 34 ++---
.../org/apache/cassandra/db/ClusteringPrefix.java | 44 +++---
.../apache/cassandra/db/SerializationHeader.java | 35 +++--
src/java/org/apache/cassandra/db/Slice.java | 9 +-
src/java/org/apache/cassandra/db/Slices.java | 16 +-
.../cassandra/db/compaction/CursorCompactor.java | 2 +-
.../cassandra/db/compaction/StatefulCursor.java | 3 +-
.../apache/cassandra/db/marshal/AbstractType.java | 2 +
.../cassandra/io/sstable/ClusteringDescriptor.java | 3 +-
.../org/apache/cassandra/io/sstable/IndexInfo.java | 5 +-
.../cassandra/io/sstable/SSTableCursorWriter.java | 2 -
.../io/sstable/metadata/MetadataCollector.java | 9 +-
.../io/sstable/metadata/StatsMetadata.java | 15 +-
.../org/apache/cassandra/schema/TableMetadata.java | 15 +-
.../serializers/AbstractTypeSerializer.java | 27 ++--
.../cassandra/service/pager/PagingState.java | 8 +-
.../cassandra/tools/SSTableMetadataViewer.java | 6 +-
src/java/org/apache/cassandra/tools/Util.java | 8 +-
test/bin/jmh | 9 +-
.../cassandra/harry/model/BytesPartitionState.java | 2 +-
.../ClusteringPrefixDeserializerBench.java | 169 +++++++++++++++++++++
.../microbench/sstable/SSTableCursorPipeUtil.java | 3 +-
.../sstable/SSTableReadingFileCursorBench.java | 3 +-
.../cassandra/db/ClusteringComparatorTest.java | 64 +++++++-
.../cassandra/db/SerializationHeaderTest.java | 90 +++++++++++
.../db/compaction/StatefulCursorTest.java | 2 +-
.../differential/ComplexColumnCursorReadTest.java | 5 +-
.../org/apache/cassandra/db/filter/SliceTest.java | 34 ++---
.../compress/CompressedSequentialWriterTest.java | 4 +-
.../DirectCompressedSequentialWriterTest.java | 3 +-
.../SSTableCursorReaderCopyCellValueTest.java | 3 +-
.../SSTableCursorReaderCorruptCellCountTest.java | 5 +-
.../DataComponentDirectWriteSelectionTest.java | 3 +-
.../io/sstable/format/big/RowIndexEntryTest.java | 4 +-
.../serializers/AbstractTypeSerializerTest.java | 63 ++++++++
.../bytecomparable/ByteSourceSequenceTest.java | 29 ++--
38 files changed, 574 insertions(+), 200 deletions(-)
diff --git a/src/java/org/apache/cassandra/db/Clustering.java
b/src/java/org/apache/cassandra/db/Clustering.java
index 9609aaab1f..a8b7d954cb 100644
--- a/src/java/org/apache/cassandra/db/Clustering.java
+++ b/src/java/org/apache/cassandra/db/Clustering.java
@@ -20,7 +20,6 @@ package org.apache.cassandra.db;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.List;
import org.apache.cassandra.cache.IMeasurableMemory;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -138,14 +137,14 @@ public interface Clustering<V> extends
ClusteringPrefix<V>, IMeasurableMemory
*/
public static class Serializer
{
- public void serialize(Clustering<?> clustering, DataOutputPlus out,
int version, List<AbstractType<?>> types) throws IOException
+ public void serialize(Clustering<?> clustering, DataOutputPlus out,
int version, AbstractType<?>[] types) throws IOException
{
assert clustering != STATIC_CLUSTERING : "We should never
serialize a static clustering";
- assert clustering.size() == types.size() : "Invalid clustering for
the table: " + clustering;
+ assert clustering.size() == types.length : "Invalid clustering for
the table: " + clustering;
ClusteringPrefix.serializer.serializeValuesWithoutSize(clustering,
out, version, types);
}
- public ByteBuffer serialize(Clustering<?> clustering, int version,
List<AbstractType<?>> types)
+ public ByteBuffer serialize(Clustering<?> clustering, int version,
AbstractType<?>[] types)
{
try (DataOutputBuffer buffer = new
DataOutputBuffer((int)serializedSize(clustering, version, types)))
{
@@ -158,27 +157,27 @@ public interface Clustering<V> extends
ClusteringPrefix<V>, IMeasurableMemory
}
}
- public long serializedSize(Clustering<?> clustering, int version,
List<AbstractType<?>> types)
+ public long serializedSize(Clustering<?> clustering, int version,
AbstractType<?>[] types)
{
return
ClusteringPrefix.serializer.valuesWithoutSizeSerializedSize(clustering,
version, types);
}
- public void skip(DataInputPlus in, int version, List<AbstractType<?>>
types) throws IOException
+ public void skip(DataInputPlus in, int version, AbstractType<?>[]
types) throws IOException
{
- if (!types.isEmpty())
- ClusteringPrefix.serializer.skipValuesWithoutSize(in,
types.size(), version, types);
+ if (types.length > 0)
+ ClusteringPrefix.serializer.skipValuesWithoutSize(in,
types.length, version, types);
}
- public Clustering<byte[]> deserialize(DataInputPlus in, int version,
List<AbstractType<?>> types) throws IOException
+ public Clustering<byte[]> deserialize(DataInputPlus in, int version,
AbstractType<?>[] types) throws IOException
{
- if (types.isEmpty())
+ if (types.length == 0)
return ByteArrayAccessor.factory.clustering();
- byte[][] values =
ClusteringPrefix.serializer.deserializeValuesWithoutSize(in, types.size(),
version, types);
+ byte[][] values =
ClusteringPrefix.serializer.deserializeValuesWithoutSize(in, types.length,
version, types);
return ByteArrayAccessor.factory.clustering(values);
}
- public Clustering<byte[]> deserialize(ByteBuffer in, int version,
List<AbstractType<?>> types)
+ public Clustering<byte[]> deserialize(ByteBuffer in, int version,
AbstractType<?>[] types)
{
try (DataInputBuffer buffer = new DataInputBuffer(in, true))
{
@@ -197,4 +196,4 @@ public interface Clustering<V> extends ClusteringPrefix<V>,
IMeasurableMemory
*/
Clustering<?> ensureAccessorFactorySupport();
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/db/ClusteringBoundOrBoundary.java
b/src/java/org/apache/cassandra/db/ClusteringBoundOrBoundary.java
index 1a13ba2567..969908e15b 100644
--- a/src/java/org/apache/cassandra/db/ClusteringBoundOrBoundary.java
+++ b/src/java/org/apache/cassandra/db/ClusteringBoundOrBoundary.java
@@ -22,7 +22,6 @@ package org.apache.cassandra.db;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.List;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.ByteArrayAccessor;
@@ -100,27 +99,27 @@ public interface ClusteringBoundOrBoundary<V> extends
ClusteringPrefix<V>
public static class Serializer
{
- public <T> void serialize(ClusteringBoundOrBoundary<T> bound,
DataOutputPlus out, int version, List<AbstractType<?>> types) throws IOException
+ public <T> void serialize(ClusteringBoundOrBoundary<T> bound,
DataOutputPlus out, int version, AbstractType<?>[] types) throws IOException
{
out.writeByte(bound.kind().ordinal());
out.writeShort(bound.size());
ClusteringPrefix.serializer.serializeValuesWithoutSize(bound, out,
version, types);
}
- public <T> long serializedSize(ClusteringBoundOrBoundary<T> bound, int
version, List<AbstractType<?>> types)
+ public <T> long serializedSize(ClusteringBoundOrBoundary<T> bound, int
version, AbstractType<?>[] types)
{
return 1 // kind ordinal
+ TypeSizes.sizeof((short)bound.size())
+
ClusteringPrefix.serializer.valuesWithoutSizeSerializedSize(bound, version,
types);
}
- public ClusteringBoundOrBoundary<byte[]> deserialize(DataInputPlus in,
int version, List<AbstractType<?>> types) throws IOException
+ public ClusteringBoundOrBoundary<byte[]> deserialize(DataInputPlus in,
int version, AbstractType<?>[] types) throws IOException
{
Kind kind = Kind.fromOrdinal(in.readByte());
return deserializeValues(in, kind, version, types);
}
- public void skipValues(DataInputPlus in, Kind kind, int version,
List<AbstractType<?>> types) throws IOException
+ public void skipValues(DataInputPlus in, Kind kind, int version,
AbstractType<?>[] types) throws IOException
{
int size = in.readUnsignedShort();
if (size == 0)
@@ -129,7 +128,7 @@ public interface ClusteringBoundOrBoundary<V> extends
ClusteringPrefix<V>
ClusteringPrefix.serializer.skipValuesWithoutSize(in, size,
version, types);
}
- public ClusteringBoundOrBoundary<byte[]>
deserializeValues(DataInputPlus in, Kind kind, int version,
List<AbstractType<?>> types) throws IOException
+ public ClusteringBoundOrBoundary<byte[]>
deserializeValues(DataInputPlus in, Kind kind, int version, AbstractType<?>[]
types) throws IOException
{
int size = in.readUnsignedShort();
if (size == 0)
diff --git a/src/java/org/apache/cassandra/db/ClusteringComparator.java
b/src/java/org/apache/cassandra/db/ClusteringComparator.java
index 88ba6e4fc5..746376b0ee 100644
--- a/src/java/org/apache/cassandra/db/ClusteringComparator.java
+++ b/src/java/org/apache/cassandra/db/ClusteringComparator.java
@@ -20,11 +20,8 @@ package org.apache.cassandra.db;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
@@ -38,6 +35,7 @@ import
org.apache.cassandra.utils.bytecomparable.ByteComparable;
import org.apache.cassandra.utils.bytecomparable.ByteSource;
import org.apache.cassandra.utils.vint.VIntCoding;
+import static
org.apache.cassandra.db.marshal.AbstractType.EMPTY_ABSTRACT_TYPE_ARRAY;
import static org.apache.cassandra.utils.bytecomparable.ByteSource.EXCLUDED;
import static
org.apache.cassandra.utils.bytecomparable.ByteSource.NEXT_COMPONENT;
import static
org.apache.cassandra.utils.bytecomparable.ByteSource.NEXT_COMPONENT_EMPTY;
@@ -54,7 +52,7 @@ import static
org.apache.cassandra.utils.bytecomparable.ByteSource.TERMINATOR;
*/
public class ClusteringComparator implements Comparator<Clusterable>
{
- private final List<AbstractType<?>> clusteringTypes;
+ private final AbstractType<?>[] clusteringTypes;
private final Comparator<IndexInfo> indexComparator;
private final Comparator<IndexInfo> indexReverseComparator;
@@ -65,20 +63,15 @@ public class ClusteringComparator implements
Comparator<Clusterable>
public ClusteringComparator(AbstractType<?>... clusteringTypes)
{
- this(ImmutableList.copyOf(clusteringTypes));
- }
-
- public ClusteringComparator(Iterable<AbstractType<?>> clusteringTypes)
- {
- // copy the list to ensure despatch is monomorphic
- this.clusteringTypes = ImmutableList.copyOf(clusteringTypes);
+ // No defensive copy: callers must not retain/mutate the array after
construction
+ this.clusteringTypes = clusteringTypes.length == 0 ?
EMPTY_ABSTRACT_TYPE_ARRAY : clusteringTypes;
this.indexComparator = (o1, o2) ->
ClusteringComparator.this.compare((ClusteringPrefix<?>) o1.lastName,
(ClusteringPrefix<?>) o2.lastName);
this.indexReverseComparator = (o1, o2) ->
ClusteringComparator.this.compare((ClusteringPrefix<?>) o1.firstName,
(ClusteringPrefix<?>) o2.firstName);
this.reverseComparator = (c1, c2) ->
ClusteringComparator.this.compare(c2, c1);
- for (AbstractType<?> type : clusteringTypes)
+ for (AbstractType<?> type : this.clusteringTypes)
type.checkComparable(); // this should already be enforced by
TableMetadata.Builder.addColumn, but we check again for other constructors
}
@@ -87,14 +80,17 @@ public class ClusteringComparator implements
Comparator<Clusterable>
*/
public int size()
{
- return clusteringTypes.size();
+ return clusteringTypes.length;
}
/**
* The "subtypes" of this clustering comparator, that is the types of the
clustering
* columns for the table this is a comparator of.
+ *
+ * @return the internal array directly (no copy), shared with every
consumer of this comparator
+ * as it is read on the serialization hot path; it must not be mutated.
*/
- public List<AbstractType<?>> subtypes()
+ public AbstractType<?>[] subtypes()
{
return clusteringTypes;
}
@@ -104,7 +100,7 @@ public class ClusteringComparator implements
Comparator<Clusterable>
*/
public AbstractType<?> subtype(int i)
{
- return clusteringTypes.get(i);
+ return clusteringTypes[i];
}
/**
@@ -303,7 +299,7 @@ public class ClusteringComparator implements
Comparator<Clusterable>
if (v2 == null)
return 1;
- return clusteringTypes.get(i).compare(v1, accessor1, v2, accessor2);
+ return clusteringTypes[i].compare(v1, accessor1, v2, accessor2);
}
public <V1, V2> int compareComponent(int i, ClusteringPrefix<V1> v1,
ClusteringPrefix<V2> v2)
@@ -664,12 +660,12 @@ public class ClusteringComparator implements
Comparator<Clusterable>
return false;
ClusteringComparator that = (ClusteringComparator)o;
- return this.clusteringTypes.equals(that.clusteringTypes);
+ return Arrays.equals(this.clusteringTypes, that.clusteringTypes);
}
@Override
public int hashCode()
{
- return Objects.hashCode(clusteringTypes);
+ return Arrays.hashCode(clusteringTypes);
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/db/ClusteringPrefix.java
b/src/java/org/apache/cassandra/db/ClusteringPrefix.java
index ff2b9261a2..913510521d 100644
--- a/src/java/org/apache/cassandra/db/ClusteringPrefix.java
+++ b/src/java/org/apache/cassandra/db/ClusteringPrefix.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.db;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.List;
import java.util.Objects;
import java.util.function.ToIntFunction;
@@ -431,7 +430,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
* Produce a human-readable representation of the clustering given the
list of types.
* Easier to access than metadata for debugging.
*/
- public default String clusteringString(List<AbstractType<?>> types)
+ public default String clusteringString(AbstractType<?>[] types)
{
StringBuilder sb = new StringBuilder();
sb.append(kind()).append('(');
@@ -439,7 +438,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
{
if (i > 0)
sb.append(", ");
- sb.append(types.get(i).getString(get(i), accessor()));
+ sb.append(types[i].getString(get(i), accessor()));
}
return sb.append(')').toString();
}
@@ -466,7 +465,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
public static class Serializer
{
- public void serialize(ClusteringPrefix<?> clustering, DataOutputPlus
out, int version, List<AbstractType<?>> types) throws IOException
+ public void serialize(ClusteringPrefix<?> clustering, DataOutputPlus
out, int version, AbstractType<?>[] types) throws IOException
{
// We shouldn't serialize static clusterings
assert clustering.kind() != Kind.STATIC_CLUSTERING;
@@ -481,7 +480,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
}
}
- public void skip(DataInputPlus in, int version, List<AbstractType<?>>
types) throws IOException
+ public void skip(DataInputPlus in, int version, AbstractType<?>[]
types) throws IOException
{
Kind kind = Kind.fromOrdinal(in.readByte());
// We shouldn't serialize static clusterings
@@ -492,7 +491,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
ClusteringBoundOrBoundary.serializer.skipValues(in, kind,
version, types);
}
- public ClusteringPrefix<byte[]> deserialize(DataInputPlus in, int
version, List<AbstractType<?>> types) throws IOException
+ public ClusteringPrefix<byte[]> deserialize(DataInputPlus in, int
version, AbstractType<?>[] types) throws IOException
{
Kind kind = Kind.fromOrdinal(in.readByte());
// We shouldn't serialize static clusterings
@@ -503,7 +502,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
return
ClusteringBoundOrBoundary.serializer.deserializeValues(in, kind, version,
types);
}
- public long serializedSize(ClusteringPrefix<?> clustering, int
version, List<AbstractType<?>> types)
+ public long serializedSize(ClusteringPrefix<?> clustering, int
version, AbstractType<?>[] types)
{
// We shouldn't serialize static clusterings
assert clustering.kind() != Kind.STATIC_CLUSTERING;
@@ -513,7 +512,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
return
ClusteringBoundOrBoundary.serializer.serializedSize((ClusteringBoundOrBoundary<?>)clustering,
version, types);
}
- <V> void serializeValuesWithoutSize(ClusteringPrefix<V> clustering,
DataOutputPlus out, int version, List<AbstractType<?>> types) throws IOException
+ <V> void serializeValuesWithoutSize(ClusteringPrefix<V> clustering,
DataOutputPlus out, int version, AbstractType<?>[] types) throws IOException
{
int offset = 0;
int clusteringSize = clustering.size();
@@ -528,13 +527,13 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
out.writeUnsignedVInt(makeHeader(clustering, offset, limit));
while (offset < limit)
{
-
clustering.writeValueSkippingNullAndEmpty(types.get(offset), offset, out);
+ clustering.writeValueSkippingNullAndEmpty(types[offset],
offset, out);
offset++;
}
}
}
- <V> long valuesWithoutSizeSerializedSize(ClusteringPrefix<V>
clustering, int version, List<AbstractType<?>> types)
+ <V> long valuesWithoutSizeSerializedSize(ClusteringPrefix<V>
clustering, int version, AbstractType<?>[] types)
{
long result = 0;
int offset = 0;
@@ -547,12 +546,12 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
}
for (int i = 0; i < clusteringSize; i++)
{
- result +=
clustering.writtenLengthSkippingNullAndEmpty(types.get(i), i);
+ result +=
clustering.writtenLengthSkippingNullAndEmpty(types[i], i);
}
return result;
}
- public byte[][] deserializeValuesWithoutSize(DataInputPlus in, int
size, int version, List<AbstractType<?>> types) throws IOException
+ public byte[][] deserializeValuesWithoutSize(DataInputPlus in, int
size, int version, AbstractType<?>[] types) throws IOException
{
// Callers of this method should handle the case where size = 0
(in all case we want to return a special value anyway).
assert size > 0;
@@ -567,14 +566,14 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
values[offset] = isNull(header, offset)
? null
: (isEmpty(header, offset) ?
ByteArrayUtil.EMPTY_BYTE_ARRAY
- :
types.get(offset).readArray(in, DatabaseDescriptor.getMaxValueSize()));
+ :
types[offset].readArray(in, DatabaseDescriptor.getMaxValueSize()));
offset++;
}
}
return values;
}
- void skipValuesWithoutSize(DataInputPlus in, int size, int version,
List<AbstractType<?>> types) throws IOException
+ void skipValuesWithoutSize(DataInputPlus in, int size, int version,
AbstractType<?>[] types) throws IOException
{
// Callers of this method should handle the case where size = 0
(in all case we want to return a special value anyway).
assert size > 0;
@@ -586,7 +585,7 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
while (offset < limit)
{
if (!isNull(header, offset) && !isEmpty(header, offset))
- types.get(offset).skipValue(in);
+ types[offset].skipValue(in);
offset++;
}
}
@@ -704,13 +703,14 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
if (i >= nextSize)
return false;
+ AbstractType<?>[] clusteringTypes =
serializationHeader.clusteringTypes();
while (deserializedSize <= i)
- deserializeOne();
+ deserializeOne(clusteringTypes);
return true;
}
- private boolean deserializeOne() throws IOException
+ private boolean deserializeOne(AbstractType<?>[] clusteringTypes)
throws IOException
{
if (deserializedSize == nextSize)
return false;
@@ -722,13 +722,14 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
nextValues[i] = Serializer.isNull(nextHeader, i)
? null
: (Serializer.isEmpty(nextHeader, i) ?
ByteArrayUtil.EMPTY_BYTE_ARRAY
- :
serializationHeader.clusteringTypes().get(i).readArray(in,
DatabaseDescriptor.getMaxValueSize()));
+ :
clusteringTypes[i].readArray(in, DatabaseDescriptor.getMaxValueSize()));
return true;
}
private void deserializeAll() throws IOException
{
- while (deserializeOne())
+ AbstractType<?>[] clusteringTypes =
serializationHeader.clusteringTypes();
+ while (deserializeOne(clusteringTypes))
continue;
}
@@ -752,12 +753,13 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
public ClusteringPrefix.Kind skipNext() throws IOException
{
+ AbstractType<?>[] clusteringTypes =
serializationHeader.clusteringTypes();
for (int i = deserializedSize; i < nextSize; i++)
{
if ((i % 32) == 0)
nextHeader = in.readUnsignedVInt();
if (!Serializer.isNull(nextHeader, i) &&
!Serializer.isEmpty(nextHeader, i))
- serializationHeader.clusteringTypes().get(i).skipValue(in);
+ clusteringTypes[i].skipValue(in);
}
deserializedSize = nextSize;
return nextKind;
@@ -808,4 +810,4 @@ public interface ClusteringPrefix<V> extends
IMeasurableMemory, Clusterable<V>
comparator.subtype(clusterIndex).checkConstraints(accessor().toBuffer(get(clusterIndex)),
constraints);
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/db/SerializationHeader.java
b/src/java/org/apache/cassandra/db/SerializationHeader.java
index dd4ea5a495..4e10d5f968 100644
--- a/src/java/org/apache/cassandra/db/SerializationHeader.java
+++ b/src/java/org/apache/cassandra/db/SerializationHeader.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.db;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -56,7 +57,7 @@ public class SerializationHeader
private final boolean isForSSTable;
private final AbstractType<?> keyType;
- private final List<AbstractType<?>> clusteringTypes;
+ private final AbstractType<?>[] clusteringTypes;
private final RegularAndStaticColumns columns;
private final EncodingStats stats;
@@ -67,7 +68,7 @@ public class SerializationHeader
private SerializationHeader(boolean isForSSTable,
AbstractType<?> keyType,
- List<AbstractType<?>> clusteringTypes,
+ AbstractType<?>[] clusteringTypes,
RegularAndStaticColumns columns,
EncodingStats stats,
Map<ByteBuffer, AbstractType<?>> typeMap)
@@ -77,7 +78,7 @@ public class SerializationHeader
private SerializationHeader(boolean isForSSTable,
AbstractType<?> keyType,
- List<AbstractType<?>> clusteringTypes,
+ AbstractType<?>[] clusteringTypes,
RegularAndStaticColumns columns,
EncodingStats stats,
Map<ByteBuffer, AbstractType<?>> typeMap,
@@ -191,7 +192,8 @@ public class SerializationHeader
return keyType;
}
- public List<AbstractType<?>> clusteringTypes()
+ /** Shared with {@link ClusteringComparator#subtypes()}, or owned when
deserialized; must not be mutated. */
+ public AbstractType<?>[] clusteringTypes()
{
return clusteringTypes;
}
@@ -313,7 +315,7 @@ public class SerializationHeader
@Override
public String toString()
{
- return String.format("SerializationHeader[key=%s, cks=%s, columns=%s,
stats=%s, typeMap=%s]", keyType, clusteringTypes, columns, stats, typeMap);
+ return String.format("SerializationHeader[key=%s, cks=%s, columns=%s,
stats=%s, typeMap=%s]", keyType, Arrays.toString(clusteringTypes), columns,
stats, typeMap);
}
/**
@@ -323,13 +325,13 @@ public class SerializationHeader
public static class Component extends MetadataComponent
{
private final AbstractType<?> keyType;
- private final List<AbstractType<?>> clusteringTypes;
+ private final AbstractType<?>[] clusteringTypes;
private final Map<ByteBuffer, AbstractType<?>> staticColumns;
private final Map<ByteBuffer, AbstractType<?>> regularColumns;
private final EncodingStats stats;
private Component(AbstractType<?> keyType,
- List<AbstractType<?>> clusteringTypes,
+ AbstractType<?>[] clusteringTypes,
Map<ByteBuffer, AbstractType<?>> staticColumns,
Map<ByteBuffer, AbstractType<?>> regularColumns,
EncodingStats stats)
@@ -397,7 +399,7 @@ public class SerializationHeader
Component that = (Component)o;
return Objects.equals(this.keyType, that.keyType)
- && Objects.equals(this.clusteringTypes, that.clusteringTypes)
+ && Arrays.equals(this.clusteringTypes, that.clusteringTypes)
&& Objects.equals(this.staticColumns, that.staticColumns)
&& Objects.equals(this.regularColumns, that.regularColumns)
&& Objects.equals(this.stats, that.stats);
@@ -406,14 +408,14 @@ public class SerializationHeader
@Override
public int hashCode()
{
- return Objects.hash(keyType, clusteringTypes, staticColumns,
regularColumns, stats);
+ return 31 * Objects.hash(keyType, staticColumns, regularColumns,
stats) + Arrays.hashCode(clusteringTypes);
}
@Override
public String toString()
{
return String.format("SerializationHeader.Component[key=%s,
cks=%s, statics=%s, regulars=%s, stats=%s]",
- keyType, clusteringTypes, staticColumns,
regularColumns, stats);
+ keyType, Arrays.toString(clusteringTypes),
staticColumns, regularColumns, stats);
}
public AbstractType<?> getKeyType()
@@ -421,7 +423,8 @@ public class SerializationHeader
return keyType;
}
- public List<AbstractType<?>> getClusteringTypes()
+ /** Shared, as in {@link SerializationHeader#clusteringTypes()}; must
not be mutated. */
+ public AbstractType<?>[] getClusteringTypes()
{
return clusteringTypes;
}
@@ -560,7 +563,7 @@ public class SerializationHeader
EncodingStats stats = EncodingStats.serializer.deserialize(in);
AbstractType<?> keyType = metadata.partitionKeyType;
- List<AbstractType<?>> clusteringTypes =
metadata.comparator.subtypes();
+ AbstractType<?>[] clusteringTypes = metadata.comparator.subtypes();
Columns statics, regulars;
if (selection == null)
@@ -602,7 +605,7 @@ public class SerializationHeader
EncodingStats.serializer.serialize(header.stats, out);
typeSerializer.serialize(header.keyType, out);
- typeSerializer.serializeList(header.clusteringTypes, out);
+ typeSerializer.serializeArray(header.clusteringTypes, out);
writeColumnsWithTypes(header.staticColumns, out);
writeColumnsWithTypes(header.regularColumns, out);
@@ -614,7 +617,7 @@ public class SerializationHeader
EncodingStats stats = EncodingStats.serializer.deserialize(in);
AbstractType<?> keyType = typeSerializer.deserialize(in);
- List<AbstractType<?>> clusteringTypes =
typeSerializer.deserializeList(in);
+ AbstractType<?>[] clusteringTypes =
typeSerializer.deserializeArray(in);
Map<ByteBuffer, AbstractType<?>> staticColumns =
readColumnsWithType(in);
Map<ByteBuffer, AbstractType<?>> regularColumns =
readColumnsWithType(in);
@@ -628,7 +631,7 @@ public class SerializationHeader
int size = EncodingStats.serializer.serializedSize(header.stats);
size += typeSerializer.serializedSize(header.keyType);
- size += typeSerializer.serializedListSize(header.clusteringTypes);
+ size += typeSerializer.serializedArraySize(header.clusteringTypes);
size += sizeofColumnsWithTypes(header.staticColumns);
size += sizeofColumnsWithTypes(header.regularColumns);
@@ -668,4 +671,4 @@ public class SerializationHeader
return typeMap;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/db/Slice.java
b/src/java/org/apache/cassandra/db/Slice.java
index 1fc60ba1f7..8838dac820 100644
--- a/src/java/org/apache/cassandra/db/Slice.java
+++ b/src/java/org/apache/cassandra/db/Slice.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.db;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.List;
import java.util.Objects;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -292,23 +291,23 @@ public class Slice
public static class Serializer
{
- public void serialize(Slice slice, DataOutputPlus out, int version,
List<AbstractType<?>> types) throws IOException
+ public void serialize(Slice slice, DataOutputPlus out, int version,
AbstractType<?>[] types) throws IOException
{
ClusteringBound.serializer.serialize(slice.start, out, version,
types);
ClusteringBound.serializer.serialize(slice.end, out, version,
types);
}
- public long serializedSize(Slice slice, int version,
List<AbstractType<?>> types)
+ public long serializedSize(Slice slice, int version, AbstractType<?>[]
types)
{
return ClusteringBound.serializer.serializedSize(slice.start,
version, types)
+ ClusteringBound.serializer.serializedSize(slice.end,
version, types);
}
- public Slice deserialize(DataInputPlus in, int version,
List<AbstractType<?>> types) throws IOException
+ public Slice deserialize(DataInputPlus in, int version,
AbstractType<?>[] types) throws IOException
{
ClusteringBound<byte[]> start = (ClusteringBound<byte[]>)
ClusteringBound.serializer.deserialize(in, version, types);
ClusteringBound<byte[]> end = (ClusteringBound<byte[]>)
ClusteringBound.serializer.deserialize(in, version, types);
return new Slice(start, end);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/db/Slices.java
b/src/java/org/apache/cassandra/db/Slices.java
index 2f2f6395dc..38811cda02 100644
--- a/src/java/org/apache/cassandra/db/Slices.java
+++ b/src/java/org/apache/cassandra/db/Slices.java
@@ -40,6 +40,8 @@ import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
+import static
org.apache.cassandra.db.marshal.AbstractType.EMPTY_ABSTRACT_TYPE_ARRAY;
+
/**
* Represents the selection of multiple range of rows within a partition.
* <p>
@@ -304,9 +306,9 @@ public abstract class Slices implements Iterable<Slice>
if (size == 0)
return;
- List<AbstractType<?>> types = slices == ALL
- ? Collections.emptyList()
- :
((ArrayBackedSlices)slices).comparator.subtypes();
+ AbstractType<?>[] types = slices == ALL
+ ? EMPTY_ABSTRACT_TYPE_ARRAY
+ : ((ArrayBackedSlices)
slices).comparator.subtypes();
for (Slice slice : slices)
Slice.serializer.serialize(slice, out, version, types);
@@ -319,9 +321,9 @@ public abstract class Slices implements Iterable<Slice>
if (slices.size() == 0)
return size;
- List<AbstractType<?>> types = slices instanceof SelectAllSlices
- ? Collections.emptyList()
- :
((ArrayBackedSlices)slices).comparator.subtypes();
+ AbstractType<?>[] types = slices instanceof SelectAllSlices
+ ? EMPTY_ABSTRACT_TYPE_ARRAY
+ : ((ArrayBackedSlices)
slices).comparator.subtypes();
for (Slice slice : slices)
size += Slice.serializer.serializedSize(slice, version, types);
@@ -864,4 +866,4 @@ public abstract class Slices implements Iterable<Slice>
return "";
}
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/db/compaction/CursorCompactor.java
b/src/java/org/apache/cassandra/db/compaction/CursorCompactor.java
index d49931d6b3..9d0deece00 100644
--- a/src/java/org/apache/cassandra/db/compaction/CursorCompactor.java
+++ b/src/java/org/apache/cassandra/db/compaction/CursorCompactor.java
@@ -524,7 +524,7 @@ public class CursorCompactor extends CompactionInfo.Holder
purger = new Purger(type, controller);
lastWrittenPartition = new
PartitionDescriptor(metadata.partitioner.createReusableKey(0));
- lastWrittenUnfiltered = new
UnfilteredDescriptor(metadata.comparator.subtypes().toArray(AbstractType[]::new));
+ lastWrittenUnfiltered = new
UnfilteredDescriptor(metadata.comparator.subtypes());
// A steal moves a descriptor between cursors, and to the write-side
instance built from
// the table comparator above. Each parses a clustering with its own
clusteringTypes, so
// all of them must parse identically.
diff --git a/src/java/org/apache/cassandra/db/compaction/StatefulCursor.java
b/src/java/org/apache/cassandra/db/compaction/StatefulCursor.java
index aeea5da967..83042ffe0a 100644
--- a/src/java/org/apache/cassandra/db/compaction/StatefulCursor.java
+++ b/src/java/org/apache/cassandra/db/compaction/StatefulCursor.java
@@ -28,7 +28,6 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.ReusableLivenessInfo;
import org.apache.cassandra.db.UnfilteredValidation;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.rows.ReusableCellLivenessInfo;
import org.apache.cassandra.io.sstable.PartitionDescriptor;
import org.apache.cassandra.io.sstable.SSTableCursorReader;
@@ -82,7 +81,7 @@ class StatefulCursor extends SSTableCursorReader
// true on SSTableCursorReader for exactly this reason; no override
needed here.
currPartition = new
PartitionDescriptor(reader.getPartitioner().createReusableKey(0));
prevPartition = new
PartitionDescriptor(reader.getPartitioner().createReusableKey(0));
- unfiltered = new
UnfilteredDescriptor(reader.header.clusteringTypes().toArray(AbstractType[]::new));
+ unfiltered = new UnfilteredDescriptor(reader.header.clusteringTypes());
}
public int readPartitionHeader()
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 257a64f931..1784b10eb2 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -67,6 +67,8 @@ public abstract class AbstractType<T> implements
Comparator<ByteBuffer>, Assignm
{
private final static int VARIABLE_LENGTH = -1;
+ public static final AbstractType<?>[] EMPTY_ABSTRACT_TYPE_ARRAY = new
AbstractType<?>[0];
+
public final Comparator<ByteBuffer> reverseComparator;
public enum ComparisonType
diff --git a/src/java/org/apache/cassandra/io/sstable/ClusteringDescriptor.java
b/src/java/org/apache/cassandra/io/sstable/ClusteringDescriptor.java
index 07f96367f6..aa5b1dfd73 100644
--- a/src/java/org/apache/cassandra/io/sstable/ClusteringDescriptor.java
+++ b/src/java/org/apache/cassandra/io/sstable/ClusteringDescriptor.java
@@ -20,7 +20,6 @@ package org.apache.cassandra.io.sstable;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.List;
import org.apache.cassandra.db.Clustering;
import org.apache.cassandra.db.ClusteringBound;
@@ -163,7 +162,7 @@ public class ClusteringDescriptor extends
ResizableByteBuffer
return (clusteringKindEncoded ==
EXCL_END_INCL_START_BOUNDARY_CLUSTERING_KIND || clusteringKindEncoded ==
INCL_END_EXCL_START_BOUNDARY_CLUSTERING_KIND);
}
- public final ClusteringPrefix<?> toClusteringPrefix(List<AbstractType<?>>
clusteringTypesList) {
+ public final ClusteringPrefix<?> toClusteringPrefix(AbstractType<?>[]
clusteringTypesList) {
if (clusteringKindEncoded == ROW_CLUSTERING_KIND) {
return Clustering.serializer.deserialize(clusteringBuffer(), 0,
clusteringTypesList);
}
diff --git a/src/java/org/apache/cassandra/io/sstable/IndexInfo.java
b/src/java/org/apache/cassandra/io/sstable/IndexInfo.java
index 607d876c0f..2be23943cc 100644
--- a/src/java/org/apache/cassandra/io/sstable/IndexInfo.java
+++ b/src/java/org/apache/cassandra/io/sstable/IndexInfo.java
@@ -19,7 +19,6 @@
package org.apache.cassandra.io.sstable;
import java.io.IOException;
-import java.util.List;
import org.apache.cassandra.db.ClusteringPrefix;
import org.apache.cassandra.db.DeletionTime;
@@ -108,9 +107,9 @@ public class IndexInfo
public static final long WIDTH_BASE = 64 * 1024;
private final Version version;
- private final List<AbstractType<?>> clusteringTypes;
+ private final AbstractType<?>[] clusteringTypes;
- public Serializer(Version version, List<AbstractType<?>>
clusteringTypes)
+ public Serializer(Version version, AbstractType<?>[] clusteringTypes)
{
this.version = version;
this.clusteringTypes = clusteringTypes;
diff --git a/src/java/org/apache/cassandra/io/sstable/SSTableCursorWriter.java
b/src/java/org/apache/cassandra/io/sstable/SSTableCursorWriter.java
index 49c61e2238..8dcafc4e58 100644
--- a/src/java/org/apache/cassandra/io/sstable/SSTableCursorWriter.java
+++ b/src/java/org/apache/cassandra/io/sstable/SSTableCursorWriter.java
@@ -68,7 +68,6 @@ public class SSTableCursorWriter implements AutoCloseable
private static final ColumnMetadata[] EMPTY_COL_META = new
ColumnMetadata[0];
private final SortedTableWriter<?,?> ssTableWriter;
private final SequentialWriter dataWriter;
- private final SortedTableWriter.AbstractIndexWriter indexWriter;
private final DeletionTime.Serializer deletionTimeSerializer;
private final MetadataCollector metadataCollector;
private final SerializationHeader serializationHeader;
@@ -160,7 +159,6 @@ public class SSTableCursorWriter implements AutoCloseable
{
this.ssTableWriter = ssTableWriter;
this.dataWriter = dataWriter;
- this.indexWriter = indexWriter;
this.deletionTimeSerializer = DeletionTime.getSerializer(desc.version);
this.metadataCollector = metadataCollector;
this.serializationHeader = serializationHeader;
diff --git
a/src/java/org/apache/cassandra/io/sstable/metadata/MetadataCollector.java
b/src/java/org/apache/cassandra/io/sstable/metadata/MetadataCollector.java
index f74412cee4..fc6bf1ee30 100644
--- a/src/java/org/apache/cassandra/io/sstable/metadata/MetadataCollector.java
+++ b/src/java/org/apache/cassandra/io/sstable/metadata/MetadataCollector.java
@@ -18,7 +18,6 @@
package org.apache.cassandra.io.sstable.metadata;
import java.nio.ByteBuffer;
-import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import java.util.UUID;
@@ -59,6 +58,8 @@ import org.apache.cassandra.utils.TimeUUID;
import
org.apache.cassandra.utils.streamhist.StreamingTombstoneHistogramBuilder;
import org.apache.cassandra.utils.streamhist.TombstoneHistogram;
+import static
org.apache.cassandra.db.marshal.AbstractType.EMPTY_ABSTRACT_TYPE_ARRAY;
+
public class MetadataCollector implements PartitionStatisticsCollector
{
public static final double NO_COMPRESSION_RATIO = -1.0;
@@ -97,7 +98,7 @@ public class MetadataCollector implements
PartitionStatisticsCollector
NO_COMPRESSION_RATIO,
defaultTombstoneDropTimeHistogram(),
0,
- Collections.emptyList(),
+ EMPTY_ABSTRACT_TYPE_ARRAY,
Slice.ALL,
true,
ActiveRepairService.UNREPAIRED_SSTABLE,
@@ -170,7 +171,7 @@ public class MetadataCollector implements
PartitionStatisticsCollector
{
this.comparator = comparator;
this.originatingHostId = originatingHostId;
- AbstractType<?>[] clusteringTypes =
comparator.subtypes().toArray(AbstractType[]::new);
+ AbstractType<?>[] clusteringTypes = comparator.subtypes();
this.minClusteringDescriptor = new
ClusteringDescriptor(clusteringTypes).resetMaxStart();
this.maxClusteringDescriptor = new
ClusteringDescriptor(clusteringTypes).resetMinEnd();
}
@@ -604,4 +605,4 @@ public class MetadataCollector implements
PartitionStatisticsCollector
return isSet ? max : defaultMax;
}
}
-}
\ No newline at end of file
+}
diff --git
a/src/java/org/apache/cassandra/io/sstable/metadata/StatsMetadata.java
b/src/java/org/apache/cassandra/io/sstable/metadata/StatsMetadata.java
index 3ec1a208c5..5f1c6cac6e 100644
--- a/src/java/org/apache/cassandra/io/sstable/metadata/StatsMetadata.java
+++ b/src/java/org/apache/cassandra/io/sstable/metadata/StatsMetadata.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.io.sstable.metadata;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.ArrayUtils;
@@ -83,7 +82,7 @@ public class StatsMetadata extends MetadataComponent
public final EncodingStats encodingStats;
// Used to serialize min/max clustering. Can be null if the metadata was
deserialized from a legacy version
- private final List<AbstractType<?>> clusteringTypes;
+ private final AbstractType<?>[] clusteringTypes;
/**
* This boolean is used as an approximation of whether a given key can be
guaranteed not to have partition
@@ -111,7 +110,7 @@ public class StatsMetadata extends MetadataComponent
double compressionRatio,
TombstoneHistogram estimatedTombstoneDropTime,
int sstableLevel,
- List<AbstractType<?>> clusteringTypes,
+ AbstractType<?>[] clusteringTypes,
Slice coveredClustering,
boolean hasLegacyCounterShards,
long repairedAt,
@@ -391,7 +390,7 @@ public class StatsMetadata extends MetadataComponent
private int improvedMinMaxSize(Version version, StatsMetadata
component, int size)
{
- size +=
typeSerializer.serializedListSize(component.clusteringTypes);
+ size +=
typeSerializer.serializedArraySize(component.clusteringTypes);
size +=
Slice.serializer.serializedSize(component.coveredClustering,
version.correspondingMessagingVersion(),
component.clusteringTypes);
@@ -514,7 +513,7 @@ public class StatsMetadata extends MetadataComponent
private void serializeImprovedMinMax(Version version, StatsMetadata
component, DataOutputPlus out) throws IOException
{
assert component.clusteringTypes != null;
- typeSerializer.serializeList(component.clusteringTypes, out);
+ typeSerializer.serializeArray(component.clusteringTypes, out);
Slice.serializer.serialize(component.coveredClustering,
out,
version.correspondingMessagingVersion(),
@@ -573,7 +572,7 @@ public class StatsMetadata extends MetadataComponent
int sstableLevel = in.readInt();
long repairedAt = in.readLong();
- List<AbstractType<?>> clusteringTypes = null;
+ AbstractType<?>[] clusteringTypes = null;
Slice coveredClustering = Slice.ALL;
if (version.hasLegacyMinMax())
{
@@ -596,7 +595,7 @@ public class StatsMetadata extends MetadataComponent
else if (version.hasImprovedMinMax())
{
// improvedMinMax will be in this place when legacyMinMax is
removed
- clusteringTypes = typeSerializer.deserializeList(in);
+ clusteringTypes = typeSerializer.deserializeArray(in);
coveredClustering = Slice.serializer.deserialize(in,
version.correspondingMessagingVersion(), clusteringTypes);
}
@@ -636,7 +635,7 @@ public class StatsMetadata extends MetadataComponent
if (version.hasImprovedMinMax() && version.hasLegacyMinMax())
{
// improvedMinMax will be in this place until legacyMinMax is
removed
- clusteringTypes = typeSerializer.deserializeList(in);
+ clusteringTypes = typeSerializer.deserializeArray(in);
coveredClustering = Slice.serializer.deserialize(in,
version.correspondingMessagingVersion(), clusteringTypes);
}
diff --git a/src/java/org/apache/cassandra/schema/TableMetadata.java
b/src/java/org/apache/cassandra/schema/TableMetadata.java
index 4f9fe1320a..5dae17eda9 100644
--- a/src/java/org/apache/cassandra/schema/TableMetadata.java
+++ b/src/java/org/apache/cassandra/schema/TableMetadata.java
@@ -91,7 +91,6 @@ import static accord.utils.Invariants.require;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.transform;
import static java.lang.String.format;
-import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static
org.apache.cassandra.db.Directories.SECONDARY_INDEX_NAME_SEPARATOR;
import static
org.apache.cassandra.db.Directories.TABLE_DIRECTORY_NAME_SEPARATOR;
@@ -255,8 +254,13 @@ public class TableMetadata implements SchemaElement
columnsById[column.uniqueId] = column;
for (ColumnMetadata column : partitionKeyColumns)
columnsById[column.uniqueId] = column;
- for (ColumnMetadata column : clusteringColumns)
+ AbstractType<?>[] clusteringTypes = new
AbstractType<?>[clusteringColumns.size()];
+ for (int i = 0; i < clusteringColumns.size(); i++)
+ {
+ ColumnMetadata column = clusteringColumns.get(i);
columnsById[column.uniqueId] = column;
+ clusteringTypes[i] = column.type;
+ }
columns = ImmutableMap.copyOf(builder.columns);
indexes = builder.indexes;
@@ -266,7 +270,7 @@ public class TableMetadata implements SchemaElement
? partitionKeyColumns.get(0).type
:
CompositeType.getInstance(transform(partitionKeyColumns, t -> t.type));
- comparator = new ClusteringComparator(transform(clusteringColumns, c
-> c.type));
+ comparator = new ClusteringComparator(clusteringTypes);
resource = DataResource.table(keyspace, name);
if (builder.isOffline)
@@ -750,7 +754,10 @@ public class TableMetadata implements SchemaElement
public ClusteringComparator partitionKeyAsClusteringComparator()
{
- return new ClusteringComparator(partitionKeyColumns.stream().map(c ->
c.type).collect(toList()));
+ AbstractType<?>[] clusteringTypes = new
AbstractType<?>[partitionKeyColumns.size()];
+ for (int i = 0; i < clusteringTypes.length; i++)
+ clusteringTypes[i] = partitionKeyColumns.get(i).type;
+ return new ClusteringComparator(clusteringTypes);
}
/**
diff --git
a/src/java/org/apache/cassandra/serializers/AbstractTypeSerializer.java
b/src/java/org/apache/cassandra/serializers/AbstractTypeSerializer.java
index 1be4d61f68..387fae7906 100644
--- a/src/java/org/apache/cassandra/serializers/AbstractTypeSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/AbstractTypeSerializer.java
@@ -20,8 +20,6 @@ package org.apache.cassandra.serializers;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -38,11 +36,12 @@ public class AbstractTypeSerializer
ByteBufferUtil.writeWithVIntLength(UTF8Type.instance.decompose(type.toString()),
out);
}
- public void serializeList(List<AbstractType<?>> types, DataOutputPlus out)
throws IOException
+ public void serializeArray(AbstractType<?>[] types, DataOutputPlus out)
throws IOException
{
- out.writeUnsignedVInt32(types.size());
- for (AbstractType<?> type : types)
- serialize(type, out);
+ int size = types.length;
+ out.writeUnsignedVInt32(size);
+ for (int i = 0; i < size; i++)
+ serialize(types[i], out);
}
public AbstractType<?> deserialize(DataInputPlus in) throws IOException
@@ -51,12 +50,12 @@ public class AbstractTypeSerializer
return TypeParser.parse(UTF8Type.instance.compose(raw));
}
- public List<AbstractType<?>> deserializeList(DataInputPlus in) throws
IOException
+ public AbstractType<?>[] deserializeArray(DataInputPlus in) throws
IOException
{
int size = (int) in.readUnsignedVInt();
- List<AbstractType<?>> types = new ArrayList<>(size);
+ AbstractType<?>[] types = new AbstractType<?>[size];
for (int i = 0; i < size; i++)
- types.add(deserialize(in));
+ types[i] = deserialize(in);
return types;
}
@@ -65,11 +64,11 @@ public class AbstractTypeSerializer
return
ByteBufferUtil.serializedSizeWithVIntLength(UTF8Type.instance.decompose(type.toString()));
}
- public long serializedListSize(List<AbstractType<?>> types)
+ public long serializedArraySize(AbstractType<?>[] types)
{
- long size = TypeSizes.sizeofUnsignedVInt(types.size());
- for (AbstractType<?> type : types)
- size += serializedSize(type);
+ long size = TypeSizes.sizeofUnsignedVInt(types.length);
+ for (int i = 0; i < types.length; i++)
+ size += serializedSize(types[i]);
return size;
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/service/pager/PagingState.java
b/src/java/org/apache/cassandra/service/pager/PagingState.java
index ef19275501..5abca604e0 100644
--- a/src/java/org/apache/cassandra/service/pager/PagingState.java
+++ b/src/java/org/apache/cassandra/service/pager/PagingState.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.service.pager;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -369,7 +368,7 @@ public class PagingState
this.protocolVersion = protocolVersion;
}
- private static List<AbstractType<?>> makeClusteringTypes(TableMetadata
metadata)
+ private static AbstractType<?>[] makeClusteringTypes(TableMetadata
metadata)
{
// This is the types that will be used when serializing the
clustering in the paging state. We can't really use the actual clustering
// types however because we can't guarantee that there won't be a
schema change between when we send the paging state and get it back,
@@ -377,9 +376,8 @@ public class PagingState
// (say timestamp -> blob). So we simply use a list of BytesTypes
(for both reading and writting), which may be slightly inefficient
// for fixed-width types, but avoid any risk during schema changes.
int size = metadata.clusteringColumns().size();
- List<AbstractType<?>> l = new ArrayList<>(size);
- for (int i = 0; i < size; i++)
- l.add(BytesType.instance);
+ AbstractType<?>[] l = new AbstractType<?>[size];
+ Arrays.fill(l, BytesType.instance);
return l;
}
diff --git a/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java
b/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java
index 2f526a5c23..3d56b723b9 100644
--- a/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java
+++ b/src/java/org/apache/cassandra/tools/SSTableMetadataViewer.java
@@ -21,8 +21,8 @@ import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Comparator;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -403,7 +403,7 @@ public class SSTableMetadataViewer
{
EncodingStats encodingStats = header.getEncodingStats();
AbstractType<?> keyType = header.getKeyType();
- List<AbstractType<?>> clusteringTypes =
header.getClusteringTypes();
+ AbstractType<?>[] clusteringTypes = header.getClusteringTypes();
Map<ByteBuffer, AbstractType<?>> staticColumns =
header.getStaticColumns();
Map<String, String> statics = staticColumns.entrySet().stream()
.collect(Collectors.toMap(e ->
UTF8Type.instance.getString(e.getKey()),
@@ -420,7 +420,7 @@ public class SSTableMetadataViewer
field("EncodingStats minTimestamp",
toDateString(encodingStats.minTimestamp, tsUnit),
Long.toString(encodingStats.minTimestamp));
field("KeyType", keyType.toString());
- field("ClusteringTypes", clusteringTypes.toString());
+ field("ClusteringTypes", Arrays.toString(clusteringTypes));
field("StaticColumns", FBUtilities.toString(statics));
field("RegularColumns", FBUtilities.toString(regulars));
if (stats != null)
diff --git a/src/java/org/apache/cassandra/tools/Util.java
b/src/java/org/apache/cassandra/tools/Util.java
index 4c4557624f..20845a7e54 100644
--- a/src/java/org/apache/cassandra/tools/Util.java
+++ b/src/java/org/apache/cassandra/tools/Util.java
@@ -37,6 +37,7 @@ import com.google.common.collect.Lists;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.db.SerializationHeader;
+import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.exceptions.ConfigurationException;
@@ -331,9 +332,10 @@ public final class Util
builder.addRegularColumn(ident, entry.getValue());
});
builder.addPartitionKeyColumn("PartitionKey", header.getKeyType());
- for (int i = 0; i < header.getClusteringTypes().size(); i++)
+ AbstractType<?>[] clusteringTypes = header.getClusteringTypes();
+ for (int i = 0; i < clusteringTypes.length; i++)
{
- builder.addClusteringColumn("clustering" + (i > 0 ? i : ""),
header.getClusteringTypes().get(i));
+ builder.addClusteringColumn("clustering" + (i > 0 ? i : ""),
clusteringTypes[i]);
}
if (SecondaryIndexManager.isIndexColumnFamily(desc.cfname))
{
@@ -346,4 +348,4 @@ public final class Util
}
return builder.build();
}
-}
\ No newline at end of file
+}
diff --git a/test/bin/jmh b/test/bin/jmh
index 53cf166de9..9bb6310151 100755
--- a/test/bin/jmh
+++ b/test/bin/jmh
@@ -136,6 +136,13 @@ CLASSPATH="$CLASSPATH:$CASSANDRA_HOME/test/conf/"
CLASSPATH="$CLASSPATH:$CASSANDRA_HOME/build/test/classes/"
CLASSPATH="$CLASSPATH:$CASSANDRA_HOME/build/test/lib/jars/*"
-exec $NUMACTL "$JAVA" -Xmx8g -XX:+UseParallelGC -cp "$CLASSPATH"
org.openjdk.jmh.Main -jvmArgs="-Djmh.shutdownTimeout=120 $cassandra_parms
$JVM_OPTS" "$@"
+if [ -n "${JMH_CLASSPATH_PREFIX:-}" ] ; then
+ CLASSPATH="$JMH_CLASSPATH_PREFIX:$CLASSPATH"
+fi
+
+JMH_OPTS="${JMH_OPTS:-}"
+
+# $JMH_OPTS is deliberately unquoted: it carries several whitespace-separated
JMH flags, e.g. JMH_OPTS="-gc true -prof gc".
+exec $NUMACTL "$JAVA" -Xmx8g -XX:+UseParallelGC -cp "$CLASSPATH"
org.openjdk.jmh.Main -jvmArgs="-Djmh.shutdownTimeout=120 $cassandra_parms
$JVM_OPTS" $JMH_OPTS "$@"
# vi:ai sw=4 ts=4 tw=0 et
diff --git
a/test/harry/main/org/apache/cassandra/harry/model/BytesPartitionState.java
b/test/harry/main/org/apache/cassandra/harry/model/BytesPartitionState.java
index b64d9613bb..e7adc26ae4 100644
--- a/test/harry/main/org/apache/cassandra/harry/model/BytesPartitionState.java
+++ b/test/harry/main/org/apache/cassandra/harry/model/BytesPartitionState.java
@@ -572,7 +572,7 @@ public class BytesPartitionState
selectionOrder = symbolListBuilder.buildAndClear();
regularAndStaticColumns =
symbolListBuilder.addAll(staticColumns).addAll(regularColumns).buildAndClear();
- clusteringComparator = new
ClusteringComparator(clusteringColumns.stream().map(Symbol::rawType).collect(Collectors.toList()));
+ clusteringComparator = new
ClusteringComparator(clusteringColumns.stream().map(Symbol::rawType).toArray(AbstractType<?>[]::new));
List<Comparator<Object>> pkComparators = new
ArrayList<>(partitionColumns.size());
for (var p : partitionColumns)
diff --git
a/test/microbench/org/apache/cassandra/test/microbench/ClusteringPrefixDeserializerBench.java
b/test/microbench/org/apache/cassandra/test/microbench/ClusteringPrefixDeserializerBench.java
new file mode 100644
index 0000000000..d569b7c760
--- /dev/null
+++
b/test/microbench/org/apache/cassandra/test/microbench/ClusteringPrefixDeserializerBench.java
@@ -0,0 +1,169 @@
+/*
+ * 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.test.microbench;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.db.ClusteringBoundOrBoundary;
+import org.apache.cassandra.db.ClusteringComparator;
+import org.apache.cassandra.db.ClusteringPrefix;
+import org.apache.cassandra.db.SerializationHeader;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.rows.UnfilteredSerializer;
+import org.apache.cassandra.io.sstable.format.SSTableFormat;
+import org.apache.cassandra.io.util.DataInputBuffer;
+import org.apache.cassandra.io.util.DataOutputBuffer;
+import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+/**
+ * Measures {@link ClusteringPrefix.Deserializer} while rotating over 1 to 3
header sources: a two-column memtable
+ * header, a one-column memtable header, and a two-column header round-tripped
through the SSTable metadata. This is
+ * what shapes the profile of the clustering-type lookup in {@code
deserializeOne} (CASSANDRA-21587). Every operation
+ * reads one non-empty component, so all sources do the same work.
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@Fork(value = 3,
+ jvmArgsAppend = { "-Xms1G", "-Xmx1G", "-Djmh.executor=CUSTOM",
+
"-Djmh.executor.class=org.apache.cassandra.test.microbench.FastThreadExecutor"
})
+@Threads(1)
+@State(Scope.Thread)
+public class ClusteringPrefixDeserializerBench
+{
+ @Param({ "1", "2", "3" })
+ public int headerSources;
+
+ private DeserializerState[] states;
+ private int index;
+
+ @Setup
+ public void setup() throws Exception
+ {
+ DatabaseDescriptor.daemonInitialization();
+
+ TableMetadata oneColumn = schema("one_column", 1);
+ TableMetadata twoColumns = schema("two_columns", 2);
+ SerializationHeader oneColumnHeader =
SerializationHeader.makeWithoutStats(oneColumn);
+ SerializationHeader twoColumnHeader =
SerializationHeader.makeWithoutStats(twoColumns);
+ SerializationHeader sstableHeader = deserializeHeader(twoColumnHeader,
twoColumns);
+
+ DeserializerState twoColumn = state(twoColumns.comparator,
twoColumnHeader);
+ DeserializerState singleColumn = state(oneColumn.comparator,
oneColumnHeader);
+ DeserializerState sstable = state(twoColumns.comparator,
sstableHeader);
+
+ switch (headerSources)
+ {
+ case 1:
+ states = new DeserializerState[] { twoColumn };
+ break;
+ case 2:
+ states = new DeserializerState[] { twoColumn, singleColumn };
+ break;
+ case 3:
+ states = new DeserializerState[] { twoColumn, singleColumn,
sstable };
+ break;
+ default:
+ throw new AssertionError("Unsupported number of header
sources: " + headerSources);
+ }
+ }
+
+ /**
+ * Rotates through {@link #states} with a conditional reset rather than
{@code index++ % states.length}: a modulo
+ * by a non-constant divisor costs an integer division (tens of cycles) on
an operation that only takes a few tens
+ * of nanoseconds, and {@code index++} eventually overflows negative. The
reset keeps the exact 1/2/3 mix of header
+ * sources, which is what shapes the call-site profile being measured.
+ */
+ @Benchmark
+ public ClusteringBoundOrBoundary<byte[]> deserializeBound() throws
IOException
+ {
+ int i = index;
+ DeserializerState state = states[i];
+ index = ++i == states.length ? 0 : i;
+ state.in.buffer().rewind();
+ state.deserializer.prepare(UnfilteredSerializer.IS_MARKER, 0);
+ return state.deserializer.deserializeNextBound();
+ }
+
+ private static TableMetadata schema(String name, int clusteringColumns)
+ {
+ TableMetadata.Builder builder =
TableMetadata.builder("ClusteringPrefixDeserializerBench", name)
+
.addPartitionKeyColumn("k", Int32Type.instance);
+ for (int i = 0; i < clusteringColumns; i++)
+ builder.addClusteringColumn("c" + i, Int32Type.instance);
+ return builder.build();
+ }
+
+ private static SerializationHeader deserializeHeader(SerializationHeader
header, TableMetadata metadata)
+ throws Exception
+ {
+ SSTableFormat<?, ?> format =
DatabaseDescriptor.getSelectedSSTableFormat();
+ try (DataOutputBuffer out = new DataOutputBuffer())
+ {
+
SerializationHeader.serializer.serialize(format.getLatestVersion(),
header.toComponent(), out);
+ SerializationHeader.Component component =
SerializationHeader.serializer.deserialize(format.getLatestVersion(),
+
new DataInputBuffer(out.buffer(), true));
+ return component.toHeader(metadata);
+ }
+ }
+
+ private static DeserializerState state(ClusteringComparator comparator,
SerializationHeader header)
+ throws IOException
+ {
+ try (DataOutputBuffer out = new DataOutputBuffer())
+ {
+ out.writeByte(ClusteringPrefix.Kind.INCL_START_BOUND.ordinal());
+ out.writeShort(1);
+ out.writeUnsignedVInt32(0);
+ header.clusteringTypes()[0].writeValue(ByteBufferUtil.bytes(0),
out);
+
+ DataInputBuffer in = new DataInputBuffer(out.buffer(), true);
+ return new DeserializerState(in, new
ClusteringPrefix.Deserializer(comparator, in, header));
+ }
+ }
+
+ private static class DeserializerState
+ {
+ final DataInputBuffer in;
+ final ClusteringPrefix.Deserializer deserializer;
+
+ private DeserializerState(DataInputBuffer in,
ClusteringPrefix.Deserializer deserializer)
+ {
+ this.in = in;
+ this.deserializer = deserializer;
+ }
+ }
+}
diff --git
a/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableCursorPipeUtil.java
b/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableCursorPipeUtil.java
index 7f2c31828e..9c2611af7e 100644
---
a/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableCursorPipeUtil.java
+++
b/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableCursorPipeUtil.java
@@ -21,7 +21,6 @@ package org.apache.cassandra.test.microbench.sstable;
import java.io.IOException;
import org.apache.cassandra.db.DeletionTime;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.rows.Cell;
import org.apache.cassandra.db.rows.ReusableCellLivenessInfo;
import org.apache.cassandra.io.sstable.PartitionDescriptor;
@@ -46,7 +45,7 @@ public class SSTableCursorPipeUtil
public static void copySSTable(SSTableCursorReader reader,
SSTableCursorWriter writer) throws Throwable
{
PartitionDescriptor pHeader = new
PartitionDescriptor(reader.ssTableReader().getPartitioner().createReusableKey(0));
- UnfilteredDescriptor unfilteredDescriptor = new
UnfilteredDescriptor(reader.ssTableReader().header.clusteringTypes().toArray(AbstractType[]::new));
+ UnfilteredDescriptor unfilteredDescriptor = new
UnfilteredDescriptor(reader.ssTableReader().header.clusteringTypes());
int readerState = PARTITION_START;
boolean first = true;
diff --git
a/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableReadingFileCursorBench.java
b/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableReadingFileCursorBench.java
index 5e73ed604b..80c458ab35 100644
---
a/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableReadingFileCursorBench.java
+++
b/test/microbench/org/apache/cassandra/test/microbench/sstable/SSTableReadingFileCursorBench.java
@@ -38,7 +38,6 @@ import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.sstable.Descriptor;
import org.apache.cassandra.io.sstable.PartitionDescriptor;
import org.apache.cassandra.io.sstable.SSTableCursorReader;
@@ -112,7 +111,7 @@ public class SSTableReadingFileCursorBench
Arrays.fill(counters, 0);
int state = PARTITION_START;
PartitionDescriptor pHeader = new
PartitionDescriptor(cursor.ssTableReader().getPartitioner().createReusableKey(0));
- UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(cursor.ssTableReader().header.clusteringTypes().toArray(AbstractType[]::new));
+ UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(cursor.ssTableReader().header.clusteringTypes());
while (state != DONE) {
state = cursor.readPartitionHeader(pHeader);
counters[0]++;
diff --git a/test/unit/org/apache/cassandra/db/ClusteringComparatorTest.java
b/test/unit/org/apache/cassandra/db/ClusteringComparatorTest.java
index 586d203c81..7dc0fd25c8 100644
--- a/test/unit/org/apache/cassandra/db/ClusteringComparatorTest.java
+++ b/test/unit/org/apache/cassandra/db/ClusteringComparatorTest.java
@@ -20,9 +20,9 @@ package org.apache.cassandra.db;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
+import org.junit.Ignore;
import org.junit.Test;
import org.apache.cassandra.db.marshal.AbstractType;
@@ -32,13 +32,67 @@ import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.utils.ByteBufferUtil;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertSame;
public class ClusteringComparatorTest
{
+ @Test
+ public void clusteringTypesAreSharedNotCopied()
+ {
+ AbstractType<?>[] types = { LongType.instance, Int32Type.instance };
+ ClusteringComparator comparator = new ClusteringComparator(types);
+
+ assertSame(types, comparator.subtypes());
+ assertSame(comparator.subtypes(), comparator.subtypes());
+ assertSame(LongType.instance, comparator.subtype(0));
+
+ assertEquals(0, new ClusteringComparator().subtypes().length);
+ assertSame(new ClusteringComparator().subtypes(), new
ClusteringComparator().subtypes());
+ }
+
+ @Ignore("Enable this test if mutation detection is enabled in
ClusteringComparator")
+ @Test
+ public void mutationOfTheSharedClusteringTypesIsDetected()
+ {
+ AbstractType<?>[] types = { LongType.instance, Int32Type.instance };
+ ClusteringComparator comparator = new ClusteringComparator(types);
+
+ types[0] = Int32Type.instance;
+ try
+ {
+ comparator.subtypes();
+ throw new RuntimeException("mutating the clustering types of a
comparator must be detected");
+ }
+ catch (AssertionError e)
+ {
+ // expected
+ }
+
+ types[0] = LongType.instance;
+ assertSame(types, comparator.subtypes());
+ }
+
+ @Test
+ public void equalsAndHashCodeAreContentBased()
+ {
+ ClusteringComparator a = new ClusteringComparator(LongType.instance,
Int32Type.instance);
+ ClusteringComparator b = new ClusteringComparator(LongType.instance,
Int32Type.instance);
+ ClusteringComparator different = new
ClusteringComparator(LongType.instance);
+
+ assertEquals(a, b);
+ assertEquals(a.hashCode(), b.hashCode());
+ assertNotEquals(a, different);
+
+ ClusteringComparator empty1 = new ClusteringComparator();
+ ClusteringComparator empty2 = new ClusteringComparator();
+ assertEquals(empty1, empty2);
+ assertEquals(empty1.hashCode(), empty2.hashCode());
+ }
+
@Test
public void compareLong()
{
- Iterable<AbstractType<?>> types;
ClusteringComparator comparator = new
ClusteringComparator(LongType.instance);
for (int i=0;i<1000; i++) {
long l1 = ThreadLocalRandom.current().nextLong();
@@ -103,7 +157,7 @@ public class ClusteringComparatorTest
{
Clustering<ByteBuffer> clustering =
Clustering.make(ByteBufferUtil.bytes(v1));
DataOutputBuffer out = new DataOutputBuffer();
- Clustering.serializer.serialize(clustering, out, 0, List.of(types));
+ Clustering.serializer.serialize(clustering, out, 0, types);
return out.asNewBuffer();
}
@@ -111,7 +165,7 @@ public class ClusteringComparatorTest
{
Clustering<ByteBuffer> clustering =
Clustering.make(ByteBufferUtil.bytes(v1));
DataOutputBuffer out = new DataOutputBuffer();
- Clustering.serializer.serialize(clustering, out, 0, List.of(types));
+ Clustering.serializer.serialize(clustering, out, 0, types);
return out.asNewBuffer();
}
-}
\ No newline at end of file
+}
diff --git a/test/unit/org/apache/cassandra/db/SerializationHeaderTest.java
b/test/unit/org/apache/cassandra/db/SerializationHeaderTest.java
index 0993606739..fef3f66a13 100644
--- a/test/unit/org/apache/cassandra/db/SerializationHeaderTest.java
+++ b/test/unit/org/apache/cassandra/db/SerializationHeaderTest.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.db.compaction.OperationType;
import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
+import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.marshal.ListType;
import org.apache.cassandra.db.marshal.LongType;
@@ -52,6 +53,8 @@ import org.apache.cassandra.io.sstable.format.SSTableFormat;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.sstable.format.SSTableWriter;
import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
+import org.apache.cassandra.io.util.DataInputBuffer;
+import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.schema.ColumnMetadata;
@@ -67,6 +70,72 @@ public class SerializationHeaderTest
DatabaseDescriptor.daemonInitialization();
}
+ /** The schema, the header and its component all share one clustering-type
array (no copy). */
+ @Test
+ public void testClusteringTypesSharingContract()
+ {
+ TableMetadata schema = TableMetadata.builder(KEYSPACE,
"testClusteringTypesSharingContract")
+ .addPartitionKeyColumn("k",
Int32Type.instance)
+ .addClusteringColumn("c",
Int32Type.instance)
+ .build();
+
+ AbstractType<?>[] schemaTypes = schema.comparator.subtypes();
+ SerializationHeader header =
SerializationHeader.makeWithoutStats(schema);
+ Assert.assertSame(schemaTypes, header.clusteringTypes());
+ Assert.assertSame(schemaTypes,
header.toComponent().getClusteringTypes());
+ Assert.assertArrayEquals(new AbstractType<?>[]{ Int32Type.instance },
schemaTypes);
+ }
+
+ @Test
+ public void testComponentEqualityIsContentBased() throws Exception
+ {
+ Function<String, TableMetadata> schema = name ->
TableMetadata.builder(KEYSPACE, name)
+
.addPartitionKeyColumn("k", Int32Type.instance)
+
.addClusteringColumn("c1", Int32Type.instance)
+
.addClusteringColumn("c2", LongType.instance)
+
.addRegularColumn("v", Int32Type.instance)
+ .build();
+
+ // distinct TableMetadata instances, hence distinct clusteringTypes
arrays with equal content
+ SerializationHeader.Component component =
SerializationHeader.makeWithoutStats(schema.apply("t")).toComponent();
+ SerializationHeader.Component same =
SerializationHeader.makeWithoutStats(schema.apply("t")).toComponent();
+ Assert.assertEquals(component, same);
+ Assert.assertEquals(component.hashCode(), same.hashCode());
+
+ TableMetadata otherClustering = TableMetadata.builder(KEYSPACE, "t")
+
.addPartitionKeyColumn("k", Int32Type.instance)
+ .addClusteringColumn("c1",
Int32Type.instance)
+ .addRegularColumn("v",
Int32Type.instance)
+ .build();
+ Assert.assertNotEquals(component,
SerializationHeader.makeWithoutStats(otherClustering).toComponent());
+
+ SSTableFormat<?, ?> format =
DatabaseDescriptor.getSelectedSSTableFormat();
+ try (DataOutputBuffer out = new DataOutputBuffer())
+ {
+
SerializationHeader.serializer.serialize(format.getLatestVersion(), component,
out);
+ SerializationHeader.Component deserialized =
+
SerializationHeader.serializer.deserialize(format.getLatestVersion(), new
DataInputBuffer(out.buffer(), true));
+ Assert.assertEquals(component, deserialized);
+ Assert.assertEquals(component.hashCode(), deserialized.hashCode());
+ }
+ }
+
+ @Test
+ public void testToStringRendersClusteringTypes() throws
UnknownColumnException
+ {
+ TableMetadata schema = TableMetadata.builder(KEYSPACE,
"testToStringRendersClusteringTypes")
+ .addPartitionKeyColumn("k",
Int32Type.instance)
+ .addClusteringColumn("c1",
Int32Type.instance)
+ .addClusteringColumn("c2",
LongType.instance)
+ .build();
+
+ SerializationHeader.Component component =
SerializationHeader.makeWithoutStats(schema).toComponent();
+ String expectedTypes = Int32Type.instance + ", " + LongType.instance;
+ Assert.assertTrue(component.toString(),
component.toString().contains("cks=[" + expectedTypes + ']'));
+ Assert.assertTrue(component.toHeader(schema).toString(),
+
component.toHeader(schema).toString().contains("cks=[" + expectedTypes + ']'));
+ }
+
/**
* Common case: schema unchanged since the SSTable was written.
*/
@@ -324,4 +393,25 @@ public class SerializationHeaderTest
}
}
+ private static TableMetadata schemaWithClusteringColumns(int
clusteringColumns)
+ {
+ TableMetadata.Builder builder = TableMetadata.builder(KEYSPACE,
"testClusteringTypesContainer" + clusteringColumns)
+
.addPartitionKeyColumn("k", Int32Type.instance);
+ for (int i = 0; i < clusteringColumns; i++)
+ builder.addClusteringColumn("c" + i, Int32Type.instance);
+ return builder.build();
+ }
+
+ private static SerializationHeader deserializeHeader(SerializationHeader
header, TableMetadata schema) throws Exception
+ {
+ SSTableFormat<?, ?> format =
DatabaseDescriptor.getSelectedSSTableFormat();
+ try (DataOutputBuffer out = new DataOutputBuffer())
+ {
+
SerializationHeader.serializer.serialize(format.getLatestVersion(),
header.toComponent(), out);
+ return
SerializationHeader.serializer.deserialize(format.getLatestVersion(),
+ new
DataInputBuffer(out.buffer(), true))
+ .toHeader(schema);
+ }
+ }
+
}
diff --git
a/test/unit/org/apache/cassandra/db/compaction/StatefulCursorTest.java
b/test/unit/org/apache/cassandra/db/compaction/StatefulCursorTest.java
index 33f5aedd5a..df12139885 100644
--- a/test/unit/org/apache/cassandra/db/compaction/StatefulCursorTest.java
+++ b/test/unit/org/apache/cassandra/db/compaction/StatefulCursorTest.java
@@ -346,7 +346,7 @@ public class StatefulCursorTest extends CQLTester
*/
private static void driveReader(SSTableReader sstable, boolean
overDeclareRowBody) throws Exception
{
- AbstractType<?>[] clusteringTypes =
sstable.header.clusteringTypes().toArray(AbstractType[]::new);
+ AbstractType<?>[] clusteringTypes = sstable.header.clusteringTypes();
UnfilteredDescriptor unfiltered = overDeclareRowBody
? new
UnfilteredDescriptor(clusteringTypes)
{
diff --git
a/test/unit/org/apache/cassandra/db/compaction/differential/ComplexColumnCursorReadTest.java
b/test/unit/org/apache/cassandra/db/compaction/differential/ComplexColumnCursorReadTest.java
index 74fdafec33..ca0b2c71a1 100644
---
a/test/unit/org/apache/cassandra/db/compaction/differential/ComplexColumnCursorReadTest.java
+++
b/test/unit/org/apache/cassandra/db/compaction/differential/ComplexColumnCursorReadTest.java
@@ -26,7 +26,6 @@ import org.junit.Test;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.rows.Cell;
import org.apache.cassandra.db.rows.ColumnData;
import org.apache.cassandra.db.rows.ComplexColumnData;
@@ -174,7 +173,7 @@ public class ComplexColumnCursorReadTest extends CQLTester
cursor.pauseAtEmptyComplexColumns(true);
PartitionDescriptor pHeader = new
PartitionDescriptor(sstable.getPartitioner().createReusableKey(0));
- UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes().toArray(AbstractType[]::new));
+ UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes());
byte[] transfer = new byte[4096];
int state = cursor.readPartitionHeader(pHeader);
@@ -367,7 +366,7 @@ public class ComplexColumnCursorReadTest extends CQLTester
DataOutputBuffer valueSink = new DataOutputBuffer())
{
PartitionDescriptor pHeader = new
PartitionDescriptor(sstable.getPartitioner().createReusableKey(0));
- UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes().toArray(AbstractType[]::new));
+ UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes());
byte[] transfer = new byte[4096];
int state = cursor.readPartitionHeader(pHeader);
diff --git a/test/unit/org/apache/cassandra/db/filter/SliceTest.java
b/test/unit/org/apache/cassandra/db/filter/SliceTest.java
index 18233f09f5..db9754f00e 100644
--- a/test/unit/org/apache/cassandra/db/filter/SliceTest.java
+++ b/test/unit/org/apache/cassandra/db/filter/SliceTest.java
@@ -19,8 +19,6 @@
package org.apache.cassandra.db.filter;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
import org.junit.Test;
@@ -48,10 +46,10 @@ public class SliceTest
@Test
public void testIntersectsSingleSlice()
{
- List<AbstractType<?>> types = new ArrayList<>();
- types.add(Int32Type.instance);
- types.add(Int32Type.instance);
- types.add(Int32Type.instance);
+ AbstractType<?>[] types = new AbstractType<?>[3];
+ types[0] = Int32Type.instance;
+ types[1] = Int32Type.instance;
+ types[2] = Int32Type.instance;
ClusteringComparator cc = new ClusteringComparator(types);
ClusteringPrefix.Kind sk = INCL_START_BOUND;
@@ -279,10 +277,10 @@ public class SliceTest
@Test
public void testDifferentMinMaxLengths()
{
- List<AbstractType<?>> types = new ArrayList<>();
- types.add(Int32Type.instance);
- types.add(Int32Type.instance);
- types.add(Int32Type.instance);
+ AbstractType<?>[] types = new AbstractType<?>[3];
+ types[0] = Int32Type.instance;
+ types[1] = Int32Type.instance;
+ types[2] = Int32Type.instance;
ClusteringComparator cc = new ClusteringComparator(types);
ClusteringPrefix.Kind sk = INCL_START_BOUND;
@@ -321,10 +319,10 @@ public class SliceTest
@Test
public void testSliceNormalization()
{
- List<AbstractType<?>> types = new ArrayList<>();
- types.add(Int32Type.instance);
- types.add(Int32Type.instance);
- types.add(Int32Type.instance);
+ AbstractType<?>[] types = new AbstractType<?>[3];
+ types[0] = Int32Type.instance;
+ types[1] = Int32Type.instance;
+ types[2] = Int32Type.instance;
ClusteringComparator cc = new ClusteringComparator(types);
assertSlicesNormalization(cc, slices(s(0, 2), s(2, 4)), slices(s(0,
4)));
@@ -337,9 +335,9 @@ public class SliceTest
@Test
public void testIsEmpty()
{
- List<AbstractType<?>> types = new ArrayList<>();
- types.add(Int32Type.instance);
- types.add(Int32Type.instance);
+ AbstractType<?>[] types = new AbstractType<?>[2];
+ types[0] = Int32Type.instance;
+ types[1] = Int32Type.instance;
ClusteringComparator cc = new ClusteringComparator(types);
assertFalse(Slice.isEmpty(cc, makeBound(INCL_START_BOUND, 5, 0),
makeBound(INCL_END_BOUND, 5, 0)));
@@ -406,4 +404,4 @@ public class SliceTest
assertFalse(String.format("Slice %s should not intersect with slice
%s", s1.toString(cc), s2.toString(cc)), s1.intersects(cc, s2));
assertFalse(String.format("Slice %s should not intersect with slice
%s", s2.toString(cc), s1.toString(cc)), s2.intersects(cc, s1));
}
-}
\ No newline at end of file
+}
diff --git
a/test/unit/org/apache/cassandra/io/compress/CompressedSequentialWriterTest.java
b/test/unit/org/apache/cassandra/io/compress/CompressedSequentialWriterTest.java
index bf5e9b83a2..e25c3e7c34 100644
---
a/test/unit/org/apache/cassandra/io/compress/CompressedSequentialWriterTest.java
+++
b/test/unit/org/apache/cassandra/io/compress/CompressedSequentialWriterTest.java
@@ -248,7 +248,7 @@ public class CompressedSequentialWriterTest extends
SequentialWriterTest
private void testWrite(File f, int bytesToTest, boolean useMemmap) throws
IOException
{
final String filename = f.absolutePath();
- MetadataCollector sstableMetadataCollector = new MetadataCollector(new
ClusteringComparator(Collections.singletonList(BytesType.instance)));
+ MetadataCollector sstableMetadataCollector = new MetadataCollector(new
ClusteringComparator(BytesType.instance));
byte[] dataPre = new byte[bytesToTest];
byte[] rawPost = new byte[bytesToTest];
@@ -348,7 +348,7 @@ public class CompressedSequentialWriterTest extends
SequentialWriterTest
File f = FileUtils.createTempFile("testUncompressedChunks", "1");
String filename = f.path();
- MetadataCollector sstableMetadataCollector = new MetadataCollector(new
ClusteringComparator(Collections.singletonList(BytesType.instance)));
+ MetadataCollector sstableMetadataCollector = new MetadataCollector(new
ClusteringComparator(BytesType.instance));
compressionParameters = new
CompressionParams(MockCompressor.class.getTypeName(),
MockCompressor.paramsFor(ratio, extra),
DEFAULT_CHUNK_LENGTH,
ratio);
diff --git
a/test/unit/org/apache/cassandra/io/compress/DirectCompressedSequentialWriterTest.java
b/test/unit/org/apache/cassandra/io/compress/DirectCompressedSequentialWriterTest.java
index 68833f1530..d801d2487f 100644
---
a/test/unit/org/apache/cassandra/io/compress/DirectCompressedSequentialWriterTest.java
+++
b/test/unit/org/apache/cassandra/io/compress/DirectCompressedSequentialWriterTest.java
@@ -31,7 +31,6 @@ import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -102,7 +101,7 @@ public class DirectCompressedSequentialWriterTest
private static MetadataCollector newCollector()
{
- return new MetadataCollector(new
ClusteringComparator(Collections.singletonList(BytesType.instance)));
+ return new MetadataCollector(new
ClusteringComparator(BytesType.instance));
}
@FunctionalInterface
diff --git
a/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCopyCellValueTest.java
b/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCopyCellValueTest.java
index be91d7604a..b4c14f456e 100644
---
a/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCopyCellValueTest.java
+++
b/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCopyCellValueTest.java
@@ -26,7 +26,6 @@ import org.junit.Test;
import org.apache.cassandra.cache.ChunkCache;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.io.sstable.format.SSTableReader;
@@ -224,7 +223,7 @@ public class SSTableCursorReaderCopyCellValueTest extends
CQLTester
{
byte[] transfer = new byte[4096];
PartitionDescriptor pHeader = new
PartitionDescriptor(sstable.getPartitioner().createReusableKey(0));
- UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes().toArray(AbstractType[]::new));
+ UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes());
int state = cursor.readPartitionHeader(pHeader);
byte[] copied = null;
diff --git
a/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCorruptCellCountTest.java
b/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCorruptCellCountTest.java
index 057a7e73ed..806afb15e6 100644
---
a/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCorruptCellCountTest.java
+++
b/test/unit/org/apache/cassandra/io/sstable/SSTableCursorReaderCorruptCellCountTest.java
@@ -25,7 +25,6 @@ import org.junit.Test;
import org.apache.cassandra.cache.ChunkCache;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.io.util.RandomAccessReader;
@@ -112,7 +111,7 @@ public class SSTableCursorReaderCorruptCellCountTest
extends CQLTester
try (SSTableCursorReader cursor = new SSTableCursorReader(sstable))
{
PartitionDescriptor pHeader = new
PartitionDescriptor(sstable.getPartitioner().createReusableKey(0));
- UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes().toArray(AbstractType[]::new));
+ UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes());
int state = cursor.readPartitionHeader(pHeader);
assertEquals("expected the single row to open directly",
ROW_START, state);
@@ -152,7 +151,7 @@ public class SSTableCursorReaderCorruptCellCountTest
extends CQLTester
try (SSTableCursorReader cursor = new SSTableCursorReader(sstable))
{
PartitionDescriptor pHeader = new
PartitionDescriptor(sstable.getPartitioner().createReusableKey(0));
- UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes().toArray(AbstractType[]::new));
+ UnfilteredDescriptor rHeader = new
UnfilteredDescriptor(sstable.header.clusteringTypes());
byte[] transfer = new byte[4096];
int state = cursor.readPartitionHeader(pHeader);
diff --git
a/test/unit/org/apache/cassandra/io/sstable/format/DataComponentDirectWriteSelectionTest.java
b/test/unit/org/apache/cassandra/io/sstable/format/DataComponentDirectWriteSelectionTest.java
index e55f364373..0bbf149904 100644
---
a/test/unit/org/apache/cassandra/io/sstable/format/DataComponentDirectWriteSelectionTest.java
+++
b/test/unit/org/apache/cassandra/io/sstable/format/DataComponentDirectWriteSelectionTest.java
@@ -19,7 +19,6 @@
package org.apache.cassandra.io.sstable.format;
import java.nio.file.Files;
-import java.util.Collections;
import org.junit.After;
import org.junit.Before;
@@ -158,7 +157,7 @@ public class DataComponentDirectWriteSelectionTest
{
Descriptor descriptor = new Descriptor(tmpDir, KS, CF, new
SequenceBasedSSTableId(nextId++),
DatabaseDescriptor.getSelectedSSTableFormat());
- MetadataCollector collector = new MetadataCollector(new
ClusteringComparator(Collections.singletonList(BytesType.instance)));
+ MetadataCollector collector = new MetadataCollector(new
ClusteringComparator(BytesType.instance));
// finishOnClose(false) routes try-with-resources close through abort,
avoiding finish() side effects on an empty writer.
SequentialWriterOption options =
SequentialWriterOption.newBuilder().finishOnClose(false).build();
return DataComponent.buildWriter(descriptor, metadata, options,
collector, op, FlushCompression.fast, null);
diff --git
a/test/unit/org/apache/cassandra/io/sstable/format/big/RowIndexEntryTest.java
b/test/unit/org/apache/cassandra/io/sstable/format/big/RowIndexEntryTest.java
index acd41cd111..ac853663e6 100644
---
a/test/unit/org/apache/cassandra/io/sstable/format/big/RowIndexEntryTest.java
+++
b/test/unit/org/apache/cassandra/io/sstable/format/big/RowIndexEntryTest.java
@@ -49,7 +49,6 @@ import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.LivenessInfo;
import org.apache.cassandra.db.SerializationHeader;
import org.apache.cassandra.db.TypeSizes;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.LongType;
import org.apache.cassandra.db.partitions.ImmutableBTreePartition;
import org.apache.cassandra.db.rows.AbstractUnfilteredRowIterator;
@@ -89,8 +88,7 @@ import static org.junit.Assert.assertTrue;
public class RowIndexEntryTest extends CQLTester
{
- private static final List<AbstractType<?>> clusterTypes =
Collections.singletonList(LongType.instance);
- private static final ClusteringComparator comp = new
ClusteringComparator(clusterTypes);
+ private static final ClusteringComparator comp = new
ClusteringComparator(LongType.instance);
private static final byte[] dummy_100k = new byte[100000];
diff --git
a/test/unit/org/apache/cassandra/serializers/AbstractTypeSerializerTest.java
b/test/unit/org/apache/cassandra/serializers/AbstractTypeSerializerTest.java
new file mode 100644
index 0000000000..0495e7ccaa
--- /dev/null
+++ b/test/unit/org/apache/cassandra/serializers/AbstractTypeSerializerTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.serializers;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.io.util.DataInputBuffer;
+import org.apache.cassandra.io.util.DataOutputBuffer;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class AbstractTypeSerializerTest
+{
+ private final AbstractTypeSerializer serializer = new
AbstractTypeSerializer();
+
+ @Test
+ public void testSerializedArraySizeMatchesSerializeArray() throws
IOException
+ {
+ assertRoundTrip();
+ assertRoundTrip(Int32Type.instance);
+ assertRoundTrip(Int32Type.instance, UTF8Type.instance);
+ assertRoundTrip(Int32Type.instance, UTF8Type.instance,
Int32Type.instance, UTF8Type.instance);
+ }
+
+ private void assertRoundTrip(AbstractType<?>... types) throws IOException
+ {
+ try (DataOutputBuffer out = new DataOutputBuffer())
+ {
+ serializer.serializeArray(types, out);
+
+ assertEquals("serializedArraySize must match the number of bytes
written by serializeArray",
+ out.getLength(),
serializer.serializedArraySize(types));
+
+ try (DataInputBuffer in = new DataInputBuffer(out.buffer(), true))
+ {
+ AbstractType<?>[] deserialized =
serializer.deserializeArray(in);
+ assertArrayEquals(types, deserialized);
+ }
+ }
+ }
+}
diff --git
a/test/unit/org/apache/cassandra/utils/bytecomparable/ByteSourceSequenceTest.java
b/test/unit/org/apache/cassandra/utils/bytecomparable/ByteSourceSequenceTest.java
index 3344424d96..b3a009be00 100644
---
a/test/unit/org/apache/cassandra/utils/bytecomparable/ByteSourceSequenceTest.java
+++
b/test/unit/org/apache/cassandra/utils/bytecomparable/ByteSourceSequenceTest.java
@@ -20,7 +20,6 @@ package org.apache.cassandra.utils.bytecomparable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@@ -298,35 +297,35 @@ public class ByteSourceSequenceTest
private static final IntegerType VARINT = IntegerType.instance;
// A regular comparator using the natural ordering for all types.
- private static final ClusteringComparator COMP = new
ClusteringComparator(Arrays.asList(
+ private static final ClusteringComparator COMP = new ClusteringComparator(
UTF8,
DECIMAL,
VARINT
- ));
+ );
// A comparator that reverses the ordering for the first unknown length
type
- private static final ClusteringComparator COMP_REVERSED_UNKNOWN_LENGTH =
new ClusteringComparator(Arrays.asList(
+ private static final ClusteringComparator COMP_REVERSED_UNKNOWN_LENGTH =
new ClusteringComparator(
ReversedType.getInstance(UTF8),
DECIMAL,
VARINT
- ));
+ );
// A comparator that reverses the ordering for the second unknown length
type
- private static final ClusteringComparator COMP_REVERSED_UNKNOWN_LENGTH_2 =
new ClusteringComparator(Arrays.asList(
+ private static final ClusteringComparator COMP_REVERSED_UNKNOWN_LENGTH_2 =
new ClusteringComparator(
UTF8,
ReversedType.getInstance(DECIMAL),
VARINT
- ));
+ );
// A comparator that reverses the ordering for the sole known/computable
length type
- private static final ClusteringComparator COMP_REVERSED_KNOWN_LENGTH = new
ClusteringComparator(Arrays.asList(
+ private static final ClusteringComparator COMP_REVERSED_KNOWN_LENGTH = new
ClusteringComparator(
UTF8,
DECIMAL,
ReversedType.getInstance(VARINT)
- ));
+ );
// A comparator that reverses the ordering for all types
- private static final ClusteringComparator COMP_ALL_REVERSED = new
ClusteringComparator(Arrays.asList(
+ private static final ClusteringComparator COMP_ALL_REVERSED = new
ClusteringComparator(
ReversedType.getInstance(UTF8),
ReversedType.getInstance(DECIMAL),
ReversedType.getInstance(VARINT)
- ));
+ );
@Test
public void testClusteringPrefixBoundNormalAndReversed()
@@ -536,7 +535,7 @@ public class ByteSourceSequenceTest
AbstractType<String> reversedStringType =
ReversedType.getInstance(UTF8);
AbstractType<BigDecimal> reversedDecimalType =
ReversedType.getInstance(DECIMAL);
- final ClusteringComparator comparator = new
ClusteringComparator(Arrays.asList(
+ final ClusteringComparator comparator = new ClusteringComparator(
// unknown length type
UTF8,
// known length type
@@ -545,7 +544,7 @@ public class ByteSourceSequenceTest
reversedStringType,
// reversed known length type
reversedDecimalType
- ));
+ );
ByteBuffer[] clusteringKeyValues = new ByteBuffer[] {
UTF8.decompose(stringValue),
DECIMAL.decompose(decimalValue),
@@ -553,7 +552,7 @@ public class ByteSourceSequenceTest
DECIMAL.decompose(decimalValue)
};
- final ClusteringComparator comparator2 = new
ClusteringComparator(Arrays.asList(
+ final ClusteringComparator comparator2 = new ClusteringComparator(
// known length type
DECIMAL,
// unknown length type
@@ -562,7 +561,7 @@ public class ByteSourceSequenceTest
reversedDecimalType,
// reversed unknown length type
reversedStringType
- ));
+ );
ByteBuffer[] clusteringKeyValues2 = new ByteBuffer[] {
DECIMAL.decompose(decimalValue),
UTF8.decompose(stringValue),
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]