netudima opened a new pull request, #4900:
URL: https://github.com/apache/cassandra/pull/4900

   Several paths report on-heap allocation to the memtable allocator through 
UpdateFunction.onAllocatedOnHeap in a way that diverges from the heap actually 
retained (BTree.sizeOnHeapOf), so the memtable's owned-heap counter drifts.
   
   1) BTree.update over-counts heap on node split/overflow, and never counts 
branch sizeMaps.
      Root cause: the Updater's running 'allocated' total is not a true net 
delta. Leaf drain()
      cleared the source node before the intended subtraction could read it (so 
the source was
      never released); the redistribute/overflow paths added freshly allocated 
nodes without
      releasing the source they replace; and branch sizeMaps were not counted 
at all. The
      reported value therefore diverged from sizeOnHeapOf, mostly over-counting.
      Fix: account each node net - add the shallow heap (backing array plus 
sizeMap) of every
      newly retained node and subtract it for every replaced source; perform 
the release before
      the source is cleared; record the root as the top builder's source so the 
old root is
      released too.
      Test: BTreeUpdateHeapAccountingTest asserts the cumulative 
onAllocatedOnHeap equals
      sizeOnHeapOf(result) across randomized inserts (small / contiguous-block 
/ overlapping /
      height-4 scenarios), with builder-path coverage verified by JaCoCo.
   
   2) BTreeRow.merge does not release a row's column tree when a row tombstone 
shadows its cells.
      Root cause: the retain branch rebuilds the existing column tree smaller 
via
      BTree.transformAndFilter, which runs with BTree node accounting disabled, 
but never
      released the freed node structure. This under-release inflates the 
memtable's owned heap.
      ColumnData.Reconciler.merge already accounted this; BTreeRow.merge did 
not.
      Fix: report onAllocatedOnHeap(sizeOnHeapOf(retained) - 
sizeOnHeapOf(existing)) when the
      filter shrinks the tree.
      Test: 
PartitionRowAccountingTest.rowTombstoneOverExistingRowDoesNotInflateOwnership.
   
   3) BTreeRow.merge does not account the row's LivenessInfo/Deletion change.
   Root cause: the merge accounts cells and column-tree nodes via the 
reconciler but not the
      row's own LivenessInfo/Deletion; when they change (e.g. a row tombstone 
replaces a live
      row) the new objects become memtable-owned and the old ones are released, 
unaccounted.
   Fix: account onAllocatedOnHeap of (reconciled liveness+deletion) - (existing 
liveness+deletion).
   Test: 
PartitionRowAccountingTest.rowTombstoneOverExistingRowDoesNotInflateOwnership.
   
   4) Allocation and release heap functions disagree on the branch sizeMap.
   Root cause: allocation accounting (BTreeRow/Columns unsharedHeapSize*) used
      sizeOfStructureOnHeap, which excluded the branch sizeMap, while the 
release paths used
      sizeOnHeapOf, which includes it, so shrinking a multi-node column tree 
over-released by
      the sizeMap.
   Fix: remove sizeOfStructureOnHeap and use sizeOnHeapOf everywhere, so 
allocation and
      release both include the sizeMap.
   Test: 
PartitionRowAccountingTest.rowTombstoneOverExistingRowDoesNotInflateOwnership, 
which
      requires two logically identical partitions reached via different merge 
paths to own
      exactly the same on-heap (only with all of 2-4 fixed does the on-heap 
ownership match).
   
   5) ColumnData.removeShadowed does not release a shadowed complex 
(collection) column's own structure.
    Root cause: when a tombstone shadows a whole complex column, removeShadowed 
filters its internal
      cell tree via BTree.transformAndFilter (node accounting disabled) and 
releases the inner cells through
      recordDeletion.delete, but never accounts the column's own cell-tree node 
structure - which can span
      multiple nodes - nor, when the column is dropped entirely, its wrapper. 
Both were counted as owned when
      the column was written (ComplexColumnData.unsharedHeapSizeExcludingData = 
EMPTY_SIZE + sizeOnHeapOf(tree)
      + cells), so this under-release inflates the memtable's owned heap. 
ColumnData.Reconciler.merge already accounted the tree delta on its retain 
path; removeShadowed did not. 
   Fix: report onAllocatedOnHeap((EMPTY_SIZE + sizeOnHeapOf(tree)) after - 
before), where 'after' is 0 when the column is dropped; on the update side 
(recordDeletion == noOp) this is a no-op, so it cannot under-count data the 
memtable never owned (CASSANDRA-21469). 
   Test: 
PartitionRowAccountingTest.rowTombstoneOverExistingCollectionDoesNotInflateOwnership
 - the collection analogue of (2)-(4): writing then deleting a row whose 
set<text> spans multiple column-tree nodes must leave the memtable owning 
exactly the same on-heap as never effectively writing it.
   
   SetCellAccountingTest is also added as a guard that a grow/reset op mix on a 
set<text> column never drives the memtable's owned heap negative.
   
   patch by Dmitry Konstantinov; reviewed by TBD for CASSANDRA-21472


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to