Merge branch 'cassandra-6.0' into trunk

* cassandra-6.0:
  Reduce cost to calculate BTreeRow.minDeletionTime
This commit is contained in:
Dmitry Konstantinov 2026-06-02 21:58:26 +01:00
commit e8cfd1acff
2 changed files with 27 additions and 2 deletions

View File

@ -3,6 +3,7 @@
* Allow nodetool garbagecollect to take a user defined list of SSTables (CASSANDRA-16767) * Allow nodetool garbagecollect to take a user defined list of SSTables (CASSANDRA-16767)
* Add a guardrail for misprepared statements (CASSANDRA-21139) * Add a guardrail for misprepared statements (CASSANDRA-21139)
Merged from 6.0: Merged from 6.0:
* Reduce cost to calculate BTreeRow.minDeletionTime (CASSANDRA-21414)
* Implement custom CassandraThread to keep direct references to frequently used thread local objects (CASSANDRA-21020) * Implement custom CassandraThread to keep direct references to frequently used thread local objects (CASSANDRA-21020)
* Avoid megamorphic call overhead at RandomAccessReader#current (CASSANDRA-21399) * Avoid megamorphic call overhead at RandomAccessReader#current (CASSANDRA-21399)
* Enable async GC logging for JDK versions which support it to avoid potential hiccups caused by GC log file I/O blocking (CASSANDRA-21372) * Enable async GC logging for JDK versions which support it to avoid potential hiccups caused by GC log file I/O blocking (CASSANDRA-21372)

View File

@ -859,6 +859,7 @@ public class BTreeRow extends AbstractRow
private final boolean isSorted; private final boolean isSorted;
private BTree.Builder<Cell<?>> cells_; private BTree.Builder<Cell<?>> cells_;
private boolean hasComplex = false; private boolean hasComplex = false;
private long minCellDeletionTime = Cell.MAX_DELETION_TIME;
// For complex column at index i of 'columns', we store at complexDeletions[i] its complex deletion. // For complex column at index i of 'columns', we store at complexDeletions[i] its complex deletion.
@ -888,6 +889,7 @@ public class BTreeRow extends AbstractRow
cells_ = builder.cells_ == null ? null : builder.cells_.copy(); cells_ = builder.cells_ == null ? null : builder.cells_.copy();
isSorted = builder.isSorted; isSorted = builder.isSorted;
hasComplex = builder.hasComplex; hasComplex = builder.hasComplex;
minCellDeletionTime = builder.minCellDeletionTime;
} }
@Override @Override
@ -919,6 +921,7 @@ public class BTreeRow extends AbstractRow
this.deletion = Deletion.LIVE; this.deletion = Deletion.LIVE;
this.cells_.reuse(); this.cells_.reuse();
this.hasComplex = false; this.hasComplex = false;
this.minCellDeletionTime = Cell.MAX_DELETION_TIME;
if (pool != null) if (pool != null)
{ {
pool.offer(this); pool.offer(this);
@ -951,12 +954,14 @@ public class BTreeRow extends AbstractRow
getCells().add(cell); getCells().add(cell);
hasComplex |= cell.column.isComplex(); hasComplex |= cell.column.isComplex();
minCellDeletionTime = Math.min(minCellDeletionTime, minDeletionTime(cell));
} }
public void addComplexDeletion(ColumnMetadata column, DeletionTime complexDeletion) public void addComplexDeletion(ColumnMetadata column, DeletionTime complexDeletion)
{ {
getCells().add(new ComplexColumnDeletion(column, complexDeletion)); getCells().add(new ComplexColumnDeletion(column, complexDeletion));
hasComplex = true; hasComplex = true;
minCellDeletionTime = Math.min(minCellDeletionTime, minDeletionTime(complexDeletion));
} }
public Row build() public Row build()
@ -965,14 +970,33 @@ public class BTreeRow extends AbstractRow
getCells().sort(); getCells().sort();
// we can avoid resolving if we're sorted and have no complex values // we can avoid resolving if we're sorted and have no complex values
// (because we'll only have unique simple cells, which are already in their final condition) // (because we'll only have unique simple cells, which are already in their final condition)
if (!isSorted | hasComplex) boolean resolved = !isSorted | hasComplex;
if (resolved)
getCells().resolve(CellResolver.instance); getCells().resolve(CellResolver.instance);
Object[] btree = getCells().build(); Object[] btree = getCells().build();
if (deletion.isShadowedBy(primaryKeyLivenessInfo)) if (deletion.isShadowedBy(primaryKeyLivenessInfo))
deletion = Deletion.LIVE; deletion = Deletion.LIVE;
long minDeletionTime = minDeletionTime(btree, primaryKeyLivenessInfo, deletion.time()); long minDeletionTime;
// Use the incrementally tracked min when it is guaranteed to be exact:
// - !resolved: CellResolver did not run, so no cells were merged or shadowed.
// - minCellDeletionTime == Cell.MAX_DELETION_TIME: no cell or complex deletion contributed
// any deletion info, so reconciliation in CellResolver cannot have made the tracked min
// pessimistic (every cell has localDeletionTime == NO_DELETION_TIME).
// Otherwise fall back to the exact O(N) computation, since reconciliation between expiring
// cells with equal timestamps may keep a cell whose localDeletionTime is larger than what
// we tracked.
if (!resolved || minCellDeletionTime == Cell.MAX_DELETION_TIME)
{
minDeletionTime = Math.min(minCellDeletionTime,
Math.min(minDeletionTime(primaryKeyLivenessInfo),
minDeletionTime(deletion.time())));
}
else
{
minDeletionTime = minDeletionTime(btree, primaryKeyLivenessInfo, deletion.time());
}
Row row = BTreeRow.create(clustering, primaryKeyLivenessInfo, deletion, btree, minDeletionTime); Row row = BTreeRow.create(clustering, primaryKeyLivenessInfo, deletion, btree, minDeletionTime);
reset(); reset();
return row; return row;