From 02d5ae650c9581ea061fb1255e2078a278697b6d Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Wed, 28 Jan 2026 12:34:20 +0000 Subject: [PATCH 1/7] force hash3_x64_128 inlining to help JIT with escape analysis and long[] heap allocation elimination --- src/java/org/apache/cassandra/dht/Murmur3Partitioner.java | 4 ++++ src/java/org/apache/cassandra/utils/MurmurHash.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/java/org/apache/cassandra/dht/Murmur3Partitioner.java b/src/java/org/apache/cassandra/dht/Murmur3Partitioner.java index 1919d530d2bc..361a3870464e 100644 --- a/src/java/org/apache/cassandra/dht/Murmur3Partitioner.java +++ b/src/java/org/apache/cassandra/dht/Murmur3Partitioner.java @@ -34,6 +34,8 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Longs; +import net.nicoulaj.compilecommand.annotations.Inline; + import accord.primitives.Ranges; import org.apache.cassandra.db.DecoratedKey; @@ -398,6 +400,7 @@ private static long flip(long value) return value ^ 0x8000000000000000L; } + @Inline // inline to help JIT with escape analysis and long[] heap allocation elimination private long[] getHash(ByteBuffer key) { long[] hash = new long[2]; @@ -405,6 +408,7 @@ private long[] getHash(ByteBuffer key) return hash; } + @Inline // inline to help JIT with escape analysis and long[] heap allocation elimination private void populateHash(ByteBuffer key, long[] hash) { MurmurHash.hash3_x64_128(key, key.position(), key.remaining(), 0, hash); diff --git a/src/java/org/apache/cassandra/utils/MurmurHash.java b/src/java/org/apache/cassandra/utils/MurmurHash.java index e5d488d6638b..79f38b9917de 100644 --- a/src/java/org/apache/cassandra/utils/MurmurHash.java +++ b/src/java/org/apache/cassandra/utils/MurmurHash.java @@ -22,6 +22,8 @@ import com.google.common.primitives.Longs; +import net.nicoulaj.compilecommand.annotations.Inline; + /** * This is a very fast, non-cryptographic hash suitable for general hash-based * lookup. See http://murmurhash.googlepages.com/ for more details. @@ -245,6 +247,10 @@ protected static long fmix(long k) return k; } + + // inline mostly to help JIT with escape analysis and long[] heap allocation elimination + // by default this method is not inlined as too big + @Inline public static void hash3_x64_128(ByteBuffer key, int offset, int length, long seed, long[] result) { final int nblocks = length >> 4; // Process as 128-bit blocks. From 34a3d7126351630eb91be1ba9546a6e3c84d9359 Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Wed, 28 Jan 2026 12:39:11 +0000 Subject: [PATCH 2/7] serializedRowBodySize: avoid capturing lamda allocation per cell by moving capturing arguments to SerializationHelper (same optimization as it was done in serializeRowBody) --- .../db/rows/SerializationHelper.java | 3 ++- .../db/rows/UnfilteredSerializer.java | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/java/org/apache/cassandra/db/rows/SerializationHelper.java b/src/java/org/apache/cassandra/db/rows/SerializationHelper.java index ff097111aa55..1ba8ebd4fcae 100644 --- a/src/java/org/apache/cassandra/db/rows/SerializationHelper.java +++ b/src/java/org/apache/cassandra/db/rows/SerializationHelper.java @@ -39,12 +39,13 @@ public class SerializationHelper private BTreeSearchIterator regulars = null; // reusable fields to avoid extra allocation during cells processing - // within org.apache.cassandra.db.rows.UnfilteredSerializer.serializeRowBody + // within org.apache.cassandra.db.rows.UnfilteredSerializer.serializeRowBody and serializedRowBodySize int flags; LivenessInfo pkLiveness; DataOutputPlus out; SearchIterator si; + boolean hasComplexDeletion; public SerializationHelper(SerializationHeader header) { diff --git a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java index 02e235bab9b9..112c1e4c6bbd 100644 --- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java +++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java @@ -369,18 +369,24 @@ private long serializedRowBodySize(Row row, SerializationHelper helper, long pre size += Columns.serializer.serializedSubsetSize(row.columns(), header.columns(isStatic)); SearchIterator si = helper.iterator(isStatic); - return row.accumulate((data, v) -> { - ColumnMetadata column = si.next(data.column()); - assert column != null; + helper.si = si; + helper.pkLiveness = pkLiveness; + helper.hasComplexDeletion = hasComplexDeletion; + return row.accumulate(UnfilteredSerializer::serializedColumnDataSize, helper, size); + } - if (data.column.isSimple()) - return v + Cell.serializer.serializedSize((Cell) data, column, pkLiveness, header); - else - return v + sizeOfComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header); - }, size); + private static long serializedColumnDataSize(SerializationHelper helper, ColumnData data, long v) + { + ColumnMetadata column = helper.si.next(data.column()); + assert column != null; + + if (data.column.isSimple()) + return v + Cell.serializer.serializedSize((Cell) data, column, helper.pkLiveness, helper.header); + else + return v + sizeOfComplexColumn((ComplexColumnData) data, column, helper.hasComplexDeletion, helper.pkLiveness, helper.header); } - private long sizeOfComplexColumn(ComplexColumnData data, ColumnMetadata column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header) + private static long sizeOfComplexColumn(ComplexColumnData data, ColumnMetadata column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header) { long size = 0; From f8f57ea14f0c40fabb0f049a79146f403c88a009 Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Wed, 28 Jan 2026 12:42:05 +0000 Subject: [PATCH 3/7] UpdateParameters: allocate DeletionTime on demand (it is not needed if we do insert/updates) --- .../apache/cassandra/cql3/UpdateParameters.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/java/org/apache/cassandra/cql3/UpdateParameters.java b/src/java/org/apache/cassandra/cql3/UpdateParameters.java index a0a51a382911..f7a5cf1e370d 100644 --- a/src/java/org/apache/cassandra/cql3/UpdateParameters.java +++ b/src/java/org/apache/cassandra/cql3/UpdateParameters.java @@ -58,7 +58,7 @@ public class UpdateParameters protected final long timestamp; private final int ttl; - private final DeletionTime deletionTime; + private DeletionTime deletionTime; // Holds data for operations that require a read-before-write. Will be null otherwise. private final Map prefetchedRows; @@ -82,8 +82,6 @@ public UpdateParameters(TableMetadata metadata, this.timestamp = timestamp; this.ttl = ttl; - this.deletionTime = DeletionTime.build(timestamp, nowInSec); - this.prefetchedRows = prefetchedRows; // We use MIN_VALUE internally to mean the absence of of timestamp (in Selection, in sstable stats, ...), so exclude @@ -128,7 +126,7 @@ private void addPrimaryKeyLivenessInfo(LivenessInfo info) public void addRowDeletion() { - addRowDeletion(Row.Deletion.regular(deletionTime)); + addRowDeletion(Row.Deletion.regular(deletionTime())); } private void addRowDeletion(Row.Deletion deletion) @@ -266,11 +264,12 @@ public void addCounter(ColumnMetadata column, long increment) throws InvalidRequ public void setComplexDeletionTime(ColumnMetadata column) { - builder.addComplexDeletion(column, deletionTime); + builder.addComplexDeletion(column, deletionTime()); } public void setComplexDeletionTimeForOverwrite(ColumnMetadata column) { + DeletionTime deletionTime = deletionTime(); builder.addComplexDeletion(column, DeletionTime.build(deletionTime.markedForDeleteAt() - 1, deletionTime.localDeletionTime())); } @@ -283,7 +282,9 @@ public Row buildRow() public DeletionTime deletionTime() { - return deletionTime; + if (deletionTime == null) + deletionTime = DeletionTime.build(timestamp, nowInSec); + return deletionTime; } public RangeTombstone makeRangeTombstone(ClusteringComparator comparator, Clustering clustering) @@ -293,7 +294,7 @@ public RangeTombstone makeRangeTombstone(ClusteringComparator comparator, Cluste public RangeTombstone makeRangeTombstone(Slice slice) { - return new RangeTombstone(slice, deletionTime); + return new RangeTombstone(slice, deletionTime()); } public byte[] nextTimeUUIDAsBytes() From e11961cf457a4545951cbfa0d20e2b929d5ae453 Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Wed, 28 Jan 2026 13:22:15 +0000 Subject: [PATCH 4/7] add fast path in valuesAsClustering logic, when we specify a single clustering key (a single row) to modify --- .../cql3/restrictions/ClusteringColumnRestrictions.java | 8 ++++++++ .../cassandra/cql3/restrictions/RestrictionSet.java | 7 ++++++- src/java/org/apache/cassandra/db/MultiCBuilder.java | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java index 93b93a7c8130..4d12be81589b 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java @@ -104,6 +104,14 @@ public ClusteringColumnRestrictions mergeWith(Restriction restriction, @Nullable public NavigableSet> valuesAsClustering(QueryOptions options, ClientState state) throws InvalidRequestException { + // fast path, a typical case when a single full restriction is used + // for example, when we specify a single clustering key (a single row) to insert/update + if (restrictions.size() == 1 && !restrictions.hasIN()) + { + SingleRestriction r = restrictions.lastRestriction(); + List values = r.values(options); + return MultiCBuilder.build(comparator, values); + } MultiCBuilder builder = new MultiCBuilder(comparator); for (SingleRestriction r : restrictions) { diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java index 9bd46f2afde3..d7b463b76202 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java @@ -70,6 +70,9 @@ public int compare(ColumnMetadata column, ColumnMetadata otherColumn) */ private final NavigableMap restrictions; + private final SingleRestriction lastRestriction; + + /** * {@code true} if it contains multi-column restrictions, {@code false} otherwise. */ @@ -100,6 +103,8 @@ private RestrictionSet(NavigableMap restricti boolean needsFilteringOrIndexing) { this.restrictions = restrictions; + // Map.lastEntry allocates an object, so we cache the value to avoid it, restrictions is immutable + this.lastRestriction = restrictions.isEmpty() ? null : restrictions.lastEntry().getValue(); this.hasMultiColumnRestrictions = hasMultiColumnRestrictions; this.hasIn = hasIn; this.hasSlice = hasSlice; @@ -312,7 +317,7 @@ ColumnMetadata nextColumn(ColumnMetadata columnDef) */ SingleRestriction lastRestriction() { - return restrictions.lastEntry().getValue(); + return lastRestriction; } /** diff --git a/src/java/org/apache/cassandra/db/MultiCBuilder.java b/src/java/org/apache/cassandra/db/MultiCBuilder.java index 6e9cf5cd5bb6..ef50d7953446 100644 --- a/src/java/org/apache/cassandra/db/MultiCBuilder.java +++ b/src/java/org/apache/cassandra/db/MultiCBuilder.java @@ -191,6 +191,11 @@ public NavigableSet> build() if (hasMissingElements) return BTreeSet.empty(comparator); + return build(comparator, clusterings); + } + + public static NavigableSet> build(ClusteringComparator comparator, List clusterings) + { if (clusterings.isEmpty()) return BTreeSet.of(comparator, Clustering.EMPTY); From b7fe9cc34c0a6c0c3d20b12fc2ccd8a11f98f460 Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Wed, 28 Jan 2026 13:33:25 +0000 Subject: [PATCH 5/7] add fast path in nonTokenRestrictionValues logic, when we specify a single partition key (a single row) to modify, optimize also the case if a partition or clustering key is a single column --- .../PartitionKeyRestrictions.java | 26 ++++++++++++++++++ .../cql3/restrictions/SimpleRestriction.java | 19 +++++++++++++ .../apache/cassandra/cql3/terms/Marker.java | 27 +++++++++++++++++++ .../apache/cassandra/cql3/terms/Terms.java | 24 +++++++++++++++++ 4 files changed, 96 insertions(+) diff --git a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java index c2b565196d9b..b1443e0a8a40 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java @@ -19,6 +19,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.SortedSet; @@ -30,6 +31,7 @@ import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.functions.Function; +import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.ClusteringComparator; import org.apache.cassandra.db.ClusteringPrefix; import org.apache.cassandra.db.MultiCBuilder; @@ -191,6 +193,17 @@ public AbstractBounds bounds(IPartitioner partitioner, QueryO */ private List nonTokenRestrictionValues(QueryOptions options, ClientState state) { + // fast path, a typical case when a single full restriction is used + // for example, when we specify a single partition key to modify + if (restrictions.size() == 1 && !restrictions.hasIN()) + { + SingleRestriction r = restrictions.lastRestriction(); + List values = r.values(options); + if (values.size() == 1) + return toByteBuffers(Clustering.make(values.get(0).toArray(new ByteBuffer[comparator.size()]))); + return toByteBuffers(MultiCBuilder.build(comparator, values)); + } + MultiCBuilder builder = new MultiCBuilder(comparator); for (SingleRestriction r : restrictions) { @@ -205,8 +218,21 @@ private List nonTokenRestrictionValues(QueryOptions options, ClientS return toByteBuffers(builder.build()); } + private List toByteBuffers(ClusteringPrefix clustering) + { + clustering.validate(); + return Collections.singletonList(clustering.serializeAsPartitionKey()); + } + private List toByteBuffers(SortedSet> clusterings) { + if (clusterings.size() == 1) + { + ClusteringPrefix clustering = clusterings.first(); + clustering.validate(); + return Collections.singletonList(clustering.serializeAsPartitionKey()); + } + List l = new ArrayList<>(clusterings.size()); for (ClusteringPrefix clustering : clusterings) { diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java index 16141b8b8c38..c5e05560a255 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java @@ -258,10 +258,22 @@ private List bindAndGetClusteringElements(QueryOptions optio private List bindAndGetSingleTermClusteringElements(QueryOptions options) { + if (values.isSingleTerm(options)) + { + ByteBuffer value = bindAndGetSingle(options); + return Collections.singletonList(ClusteringElements.of(columnsExpression.columnSpecification(), value, isOnToken())); + } + List values = bindAndGet(options); if (values.isEmpty()) return Collections.emptyList(); + if (values.size() == 1) + { + ClusteringElements value = ClusteringElements.of(columnsExpression.columnSpecification(), values.get(0), isOnToken()); + return Collections.singletonList(value); + } + List elements = new ArrayList<>(values.size()); for (int i = 0; i < values.size(); i++) elements.add(ClusteringElements.of(columnsExpression.columnSpecification(), values.get(i), isOnToken())); @@ -288,6 +300,13 @@ private List bindAndGet(QueryOptions options) return buffers; } + private ByteBuffer bindAndGetSingle(QueryOptions options) + { + ByteBuffer buffer = values.bindAndGetSingleTermValue(options); + validate(buffer); + return buffer; + } + private List> bindAndGetElements(QueryOptions options) { List> elementsList = values.bindAndGetElements(options); diff --git a/src/java/org/apache/cassandra/cql3/terms/Marker.java b/src/java/org/apache/cassandra/cql3/terms/Marker.java index 26ff84598700..d36e81bdfd5f 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Marker.java +++ b/src/java/org/apache/cassandra/cql3/terms/Marker.java @@ -95,6 +95,33 @@ public Term.Terminal bind(QueryOptions options) throws InvalidRequestException } } + // an optimized version without allocating interim Terminal objects + @Override + public ByteBuffer bindAndGet(QueryOptions options) + { + try + { + ByteBuffer bytes = options.getValue(bindIndex); + if (bytes == null) + return null; + + if (bytes == ByteBufferUtil.UNSET_BYTE_BUFFER) + return ByteBufferUtil.UNSET_BYTE_BUFFER; + + if (receiver.type instanceof MultiElementType) + { + return MultiElements.Value.fromSerialized(bytes, (MultiElementType) receiver.type).get(); + } + + receiver.type.validate(bytes); + return bytes; + } + catch (MarshalException e) + { + throw new InvalidRequestException(e.getMessage(), e); + } + } + public boolean isByteArrayGetSupported(QueryOptions options) { return options.isByteArrayValuesGetSupported(); diff --git a/src/java/org/apache/cassandra/cql3/terms/Terms.java b/src/java/org/apache/cassandra/cql3/terms/Terms.java index 3f411f901f61..10f0c5d137e2 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Terms.java +++ b/src/java/org/apache/cassandra/cql3/terms/Terms.java @@ -116,6 +116,17 @@ public String toString() */ List bindAndGet(QueryOptions options); + default boolean isSingleTerm(QueryOptions options) + { + return false; + } + + default ByteBuffer bindAndGetSingleTermValue(QueryOptions options) + { + throw new IllegalStateException("bindAndGetSingleTermValue() method is not implemented, " + + "isSingleTerm() must be always checked before invoking this method"); + } + /** * A shorter for {@code bind(options).getElements()}. * We expose it mainly because for constants it can avoid allocating a temporary @@ -627,6 +638,19 @@ public List bindAndGet(QueryOptions options) return Collections.singletonList(term.bindAndGet(options)); } + @Override + public boolean isSingleTerm(QueryOptions options) + { + return true; + } + + @Override + public ByteBuffer bindAndGetSingleTermValue(QueryOptions options) + { + return term.bindAndGet(options); + } + + @Override public List> bindAndGetElements(QueryOptions options) { From d011dfa68b88fa2d52c9a661d4945c719febf1d5 Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Wed, 28 Jan 2026 13:34:43 +0000 Subject: [PATCH 6/7] BatchStatement: check if many similar rows for the same table are written unconditionally, in this case we can avoid columns info merging and builders allocation --- .../cql3/statements/BatchStatement.java | 55 +++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java index 39d36aa4a52b..8a38387d8882 100644 --- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java @@ -142,27 +142,62 @@ public BatchStatement(Type type, VariableSpecifications bindVariables, List Date: Wed, 28 Jan 2026 14:05:14 +0000 Subject: [PATCH 7/7] avoid ClusteringIndexSliceFilter allocation if a write does not required a read (plain usual write), avoid iterator allocation, use array instead of ArrayList for perStatementOptions which does not grow dynamically --- .../cassandra/cql3/BatchQueryOptions.java | 13 +++++---- .../statements/ModificationStatement.java | 29 ++++++++++++------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java b/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java index 13143453bb31..ef767aa396c7 100644 --- a/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java +++ b/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java @@ -152,19 +152,20 @@ public QueryOptions forStatement(int i) private static class WithPerStatementVariables extends BatchQueryOptions { - private final List perStatementOptions; + private final QueryOptions[] perStatementOptions; private WithPerStatementVariables(QueryOptions wrapped, List variables, List queryOrIdList) { super(wrapped, queryOrIdList); - this.perStatementOptions = new ArrayList<>(variables.size()); + this.perStatementOptions = new QueryOptions[variables.size()]; + int i = 0; for (final byte[][] vars : variables) - perStatementOptions.add(new BatchQueryOptionsWrapper(wrapped, vars)); + perStatementOptions[i++] = new BatchQueryOptionsWrapper(wrapped, vars); } public QueryOptions forStatement(int i) { - return perStatementOptions.get(i); + return perStatementOptions[i]; } @Override @@ -172,10 +173,10 @@ public void prepareStatement(int i, ImmutableList boundName { if (isPreparedStatement(i)) { - QueryOptions options = perStatementOptions.get(i); + QueryOptions options = perStatementOptions[i]; options.prepare(boundNames); options = QueryOptions.addColumnSpecifications(options, boundNames); - perStatementOptions.set(i, options); + perStatementOptions[i] = options; } else { diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index 118f2c1fa44f..fd853004be71 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -521,8 +521,8 @@ public List buildPartitionKeyNames(QueryOptions options, ClientState throws InvalidRequestException { List partitionKeys = restrictions.getPartitionKeys(options, state); - for (ByteBuffer key : partitionKeys) - QueryProcessor.validateKey(key); + for (int i = 0; i < partitionKeys.size(); i++) + QueryProcessor.validateKey(partitionKeys.get(i)); return partitionKeys; } @@ -567,8 +567,9 @@ public boolean requiresRead() return isReadRequired; } - private Map readRequiredLists(Collection partitionKeys, - ClusteringIndexFilter filter, + private Map readRequiredLists(Collection partitionKeys, + java.util.function.Function filterBuilder, + F filterArg, DataLimits limits, boolean local, ConsistencyLevel cl, @@ -595,7 +596,7 @@ private Map readRequiredLists(Collection pa RowFilter.none(), limits, metadata().partitioner.decorateKey(key), - filter)); + filterBuilder.apply(filterArg))); SinglePartitionReadCommand.Group group = SinglePartitionReadCommand.Group.create(commands, DataLimits.NONE); @@ -1024,7 +1025,8 @@ final void addUpdates(UpdatesCollector collector, return; UpdateParameters params = makeUpdateParameters(keys, - new ClusteringIndexSliceFilter(slices, false), + (slicesToFilter) -> new ClusteringIndexSliceFilter(slicesToFilter, false), + slices, state, options, DataLimits.NONE, @@ -1117,7 +1119,8 @@ private UpdateParameters makeUpdateParameters(Collection keys, { if (clusterings.contains(Clustering.STATIC_CLUSTERING)) return makeUpdateParameters(keys, - new ClusteringIndexSliceFilter(Slices.ALL, false), + (clusteringsToFilter) -> new ClusteringIndexSliceFilter(Slices.ALL, false), + clusterings, state, options, DataLimits.cqlLimits(1), @@ -1128,7 +1131,8 @@ private UpdateParameters makeUpdateParameters(Collection keys, ); return makeUpdateParameters(keys, - new ClusteringIndexNamesFilter(clusterings, false), + (clusteringsToFilter) -> new ClusteringIndexNamesFilter(clusteringsToFilter, false), + clusterings, state, options, DataLimits.NONE, @@ -1139,8 +1143,10 @@ private UpdateParameters makeUpdateParameters(Collection keys, ); } - private UpdateParameters makeUpdateParameters(Collection keys, - ClusteringIndexFilter filter, + private UpdateParameters makeUpdateParameters(Collection keys, + // filter is needed rarely, so we allocate it on demand + java.util.function.Function filterBuilder, + F filterArg, ClientState state, QueryOptions options, DataLimits limits, @@ -1152,7 +1158,8 @@ private UpdateParameters makeUpdateParameters(Collection keys, // Some lists operation requires reading Map lists = readRequiredLists(keys, - filter, + filterBuilder, + filterArg, limits, local, options.getConsistency(),