From ade24b0db9ee9e7df35d0e6ac0dfdc2c518c92a1 Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Mon, 6 Apr 2026 21:39:56 +0100 Subject: [PATCH] Avoid capturing lambda allocation in UnfilteredSerializer.deserializeRowBody The same idea was applied previously in UnfilteredSerializer#serializeRowBody. A state is passed into a consumer function using the existing helper object. patch by Dmitry Konstantinov; reviewed by TBD for CASSANDRA-21289 --- src/java/org/apache/cassandra/db/Columns.java | 6 +++ .../db/rows/DeserializationHelper.java | 13 ++++++ .../db/rows/UnfilteredSerializer.java | 41 +++++++++++-------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/java/org/apache/cassandra/db/Columns.java b/src/java/org/apache/cassandra/db/Columns.java index f978b407171c..416b6645e4fd 100644 --- a/src/java/org/apache/cassandra/db/Columns.java +++ b/src/java/org/apache/cassandra/db/Columns.java @@ -24,6 +24,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.Objects; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -431,6 +432,11 @@ public void apply(Consumer function) BTree.apply(columns, function); } + public void apply(BiConsumer function, A argument) + { + BTree.apply(columns, function, argument); + } + @Override public boolean equals(Object other) { diff --git a/src/java/org/apache/cassandra/db/rows/DeserializationHelper.java b/src/java/org/apache/cassandra/db/rows/DeserializationHelper.java index d88e85c07d39..ab514ea98743 100644 --- a/src/java/org/apache/cassandra/db/rows/DeserializationHelper.java +++ b/src/java/org/apache/cassandra/db/rows/DeserializationHelper.java @@ -20,15 +20,20 @@ import java.nio.ByteBuffer; import java.util.Map; +import javax.annotation.concurrent.NotThreadSafe; + import org.apache.cassandra.db.DeletionTime; import org.apache.cassandra.db.LivenessInfo; +import org.apache.cassandra.db.SerializationHeader; import org.apache.cassandra.db.context.CounterContext; import org.apache.cassandra.db.filter.ColumnFilter; import org.apache.cassandra.db.marshal.ValueAccessor; +import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.DroppedColumn; import org.apache.cassandra.schema.TableMetadata; +@NotThreadSafe public class DeserializationHelper { /** @@ -57,6 +62,14 @@ public enum Flag private final Map droppedColumns; private DroppedColumn currentDroppedComplex; + // reusable fields to avoid extra allocation during cells processing + // within org.apache.cassandra.db.rows.UnfilteredSerializer.deserializeRowBody + DataInputPlus in; + SerializationHeader header; + Row.Builder builder; + LivenessInfo livenessInfo; + boolean hasComplexDeletion; + public DeserializationHelper(TableMetadata metadata, int version, Flag flag, ColumnFilter columnsToFetch) { diff --git a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java index 112c1e4c6bbd..587374de40a3 100644 --- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java +++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java @@ -626,20 +626,12 @@ public Row deserializeRowBody(DataInputPlus in, try { - DataInputPlus finalIn = in; - columns.apply(column -> { - try - { - if (column.isSimple()) - readSimpleColumn(column, finalIn, header, helper, builder, livenessInfo); - else - readComplexColumn(column, finalIn, header, helper, hasComplexDeletion, builder, livenessInfo); - } - catch (IOException e) - { - throw new WrappedException(e); - } - }); + helper.in = in; + helper.header = header; + helper.builder = builder; + helper.livenessInfo = livenessInfo; + helper.hasComplexDeletion = hasComplexDeletion; + columns.apply(UnfilteredSerializer::readColumn, helper); } catch (WrappedException e) { @@ -661,7 +653,22 @@ public Row deserializeRowBody(DataInputPlus in, } } - private void readSimpleColumn(ColumnMetadata column, DataInputPlus in, SerializationHeader header, DeserializationHelper helper, Row.Builder builder, LivenessInfo rowLiveness) + private static void readColumn(DeserializationHelper helper, ColumnMetadata column) + { + try + { + if (column.isSimple()) + readSimpleColumn(column, helper.in, helper.header, helper, helper.builder, helper.livenessInfo); + else + readComplexColumn(column, helper.in, helper.header, helper, helper.hasComplexDeletion, helper.builder, helper.livenessInfo); + } + catch (IOException e) + { + throw new WrappedException(e); + } + } + + private static void readSimpleColumn(ColumnMetadata column, DataInputPlus in, SerializationHeader header, DeserializationHelper helper, Row.Builder builder, LivenessInfo rowLiveness) throws IOException { if (helper.includes(column)) @@ -676,7 +683,7 @@ private void readSimpleColumn(ColumnMetadata column, DataInputPlus in, Serializa } } - private void readComplexColumn(ColumnMetadata column, DataInputPlus in, SerializationHeader header, DeserializationHelper helper, boolean hasComplexDeletion, Row.Builder builder, LivenessInfo rowLiveness) + private static void readComplexColumn(ColumnMetadata column, DataInputPlus in, SerializationHeader header, DeserializationHelper helper, boolean hasComplexDeletion, Row.Builder builder, LivenessInfo rowLiveness) throws IOException { if (helper.includes(column)) @@ -733,7 +740,7 @@ public void skipMarkerBody(DataInputPlus in) throws IOException in.skipBytesFully(markerSize); } - private void skipComplexColumn(DataInputPlus in, ColumnMetadata column, SerializationHeader header, boolean hasComplexDeletion) + private static void skipComplexColumn(DataInputPlus in, ColumnMetadata column, SerializationHeader header, boolean hasComplexDeletion) throws IOException { if (hasComplexDeletion)