Cache various Enum.values() used in deserialization to avoid per-read array allocation

patch by Koo Taejin; reviewed by Dmitry Konstantinov, Stefan Miklosovic for CASSANDRA-21528
This commit is contained in:
koo.taejin 2026-07-20 23:11:58 +09:00 committed by Stefan Miklosovic
parent f51c29c196
commit 03304bce53
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
13 changed files with 82 additions and 19 deletions

View File

@ -1,4 +1,5 @@
6.0-alpha2 6.0-alpha2
* Cache various Enum.values() used in deserialization to avoid per-read array allocation (CASSANDRA-21528)
* Fix Accord transaction error message when altering a table (CASSANDRA-20580) * Fix Accord transaction error message when altering a table (CASSANDRA-20580)
* Depend only on platform-specific Zstd JNI native libraries (CASSANDRA-21483) * Depend only on platform-specific Zstd JNI native libraries (CASSANDRA-21483)
* Expose immediately-executed tasks in the queries virtual table (CASSANDRA-21471) * Expose immediately-executed tasks in the queries virtual table (CASSANDRA-21471)

View File

@ -96,12 +96,19 @@ public abstract class Selector
SLICE_SELECTOR(ElementsSelector.SliceSelector.deserializer), SLICE_SELECTOR(ElementsSelector.SliceSelector.deserializer),
VECTOR_SELECTOR(VectorSelector.deserializer); VECTOR_SELECTOR(VectorSelector.deserializer);
private static final Kind[] VALUES = values();
private final SelectorDeserializer deserializer; private final SelectorDeserializer deserializer;
Kind(SelectorDeserializer deserializer) Kind(SelectorDeserializer deserializer)
{ {
this.deserializer = deserializer; this.deserializer = deserializer;
} }
public static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
} }
/** /**
@ -260,7 +267,7 @@ public abstract class Selector
public Selector deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException public Selector deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
{ {
Kind kind = Kind.values()[in.readUnsignedByte()]; Kind kind = Kind.fromOrdinal(in.readUnsignedByte());
return kind.deserializer.deserialize(in, version, metadata); return kind.deserializer.deserialize(in, version, metadata);
} }

View File

@ -116,7 +116,7 @@ public interface ClusteringBoundOrBoundary<V> extends ClusteringPrefix<V>
public ClusteringBoundOrBoundary<byte[]> deserialize(DataInputPlus in, int version, List<AbstractType<?>> types) throws IOException public ClusteringBoundOrBoundary<byte[]> deserialize(DataInputPlus in, int version, List<AbstractType<?>> types) throws IOException
{ {
Kind kind = Kind.values()[in.readByte()]; Kind kind = Kind.fromOrdinal(in.readByte());
return deserializeValues(in, kind, version, types); return deserializeValues(in, kind, version, types);
} }

View File

@ -84,6 +84,8 @@ public interface ClusteringPrefix<V> extends IMeasurableMemory, Clusterable<V>
SSTABLE_UPPER_BOUND ( 4, 1, v -> ByteSource.GTGT_NEXT_COMPONENT); SSTABLE_UPPER_BOUND ( 4, 1, v -> ByteSource.GTGT_NEXT_COMPONENT);
// @formatter:on // @formatter:on
private static final Kind[] VALUES = values();
private final int comparison; private final int comparison;
/** /**
@ -101,6 +103,11 @@ public interface ClusteringPrefix<V> extends IMeasurableMemory, Clusterable<V>
this.asByteComparable = asByteComparable; this.asByteComparable = asByteComparable;
} }
public static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
/** /**
* Compares the 2 provided kind. * Compares the 2 provided kind.
* <p> * <p>
@ -476,7 +483,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, List<AbstractType<?>> types) throws IOException
{ {
Kind kind = Kind.values()[in.readByte()]; Kind kind = Kind.fromOrdinal(in.readByte());
// We shouldn't serialize static clusterings // We shouldn't serialize static clusterings
assert kind != Kind.STATIC_CLUSTERING; assert kind != Kind.STATIC_CLUSTERING;
if (kind == Kind.CLUSTERING) if (kind == Kind.CLUSTERING)
@ -487,7 +494,7 @@ public interface ClusteringPrefix<V> extends IMeasurableMemory, Clusterable<V>
public ClusteringPrefix<byte[]> deserialize(DataInputPlus in, int version, List<AbstractType<?>> types) throws IOException public ClusteringPrefix<byte[]> deserialize(DataInputPlus in, int version, List<AbstractType<?>> types) throws IOException
{ {
Kind kind = Kind.values()[in.readByte()]; Kind kind = Kind.fromOrdinal(in.readByte());
// We shouldn't serialize static clusterings // We shouldn't serialize static clusterings
assert kind != Kind.STATIC_CLUSTERING; assert kind != Kind.STATIC_CLUSTERING;
if (kind == Kind.CLUSTERING) if (kind == Kind.CLUSTERING)
@ -657,7 +664,7 @@ public interface ClusteringPrefix<V> extends IMeasurableMemory, Clusterable<V>
throw new IOException("Corrupt flags value for clustering prefix (isStatic flag set): " + flags); throw new IOException("Corrupt flags value for clustering prefix (isStatic flag set): " + flags);
this.nextIsRow = UnfilteredSerializer.kind(flags) == Unfiltered.Kind.ROW; this.nextIsRow = UnfilteredSerializer.kind(flags) == Unfiltered.Kind.ROW;
this.nextKind = nextIsRow ? Kind.CLUSTERING : ClusteringPrefix.Kind.values()[in.readByte()]; this.nextKind = nextIsRow ? Kind.CLUSTERING : Kind.fromOrdinal(in.readByte());
this.nextSize = nextIsRow ? comparator.size() : in.readUnsignedShort(); this.nextSize = nextIsRow ? comparator.size() : in.readUnsignedShort();
this.deserializedSize = 0; this.deserializedSize = 0;

View File

@ -198,6 +198,8 @@ public abstract class ReadCommand extends AbstractReadQuery
SINGLE_PARTITION (SinglePartitionReadCommand.selectionDeserializer, SinglePartitionReadCommand.accordSelectionDeserializer), SINGLE_PARTITION (SinglePartitionReadCommand.selectionDeserializer, SinglePartitionReadCommand.accordSelectionDeserializer),
PARTITION_RANGE (PartitionRangeReadCommand.selectionDeserializer, ignore -> PartitionRangeReadCommand.selectionDeserializer); PARTITION_RANGE (PartitionRangeReadCommand.selectionDeserializer, ignore -> PartitionRangeReadCommand.selectionDeserializer);
private static final Kind[] VALUES = values();
private final SelectionDeserializer selectionDeserializer; private final SelectionDeserializer selectionDeserializer;
private final Function<Seekable, SelectionDeserializer> accordSelectionDeserializer; private final Function<Seekable, SelectionDeserializer> accordSelectionDeserializer;
@ -206,6 +208,11 @@ public abstract class ReadCommand extends AbstractReadQuery
this.selectionDeserializer = selectionDeserializer; this.selectionDeserializer = selectionDeserializer;
this.accordSelectionDeserializer = accordSelectionDeserializer; this.accordSelectionDeserializer = accordSelectionDeserializer;
} }
public static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
} }
protected ReadCommand(Epoch serializedAtEpoch, protected ReadCommand(Epoch serializedAtEpoch,
@ -1450,7 +1457,7 @@ public abstract class ReadCommand extends AbstractReadQuery
public ReadCommand deserialize(DataInputPlus in, int version) throws IOException public ReadCommand deserialize(DataInputPlus in, int version) throws IOException
{ {
Kind kind = Kind.values()[in.readByte()]; Kind kind = Kind.fromOrdinal(in.readByte());
int flags = in.readByte(); int flags = in.readByte();
// Shouldn't happen or it's a user error (see comment above) but // Shouldn't happen or it's a user error (see comment above) but
// better complain loudly than doing the wrong thing. // better complain loudly than doing the wrong thing.
@ -1488,7 +1495,7 @@ public abstract class ReadCommand extends AbstractReadQuery
public ReadCommand deserializeForAccord(Seekable key, TableMetadatas tables, DataInputPlus in, int version) throws IOException public ReadCommand deserializeForAccord(Seekable key, TableMetadatas tables, DataInputPlus in, int version) throws IOException
{ {
Kind kind = Kind.values()[in.readByte()]; Kind kind = Kind.fromOrdinal(in.readByte());
int flags = in.readByte(); int flags = in.readByte();
if (isDigest(flags) || isForThrift(flags) || acceptsTransient(flags)) if (isDigest(flags) || isForThrift(flags) || acceptsTransient(flags))
throw new IllegalStateException("Received an Accord command with a digest/thrift/transient flag set."); throw new IllegalStateException("Received an Accord command with a digest/thrift/transient flag set.");

View File

@ -65,7 +65,14 @@ public abstract class AggregationSpecification
*/ */
public enum Kind public enum Kind
{ {
AGGREGATE_EVERYTHING, AGGREGATE_BY_PK_PREFIX, AGGREGATE_BY_PK_PREFIX_WITH_SELECTOR AGGREGATE_EVERYTHING, AGGREGATE_BY_PK_PREFIX, AGGREGATE_BY_PK_PREFIX_WITH_SELECTOR;
private static final Kind[] VALUES = values();
public static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
} }
/** /**
@ -253,7 +260,7 @@ public abstract class AggregationSpecification
public AggregationSpecification deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException public AggregationSpecification deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
{ {
Kind kind = Kind.values()[in.readUnsignedByte()]; Kind kind = Kind.fromOrdinal(in.readUnsignedByte());
switch (kind) switch (kind)
{ {
case AGGREGATE_EVERYTHING: case AGGREGATE_EVERYTHING:

View File

@ -80,7 +80,7 @@ public abstract class AbstractClusteringIndexFilter implements ClusteringIndexFi
public ClusteringIndexFilter deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException public ClusteringIndexFilter deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
{ {
Kind kind = Kind.values()[in.readUnsignedByte()]; Kind kind = Kind.fromOrdinal(in.readUnsignedByte());
boolean reversed = in.readBoolean(); boolean reversed = in.readBoolean();
return kind.deserializer.deserialize(in, version, metadata, reversed); return kind.deserializer.deserialize(in, version, metadata, reversed);

View File

@ -46,12 +46,19 @@ public interface ClusteringIndexFilter
SLICE (ClusteringIndexSliceFilter.deserializer), SLICE (ClusteringIndexSliceFilter.deserializer),
NAMES (ClusteringIndexNamesFilter.deserializer); NAMES (ClusteringIndexNamesFilter.deserializer);
private static final Kind[] VALUES = values();
protected final InternalDeserializer deserializer; protected final InternalDeserializer deserializer;
private Kind(InternalDeserializer deserializer) private Kind(InternalDeserializer deserializer)
{ {
this.deserializer = deserializer; this.deserializer = deserializer;
} }
public static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
} }
static interface InternalDeserializer static interface InternalDeserializer

View File

@ -44,7 +44,17 @@ public abstract class ColumnSubselection implements Comparable<ColumnSubselectio
public static final Serializer serializer = new Serializer(); public static final Serializer serializer = new Serializer();
/* this enum is used in serialization; preserve order for compatibility */ /* this enum is used in serialization; preserve order for compatibility */
private enum Kind { SLICE, ELEMENT } private enum Kind
{
SLICE, ELEMENT;
private static final Kind[] VALUES = values();
static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
}
protected final ColumnMetadata column; protected final ColumnMetadata column;
@ -229,7 +239,7 @@ public abstract class ColumnSubselection implements Comparable<ColumnSubselectio
} }
} }
Kind kind = Kind.values()[in.readUnsignedByte()]; Kind kind = Kind.fromOrdinal(in.readUnsignedByte());
switch (kind) switch (kind)
{ {
case SLICE: case SLICE:

View File

@ -102,7 +102,14 @@ public abstract class DataLimits
/** @deprecated See CASSANDRA-16582 */ /** @deprecated See CASSANDRA-16582 */
@Deprecated(since = "4.0") SUPER_COLUMN_COUNTING_LIMIT, //Deprecated and unused in 4.0, stop publishing in 5.0, reclaim in 6.0 @Deprecated(since = "4.0") SUPER_COLUMN_COUNTING_LIMIT, //Deprecated and unused in 4.0, stop publishing in 5.0, reclaim in 6.0
CQL_GROUP_BY_LIMIT, CQL_GROUP_BY_LIMIT,
CQL_GROUP_BY_PAGING_LIMIT, CQL_GROUP_BY_PAGING_LIMIT;
private static final Kind[] VALUES = values();
public static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
} }
public static DataLimits cqlLimits(int cqlRowLimit) public static DataLimits cqlLimits(int cqlRowLimit)
@ -1191,7 +1198,7 @@ public abstract class DataLimits
public DataLimits deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException public DataLimits deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
{ {
Kind kind = Kind.values()[in.readUnsignedByte()]; Kind kind = Kind.fromOrdinal(in.readUnsignedByte());
switch (kind) switch (kind)
{ {
case CQL_LIMIT: case CQL_LIMIT:

View File

@ -493,7 +493,17 @@ public class RowFilter implements Iterable<RowFilter.Expression>
// and this is why we have some UNUSEDX for values we don't use anymore // and this is why we have some UNUSEDX for values we don't use anymore
// (we could clean those on a major protocol update, but it's not worth // (we could clean those on a major protocol update, but it's not worth
// the trouble for now) // the trouble for now)
protected enum Kind { SIMPLE, MAP_ELEMENT, UNUSED1, CUSTOM, USER } protected enum Kind
{
SIMPLE, MAP_ELEMENT, UNUSED1, CUSTOM, USER;
private static final Kind[] VALUES = values();
static Kind fromOrdinal(int ordinal)
{
return VALUES[ordinal];
}
}
protected abstract Kind kind(); protected abstract Kind kind();
protected final ColumnMetadata column; protected final ColumnMetadata column;
@ -685,7 +695,7 @@ public class RowFilter implements Iterable<RowFilter.Expression>
public Expression deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException public Expression deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
{ {
Kind kind = Kind.values()[in.readByte()]; Kind kind = Kind.fromOrdinal(in.readByte());
// custom expressions (3.0+ only) do not contain a column or operator, only a value // custom expressions (3.0+ only) do not contain a column or operator, only a value
if (kind == Kind.CUSTOM) if (kind == Kind.CUSTOM)

View File

@ -61,7 +61,7 @@ public class ClusteringDescriptor extends ResizableByteBuffer
protected void loadClustering(RandomAccessReader dataReader, byte clusteringKind, int clusteringColumnsBound) throws IOException protected void loadClustering(RandomAccessReader dataReader, byte clusteringKind, int clusteringColumnsBound) throws IOException
{ {
set(ClusteringPrefix.Kind.values()[clusteringKind], clusteringKind, clusteringColumnsBound); set(ClusteringPrefix.Kind.fromOrdinal(clusteringKind), clusteringKind, clusteringColumnsBound);
if (clusteringKind != STATIC_CLUSTERING_KIND) if (clusteringKind != STATIC_CLUSTERING_KIND)
readUnfilteredClustering(dataReader, clusteringTypes, this.clusteringColumnsBound, this); readUnfilteredClustering(dataReader, clusteringTypes, this.clusteringColumnsBound, this);
else else
@ -103,7 +103,7 @@ public class ClusteringDescriptor extends ResizableByteBuffer
} }
private void set(byte clusteringKindEncoded, int clusteringColumnsBound) { private void set(byte clusteringKindEncoded, int clusteringColumnsBound) {
set(ClusteringPrefix.Kind.values()[clusteringKindEncoded], clusteringKindEncoded, clusteringColumnsBound); set(ClusteringPrefix.Kind.fromOrdinal(clusteringKindEncoded), clusteringKindEncoded, clusteringColumnsBound);
} }
private void set(ClusteringPrefix.Kind clusteringKind, byte clusteringKindEncoded, int clusteringColumnsBound) private void set(ClusteringPrefix.Kind clusteringKind, byte clusteringKindEncoded, int clusteringColumnsBound)

View File

@ -513,7 +513,7 @@ public class SSTableCursorWriter implements AutoCloseable
public void writeRangeTombstone(UnfilteredDescriptor rangeTombstone, boolean updateClusteringMetadata) throws IOException public void writeRangeTombstone(UnfilteredDescriptor rangeTombstone, boolean updateClusteringMetadata) throws IOException
{ {
int tombstoneKind = rangeTombstone.clusteringKindEncoded(); int tombstoneKind = rangeTombstone.clusteringKindEncoded();
ClusteringPrefix.Kind kind = ClusteringPrefix.Kind.values()[tombstoneKind]; ClusteringPrefix.Kind kind = ClusteringPrefix.Kind.fromOrdinal(tombstoneKind);
long unfilteredStartPosition = getPosition(); long unfilteredStartPosition = getPosition();
/** See: {@link org.apache.cassandra.db.rows.UnfilteredSerializer#serialize */ /** See: {@link org.apache.cassandra.db.rows.UnfilteredSerializer#serialize */
dataWriter.writeByte((byte)IS_MARKER); dataWriter.writeByte((byte)IS_MARKER);