From 7f7eaa4c7a70755341d1e8f2a52d1d11f8b29523 Mon Sep 17 00:00:00 2001 From: Dmitry Konstantinov Date: Thu, 16 Jul 2026 23:17:45 +0100 Subject: [PATCH] avoid megamorphic calls for Cell.timestamp/ttl/path/localDeletionTimeAsUnsignedInt methods --- .../cassandra/db/rows/AbstractCell.java | 7 ++ .../apache/cassandra/db/rows/ArrayCell.java | 32 +--------- .../apache/cassandra/db/rows/BTreeRow.java | 2 +- .../apache/cassandra/db/rows/BufferCell.java | 33 +--------- .../org/apache/cassandra/db/rows/Cell.java | 2 + .../cassandra/db/rows/HeapAbstractCell.java | 64 +++++++++++++++++++ .../index/sai/utils/CellWithSource.java | 6 ++ 7 files changed, 84 insertions(+), 62 deletions(-) create mode 100644 src/java/org/apache/cassandra/db/rows/HeapAbstractCell.java diff --git a/src/java/org/apache/cassandra/db/rows/AbstractCell.java b/src/java/org/apache/cassandra/db/rows/AbstractCell.java index c3988397421e..109590c977af 100644 --- a/src/java/org/apache/cassandra/db/rows/AbstractCell.java +++ b/src/java/org/apache/cassandra/db/rows/AbstractCell.java @@ -64,6 +64,13 @@ public boolean isTombstone() return localDeletionTime() != NO_DELETION_TIME && ttl() == NO_TTL; } + public long minDeletionTime() + { + long localDeletionTime = localDeletionTime(); + boolean isTombstone = localDeletionTime != NO_DELETION_TIME && ttl() == NO_TTL; + return isTombstone ? Long.MIN_VALUE : localDeletionTime; + } + public boolean isExpiring() { return ttl() != NO_TTL; diff --git a/src/java/org/apache/cassandra/db/rows/ArrayCell.java b/src/java/org/apache/cassandra/db/rows/ArrayCell.java index 77d44c3eb7fc..c74103f2e0c6 100644 --- a/src/java/org/apache/cassandra/db/rows/ArrayCell.java +++ b/src/java/org/apache/cassandra/db/rows/ArrayCell.java @@ -31,17 +31,12 @@ import static org.apache.cassandra.utils.ByteArrayUtil.EMPTY_BYTE_ARRAY; -public class ArrayCell extends AbstractCell +public class ArrayCell extends HeapAbstractCell { private static final long EMPTY_SIZE = ObjectSizes.measure(new ArrayCell(ColumnMetadata.regularColumn("", "", "", ByteType.instance, ColumnMetadata.NO_UNIQUE_ID), 0L, 0, 0, EMPTY_BYTE_ARRAY, null)); // Careful: Adding vars here has an impact on memtable size - private final long timestamp; - private final int ttl; - private final int localDeletionTimeUnsignedInteger; - private final byte[] value; - private final CellPath path; // Please keep both int/long overloaded ctros public. Otherwise silent casts will mess timestamps when one is not // available. @@ -52,12 +47,8 @@ public ArrayCell(ColumnMetadata column, long timestamp, int ttl, long localDelet public ArrayCell(ColumnMetadata column, long timestamp, int ttl, int localDeletionTimeUnsignedInteger, byte[] value, CellPath path) { - super(column); - this.timestamp = timestamp; - this.ttl = ttl; - this.localDeletionTimeUnsignedInteger = localDeletionTimeUnsignedInteger; + super(column, timestamp, ttl, localDeletionTimeUnsignedInteger, path); this.value = value; - this.path = path; } public static ArrayCell live(ColumnMetadata column, long timestamp, byte[] value, CellPath path) @@ -71,16 +62,6 @@ public static ArrayCell expiring(ColumnMetadata column, long timestamp, int ttl, return new ArrayCell(column, timestamp, ttl, ExpirationDateOverflowHandling.computeLocalExpirationTime(nowInSec, ttl), value, path); } - public long timestamp() - { - return timestamp; - } - - public int ttl() - { - return ttl; - } - public byte[] value() { return value; @@ -91,10 +72,6 @@ public ValueAccessor accessor() return ByteArrayAccessor.instance; } - public CellPath path() - { - return path; - } public Cell withUpdatedColumn(ColumnMetadata newColumn) { @@ -144,9 +121,4 @@ public long unsharedHeapSizeExcludingData() return EMPTY_SIZE + ObjectSizes.sizeOfArray(value) - value.length + (path == null ? 0 : path.unsharedHeapSizeExcludingData()); } - @Override - protected int localDeletionTimeAsUnsignedInt() - { - return localDeletionTimeUnsignedInteger; - } } diff --git a/src/java/org/apache/cassandra/db/rows/BTreeRow.java b/src/java/org/apache/cassandra/db/rows/BTreeRow.java index b8df8450c136..cdc2e973c343 100644 --- a/src/java/org/apache/cassandra/db/rows/BTreeRow.java +++ b/src/java/org/apache/cassandra/db/rows/BTreeRow.java @@ -172,7 +172,7 @@ public static BTreeRow noCellLiveRow(Clustering clustering, LivenessInfo prim private static long minDeletionTime(Cell cell) { - return cell.isTombstone() ? Long.MIN_VALUE : cell.localDeletionTime(); + return cell.minDeletionTime(); } private static long minDeletionTime(LivenessInfo info) diff --git a/src/java/org/apache/cassandra/db/rows/BufferCell.java b/src/java/org/apache/cassandra/db/rows/BufferCell.java index fa0826fa577f..3f8de1d5fe08 100644 --- a/src/java/org/apache/cassandra/db/rows/BufferCell.java +++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java @@ -30,17 +30,12 @@ import static java.lang.String.format; -public class BufferCell extends AbstractCell +public class BufferCell extends HeapAbstractCell { private static final long EMPTY_SIZE = ObjectSizes.measure(new BufferCell(ColumnMetadata.regularColumn("", "", "", ByteType.instance, ColumnMetadata.NO_UNIQUE_ID), 0L, 0, 0, ByteBufferUtil.EMPTY_BYTE_BUFFER, null)); // Careful: Adding vars here has an impact on memtable size - private final long timestamp; - private final int ttl; - private final int localDeletionTimeUnsignedInteger; - private final ByteBuffer value; - private final CellPath path; // Please keep both int/long overloaded ctros public. Otherwise silent casts will mess timestamps when one is not // available. @@ -51,14 +46,10 @@ public BufferCell(ColumnMetadata column, long timestamp, int ttl, long localDele public BufferCell(ColumnMetadata column, long timestamp, int ttl, int localDeletionTimeUnsignedInteger, ByteBuffer value, CellPath path) { - super(column); + super(column, timestamp, ttl, localDeletionTimeUnsignedInteger, path); assert !column.isPrimaryKeyColumn(); assert column.isComplex() == (path != null) : format("Column %s.%s(%s: %s) isComplex: %b with cellpath: %s", column.ksName, column.cfName, column.name, column.type.toString(), column.isComplex(), path); - this.timestamp = timestamp; - this.ttl = ttl; - this.localDeletionTimeUnsignedInteger = localDeletionTimeUnsignedInteger; this.value = value; - this.path = path; } public static BufferCell live(ColumnMetadata column, long timestamp, ByteBuffer value) @@ -92,16 +83,6 @@ public static BufferCell tombstone(ColumnMetadata column, long timestamp, long n return new BufferCell(column, timestamp, NO_TTL, nowInSec, ByteBufferUtil.EMPTY_BYTE_BUFFER, path); } - public long timestamp() - { - return timestamp; - } - - public int ttl() - { - return ttl; - } - public ByteBuffer value() { return value; @@ -112,10 +93,6 @@ public ValueAccessor accessor() return ByteBufferAccessor.instance; } - public CellPath path() - { - return path; - } public Cell withUpdatedColumn(ColumnMetadata newColumn) { @@ -163,10 +140,4 @@ public long unsharedHeapSizeExcludingData() { return EMPTY_SIZE + ObjectSizes.sizeOnHeapExcludingDataOf(value) + (path == null ? 0 : path.unsharedHeapSizeExcludingData()); } - - @Override - protected int localDeletionTimeAsUnsignedInt() - { - return localDeletionTimeUnsignedInteger; - } } diff --git a/src/java/org/apache/cassandra/db/rows/Cell.java b/src/java/org/apache/cassandra/db/rows/Cell.java index 6dea39699338..5a76d1bcd456 100644 --- a/src/java/org/apache/cassandra/db/rows/Cell.java +++ b/src/java/org/apache/cassandra/db/rows/Cell.java @@ -150,6 +150,8 @@ public long localDeletionTime() return deletionTimeUnsignedIntegerToLong(localDeletionTimeAsUnsignedInt()); } + public abstract long minDeletionTime(); + /** * Whether the cell is a tombstone or not. * diff --git a/src/java/org/apache/cassandra/db/rows/HeapAbstractCell.java b/src/java/org/apache/cassandra/db/rows/HeapAbstractCell.java new file mode 100644 index 000000000000..d44186b2e93a --- /dev/null +++ b/src/java/org/apache/cassandra/db/rows/HeapAbstractCell.java @@ -0,0 +1,64 @@ +/* + * 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.db.rows; + +import org.apache.cassandra.schema.ColumnMetadata; + +public abstract class HeapAbstractCell extends AbstractCell +{ + // Careful: Adding vars here has an impact on memtable size + protected final long timestamp; + protected final int ttl; + protected final int localDeletionTimeUnsignedInteger; + + protected final CellPath path; + + protected HeapAbstractCell(ColumnMetadata column, long timestamp, int ttl, int localDeletionTimeUnsignedInteger, CellPath path) + { + super(column); + this.timestamp = timestamp; + this.ttl = ttl; + this.localDeletionTimeUnsignedInteger = localDeletionTimeUnsignedInteger; + this.path = path; + } + + @Override + public long timestamp() + { + return timestamp; + } + + @Override + public int ttl() + { + return ttl; + } + + @Override + protected int localDeletionTimeAsUnsignedInt() + { + return localDeletionTimeUnsignedInteger; + } + + @Override + public CellPath path() + { + return path; + } +} diff --git a/src/java/org/apache/cassandra/index/sai/utils/CellWithSource.java b/src/java/org/apache/cassandra/index/sai/utils/CellWithSource.java index ce697f361a14..b0ee8284b49c 100644 --- a/src/java/org/apache/cassandra/index/sai/utils/CellWithSource.java +++ b/src/java/org/apache/cassandra/index/sai/utils/CellWithSource.java @@ -103,6 +103,12 @@ public long localDeletionTime() return cell.localDeletionTime(); } + @Override + public long minDeletionTime() + { + return cell.minDeletionTime(); + } + @Override public boolean isTombstone() {