This is an automated email from the ASF dual-hosted git repository. michaelsembwever pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit aa971bc008d00f807e6b79b6183959d383778725 Merge: d2fa1a85e9 18e1ab3701 Author: mck <[email protected]> AuthorDate: Sun Sep 13 13:04:38 2026 +0200 Merge branch 'cassandra-6.0' into trunk * cassandra-6.0: Separate memtable memory usage tracking from limit checking CHANGES.txt | 1 + NEWS.txt | 3 + conf/cassandra.yaml | 3 + .../cassandra/config/DatabaseDescriptor.java | 43 ++++-- .../apache/cassandra/db/CassandraWriteContext.java | 31 +++++ .../org/apache/cassandra/db/ColumnFamilyStore.java | 18 ++- .../db/memtable/AbstractAllocatorMemtable.java | 27 +++- .../org/apache/cassandra/db/memtable/Memtable.java | 52 +++++-- .../db/memtable/ShardedSkipListMemtable.java | 4 +- .../cassandra/db/memtable/SkipListMemtable.java | 2 +- .../apache/cassandra/db/memtable/TrieMemtable.java | 4 +- .../db/partitions/BTreePartitionUpdater.java | 2 +- .../cassandra/index/accord/RouteJournalIndex.java | 3 +- .../cassandra/index/sai/StorageAttachedIndex.java | 18 +-- .../org/apache/cassandra/index/sasi/SASIIndex.java | 8 +- .../cassandra/service/accord/AccordKeyspace.java | 2 +- .../apache/cassandra/utils/memory/HeapPool.java | 8 +- .../cassandra/utils/memory/MemtableAllocator.java | 72 +++++----- .../utils/memory/MemtableBufferAllocator.java | 4 +- .../cassandra/utils/memory/MemtablePool.java | 36 ++--- .../cassandra/utils/memory/NativeAllocator.java | 4 +- .../cassandra/utils/memory/SlabAllocator.java | 2 +- .../cassandra/config/DatabaseDescriptorTest.java | 45 ++++++ .../apache/cassandra/db/ColumnFamilyStoreTest.java | 4 +- .../cassandra/db/NestedMemtableWriteTest.java | 141 +++++++++++++++++++ .../memory/MemtableAllocatorAwaitRoomTest.java | 120 ++++++++++++++++ .../utils/memory/NativeAllocatorTest.java | 17 ++- .../memory/TrieMemtableShardLockDeadlockTest.java | 153 +++++++++++++++++++++ 28 files changed, 700 insertions(+), 127 deletions(-) diff --cc NEWS.txt index 0032736c90,e506c9dbb1..20c2422b44 --- a/NEWS.txt +++ b/NEWS.txt @@@ -74,36 -74,6 +74,39 @@@ restore snapshots created with the prev 'sstableloader' tool. You can upgrade the file format of your snapshots using the provided 'sstableupgrade' tool. +7.0 +=== + +New features +------------ + + - CEP-49 - it is possible to implement custom compression providers, e.g. + integrating with a specialized hardware which makes de/compression faster. + See CASSANDRA-20975 for more information. + - It is now possible to bootstrap a cluster's initial superuser role without + creating the 'cassandra/cassandra' password, by configuring 'default_role_initializer' + in cassandra.yaml. The new MutualTlsDefaultRoleInitializer creates the role + passwordless and maps a client certificate identity onto it instead, for + deployments that authenticate over mutual TLS. PasswordDefaultRoleInitializer + remains the default and produces the historical behaviour. See CASSANDRA-21546 + for more information. + +Upgrading +--------- ++ - memtable_offheap_space: 0 is now rejected at startup when memtable_allocation_type is ++ offheap_buffers or offheap_objects. Set a positive memtable_offheap_space, or an allocation ++ type that does not allocate off-heap. See CASSANDRA-21019. + + - trickle_fsync is by default set to "true" instead of "false" in cassandra.yaml (see CASSANDRA-21572) + - Authorizing a batch or an Accord transaction now performs the table permission checks once per run of + consecutive statements on the same table rather than once per statement. The permissions required are + unchanged, and the datacenter and CIDR checks that ride along with them still run at least once per + request. Auth cache request/hit/miss counts and the system_views.cidr_filtering_metrics_* counts and + latencies will therefore report fewer events than before. (see CASSANDRA-21606) + +Deprecation +----------- + 6.0 === diff --cc src/java/org/apache/cassandra/db/memtable/AbstractAllocatorMemtable.java index 8e296ac604,c74096e601..7cb6f5277b --- a/src/java/org/apache/cassandra/db/memtable/AbstractAllocatorMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/AbstractAllocatorMemtable.java @@@ -193,14 -212,14 +212,14 @@@ public abstract class AbstractAllocator stats.ownsOffHeap += getAllocator().offHeap().owns(); } -- public void markExtraOnHeapUsed(long additionalSpace, OpOrder.Group opGroup) ++ public void markExtraOnHeapUsed(long additionalSpace) { -- getAllocator().onHeap().allocate(additionalSpace, opGroup); ++ getAllocator().onHeap().allocate(additionalSpace); } -- public void markExtraOffHeapUsed(long additionalSpace, OpOrder.Group opGroup) ++ public void markExtraOffHeapUsed(long additionalSpace) { -- getAllocator().offHeap().allocate(additionalSpace, opGroup); ++ getAllocator().offHeap().allocate(additionalSpace); } void scheduleFlush() diff --cc src/java/org/apache/cassandra/db/memtable/Memtable.java index e11fba165b,335648b841..76a43d0185 --- a/src/java/org/apache/cassandra/db/memtable/Memtable.java +++ b/src/java/org/apache/cassandra/db/memtable/Memtable.java @@@ -295,23 -325,29 +325,27 @@@ public interface Memtable extends Compa /** * Adjust the used on-heap space by the given size (e.g. to reflect memory used by a non-table-based index). - * This operation may block until enough memory is available in the memory pool. + * This records the usage and returns; it does not wait for room, and so is safe to call from an indexer + * running under a memtable-internal lock, which is how SAI, SASI and the Accord route index all reach it. + * The recorded total drives cleaning; the memory limit is only enforced once per write, in - * {@link #checkSpaceAndPut} (CASSANDRA-21019). ++ * {@link #checkSpaceAndPut} (CASSANDRA-21019). It takes no OpOrder.Group for that reason: an ++ * implementation has nothing to wait on, so it cannot park where markBlocking() could not release it. * * @param additionalSpace the number of allocated bytes - * @param opGroup write operation group, used to permit the operation to complete if it is needed to complete a - * flush to free space. - * @param opGroup write operation group. Unused since CASSANDRA-21019, as this call no longer waits; kept for - * compatibility with memtable implementations built against this release series. */ -- void markExtraOnHeapUsed(long additionalSpace, OpOrder.Group opGroup); ++ void markExtraOnHeapUsed(long additionalSpace); /** * Adjust the used off-heap space by the given size (e.g. to reflect memory used by a non-table-based index). - * This operation may block until enough memory is available in the memory pool. + * This records the usage and returns; it does not wait for room, and so is safe to call from an indexer + * running under a memtable-internal lock, which is how SAI, SASI and the Accord route index all reach it. + * The recorded total drives cleaning; the memory limit is only enforced once per write, in - * {@link #checkSpaceAndPut} (CASSANDRA-21019). ++ * {@link #checkSpaceAndPut} (CASSANDRA-21019). It takes no OpOrder.Group for that reason: an ++ * implementation has nothing to wait on, so it cannot park where markBlocking() could not release it. * * @param additionalSpace the number of allocated bytes - * @param opGroup write operation group, used to permit the operation to complete if it is needed to complete a - * flush to free space. - * @param opGroup write operation group. Unused since CASSANDRA-21019, as this call no longer waits; kept for - * compatibility with memtable implementations built against this release series. */ -- void markExtraOffHeapUsed(long additionalSpace, OpOrder.Group opGroup); ++ void markExtraOffHeapUsed(long additionalSpace); // Flushing diff --cc src/java/org/apache/cassandra/db/memtable/ShardedSkipListMemtable.java index fefb8eb266,68a7d1be5e..f495730dd4 --- a/src/java/org/apache/cassandra/db/memtable/ShardedSkipListMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/ShardedSkipListMemtable.java @@@ -394,7 -395,7 +395,7 @@@ public class ShardedSkipListMemtable ex // allocate the row overhead after the fact; this saves over allocating and having to free after, but // means we can overshoot our declared limit. int overhead = (int) (cloneKey.getToken().getHeapSize() + SkipListMemtable.ROW_OVERHEAD_HEAP_SIZE); -- allocator.onHeap().allocate(overhead, opGroup); ++ allocator.onHeap().allocate(overhead); initialSize = 8; } } diff --cc src/java/org/apache/cassandra/db/memtable/SkipListMemtable.java index 1389b1954b,1389b1954b..81aab3880b --- a/src/java/org/apache/cassandra/db/memtable/SkipListMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/SkipListMemtable.java @@@ -132,7 -132,7 +132,7 @@@ public class SkipListMemtable extends A // allocate the row overhead after the fact; this saves over allocating and having to free after, but // means we can overshoot our declared limit. int overhead = (int) (cloneKey.getToken().getHeapSize() + ROW_OVERHEAD_HEAP_SIZE); -- allocator.onHeap().allocate(overhead, opGroup); ++ allocator.onHeap().allocate(overhead); initialSize = 8; } } diff --cc src/java/org/apache/cassandra/db/memtable/TrieMemtable.java index 3eb55743fe,3eb55743fe..0522f537f6 --- a/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java @@@ -580,8 -580,8 +580,8 @@@ public class TrieMemtable extends Abstr update, updater::mergePartitions, key.getKeyLength() < MAX_RECURSIVE_KEY_LENGTH); -- allocator.offHeap().adjust(data.sizeOffHeap() - offHeap, opGroup); -- allocator.onHeap().adjust(data.sizeOnHeap() - onHeap, opGroup); ++ allocator.offHeap().adjust(data.sizeOffHeap() - offHeap); ++ allocator.onHeap().adjust(data.sizeOnHeap() - onHeap); } finally { diff --cc src/java/org/apache/cassandra/db/partitions/BTreePartitionUpdater.java index 9a4d6acaaa,9a4d6acaaa..f55f379b63 --- a/src/java/org/apache/cassandra/db/partitions/BTreePartitionUpdater.java +++ b/src/java/org/apache/cassandra/db/partitions/BTreePartitionUpdater.java @@@ -214,7 -214,7 +214,7 @@@ public class BTreePartitionUpdater impl public void reportAllocatedMemory() { -- allocator.onHeap().adjust(heapSize, writeOp); ++ allocator.onHeap().adjust(heapSize); if (contextCloner != null && contextCloner != cloner) contextCloner.adjustUnused(); } diff --cc src/java/org/apache/cassandra/index/accord/RouteJournalIndex.java index 2eb346ffef,2eb346ffef..5507132b56 --- a/src/java/org/apache/cassandra/index/accord/RouteJournalIndex.java +++ b/src/java/org/apache/cassandra/index/accord/RouteJournalIndex.java @@@ -44,7 -44,7 +44,6 @@@ import accord.primitives.TxnId import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.Operator; --import org.apache.cassandra.db.CassandraWriteContext; import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.DecoratedKey; @@@ -357,7 -357,7 +356,7 @@@ public class RouteJournalIndex implemen { long size = memtableIndexManager.index(key, row, memtable); if (size > 0) -- memtable.markExtraOnHeapUsed(size, CassandraWriteContext.fromContext(ctx).getGroup()); ++ memtable.markExtraOnHeapUsed(size); } @Override diff --cc src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java index 881ca81911,881ca81911..99c008b22f --- a/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java +++ b/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java @@@ -55,7 -55,7 +55,6 @@@ import org.apache.cassandra.cql3.restri import org.apache.cassandra.cql3.restrictions.Restriction; import org.apache.cassandra.cql3.restrictions.SimpleRestriction; import org.apache.cassandra.cql3.statements.schema.IndexTarget; --import org.apache.cassandra.db.CassandraWriteContext; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.ReadCommand; @@@ -113,7 -113,7 +112,6 @@@ import org.apache.cassandra.utils.Pair import org.apache.cassandra.utils.concurrent.Future; import org.apache.cassandra.utils.concurrent.FutureCombiner; import org.apache.cassandra.utils.concurrent.ImmediateFuture; --import org.apache.cassandra.utils.concurrent.OpOrder; import io.github.jbellis.jvector.vector.VectorSimilarityFunction; @@@ -604,7 -604,7 +602,7 @@@ public class StorageAttachedIndex imple { if (transactionType == IndexTransaction.Type.UPDATE) { -- return new UpdateIndexer(key, memtable, writeContext); ++ return new UpdateIndexer(key, memtable); } // we are only interested in the data from Memtable @@@ -1018,34 -1018,34 +1016,30 @@@ { private final DecoratedKey key; private final Memtable memtable; -- private final WriteContext writeContext; -- UpdateIndexer(DecoratedKey key, Memtable memtable, WriteContext writeContext) ++ UpdateIndexer(DecoratedKey key, Memtable memtable) { this.key = key; this.memtable = memtable; -- this.writeContext = writeContext; } @Override public void insertRow(Row row) { -- adjustMemtableSize(memtableIndexManager.index(key, row, memtable), -- CassandraWriteContext.fromContext(writeContext).getGroup()); ++ adjustMemtableSize(memtableIndexManager.index(key, row, memtable)); } @Override public void updateRow(Row oldRow, Row newRow) { -- adjustMemtableSize(memtableIndexManager.update(key, oldRow, newRow, memtable), -- CassandraWriteContext.fromContext(writeContext).getGroup()); ++ adjustMemtableSize(memtableIndexManager.update(key, oldRow, newRow, memtable)); } -- void adjustMemtableSize(long additionalSpace, OpOrder.Group opGroup) ++ void adjustMemtableSize(long additionalSpace) { // The memtable will assert if we try and reduce its memory usage so, for now, just don't tell it. if (additionalSpace >= 0) -- memtable.markExtraOnHeapUsed(additionalSpace, opGroup); ++ memtable.markExtraOnHeapUsed(additionalSpace); } } } diff --cc src/java/org/apache/cassandra/index/sasi/SASIIndex.java index e4e56b542c,e4e56b542c..8840f15f57 --- a/src/java/org/apache/cassandra/index/sasi/SASIIndex.java +++ b/src/java/org/apache/cassandra/index/sasi/SASIIndex.java @@@ -33,7 -33,7 +33,6 @@@ import com.googlecode.concurrenttrees.c import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.Operator; import org.apache.cassandra.cql3.statements.schema.IndexTarget; --import org.apache.cassandra.db.CassandraWriteContext; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.DeletionTime; @@@ -80,7 -80,7 +79,6 @@@ import org.apache.cassandra.schema.Tabl import org.apache.cassandra.service.ClientState; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.Pair; --import org.apache.cassandra.utils.concurrent.OpOrder; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@@ -295,7 -295,7 +293,7 @@@ public class SASIIndex implements Index public void insertRow(Row row) { if (isNewData()) -- adjustMemtableSize(index.index(key, row), CassandraWriteContext.fromContext(context).getGroup()); ++ adjustMemtableSize(index.index(key, row)); } public void updateRow(Row oldRow, Row newRow) @@@ -316,9 -316,9 +314,9 @@@ return transactionType == IndexTransaction.Type.UPDATE; } -- public void adjustMemtableSize(long additionalSpace, OpOrder.Group opGroup) ++ public void adjustMemtableSize(long additionalSpace) { -- baseCfs.getTracker().getView().getCurrentMemtable().markExtraOnHeapUsed(additionalSpace, opGroup); ++ baseCfs.getTracker().getView().getCurrentMemtable().markExtraOnHeapUsed(additionalSpace); } }; } diff --cc src/java/org/apache/cassandra/utils/memory/HeapPool.java index cf1307f11b,cf1307f11b..c652c5e1a5 --- a/src/java/org/apache/cassandra/utils/memory/HeapPool.java +++ b/src/java/org/apache/cassandra/utils/memory/HeapPool.java @@@ -69,7 -69,7 +69,7 @@@ public class HeapPool extends MemtableP public ByteBuffer allocate(int size, OpOrder.Group opGroup) { -- super.onHeap().allocate(size, opGroup); ++ super.onHeap().allocate(size); return ByteBuffer.allocate(size); } @@@ -105,10 -105,10 +105,10 @@@ { return new MemtableAllocator.SubAllocator(this) { -- public void allocate(long size, OpOrder.Group opGroup) ++ public void allocate(long size) { onAllocated.accept(size, table); -- super.allocate(size, opGroup); ++ super.allocate(size); } }; } @@@ -133,7 -133,7 +133,7 @@@ public ByteBuffer allocate(int size, OpOrder.Group opGroup) { -- super.onHeap().allocate(size, opGroup); ++ super.onHeap().allocate(size); return ByteBuffer.allocate(size); } diff --cc src/java/org/apache/cassandra/utils/memory/MemtableAllocator.java index ee83f92d64,dd80bf8e54..8bfb766e68 --- a/src/java/org/apache/cassandra/utils/memory/MemtableAllocator.java +++ b/src/java/org/apache/cassandra/utils/memory/MemtableAllocator.java @@@ -158,16 -165,18 +165,17 @@@ public abstract class MemtableAllocato /** * Like allocate, but permits allocations to be negative. */ -- public void adjust(long size, OpOrder.Group opGroup) ++ public void adjust(long size) { if (size <= 0) released(-size); else -- allocate(size, opGroup); ++ allocate(size); } - // allocate memory in the tracker, and mark ourselves as owning it - public void allocate(long size, OpOrder.Group opGroup) + // account memory in the tracker, and mark ourselves as owning it; this only tracks usage, which - // still drives cleaning, the limit is enforced in awaitRoom() (CASSANDRA-21019). opGroup is unused, - // kept for compatibility within this release series. - public void allocate(long size, OpOrder.Group opGroup) ++ // still drives cleaning, the limit is enforced in awaitRoom() (CASSANDRA-21019). ++ public void allocate(long size) { assert size >= 0; diff --cc src/java/org/apache/cassandra/utils/memory/MemtableBufferAllocator.java index 9680da6693,9680da6693..d5f7d10b55 --- a/src/java/org/apache/cassandra/utils/memory/MemtableBufferAllocator.java +++ b/src/java/org/apache/cassandra/utils/memory/MemtableBufferAllocator.java @@@ -99,9 -99,9 +99,9 @@@ public abstract class MemtableBufferAll { int remaining = contextSlab.remaining(); if (contextSlab.isDirect()) -- allocator.offHeap().adjust(-remaining, opGroup); ++ allocator.offHeap().adjust(-remaining); else -- allocator.onHeap().adjust(-remaining, opGroup); ++ allocator.onHeap().adjust(-remaining); contextSlab.position(contextSlab.position() + remaining); } } diff --cc src/java/org/apache/cassandra/utils/memory/NativeAllocator.java index 531c7e1041,531c7e1041..d35101f922 --- a/src/java/org/apache/cassandra/utils/memory/NativeAllocator.java +++ b/src/java/org/apache/cassandra/utils/memory/NativeAllocator.java @@@ -198,7 -198,7 +198,7 @@@ public class NativeAllocator extends Me int remaining = limit - offset; if (remaining > 0) { -- allocator.offHeap().adjust(-remaining, opGroup); ++ allocator.offHeap().adjust(-remaining); offset += remaining; } } @@@ -212,7 -212,7 +212,7 @@@ public long allocate(int size, OpOrder.Group opGroup) { assert size >= 0; -- offHeap().allocate(size, opGroup); ++ offHeap().allocate(size); // satisfy large allocations directly from JVM since they don't cause fragmentation // as badly, and fill up our regions quickly if (size > MAX_CLONED_SIZE) diff --cc src/java/org/apache/cassandra/utils/memory/SlabAllocator.java index 9c0b263f10,9c0b263f10..c99c766d55 --- a/src/java/org/apache/cassandra/utils/memory/SlabAllocator.java +++ b/src/java/org/apache/cassandra/utils/memory/SlabAllocator.java @@@ -85,7 -85,7 +85,7 @@@ public class SlabAllocator extends Memt if (size == 0) return ByteBufferUtil.EMPTY_BYTE_BUFFER; -- (allocateOnHeapOnly ? onHeap() : offHeap()).allocate(size, opGroup); ++ (allocateOnHeapOnly ? onHeap() : offHeap()).allocate(size); // satisfy large allocations directly from JVM since they don't cause fragmentation // as badly, and fill up our regions quickly if (size > MAX_CLONED_SIZE) diff --cc test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java index c8b636c94e,c8b636c94e..f829a2646f --- a/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java +++ b/test/unit/org/apache/cassandra/db/ColumnFamilyStoreTest.java @@@ -868,12 -868,12 +868,12 @@@ public class ColumnFamilyStoreTes } @Override -- public void markExtraOnHeapUsed(long additionalSpace, Group opGroup) ++ public void markExtraOnHeapUsed(long additionalSpace) { } @Override -- public void markExtraOffHeapUsed(long additionalSpace, Group opGroup) ++ public void markExtraOffHeapUsed(long additionalSpace) { } diff --cc test/unit/org/apache/cassandra/utils/memory/MemtableAllocatorAwaitRoomTest.java index 0000000000,b4d59658f7..be40f66e9f mode 000000,100644..100644 --- a/test/unit/org/apache/cassandra/utils/memory/MemtableAllocatorAwaitRoomTest.java +++ b/test/unit/org/apache/cassandra/utils/memory/MemtableAllocatorAwaitRoomTest.java @@@ -1,0 -1,122 +1,120 @@@ + /* + * 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.utils.memory; + + import java.util.concurrent.TimeUnit; + + import org.junit.BeforeClass; + import org.junit.Test; + + import org.apache.cassandra.config.DatabaseDescriptor; + import org.apache.cassandra.utils.concurrent.ImmediateFuture; + import org.apache.cassandra.utils.concurrent.OpOrder; + + import static org.junit.Assert.assertFalse; + import static org.junit.Assert.assertTrue; + + /** + * CASSANDRA-21019: heap_buffers / unslabbed_heap_buffers create their pool with an + * off-heap limit of 0; a zero-limit sub-pool is never allocated from and never + * signalled, so awaitRoom() must skip it, and configured limits must still gate. + * awaitRoom() is also the only place that waits: accounting an allocation must not. + */ + public class MemtableAllocatorAwaitRoomTest + { + @BeforeClass + public static void setup() + { + DatabaseDescriptor.daemonInitialization(); + } + + @Test(timeout = 30_000) + public void zeroLimitPoolDoesNotBlock() throws Exception + { + SlabPool pool = new SlabPool(1 << 20, 0, 1.0f, () -> ImmediateFuture.success(true)); + try + { + MemtableAllocator allocator = pool.newAllocator("test"); + OpOrder.Group g = new OpOrder().start(); + Thread gate = run(() -> allocator.awaitRoomToStart(g)); + gate.join(5_000); + assertFalse("awaitRoomToStart hung on the unused zero-limit off-heap pool", gate.isAlive()); + g.close(); + } + finally + { + pool.shutdownAndWait(1, TimeUnit.MINUTES); + } + } + + @Test(timeout = 30_000) + public void configuredLimitStillGates() throws Exception + { + SlabPool pool = new SlabPool(1 << 20, 1 << 20, 1.0f, () -> ImmediateFuture.success(true)); + try + { + MemtableAllocator allocator = pool.newAllocator("test"); + OpOrder.Group g = new OpOrder().start(); + pool.onHeap.allocated(pool.onHeap.limit); + Thread gate = run(() -> allocator.awaitRoomToStart(g)); + gate.join(2_000); + assertTrue("gate did not wait while the on-heap pool was at its limit", gate.isAlive()); + pool.onHeap.released(pool.onHeap.limit); + gate.join(10_000); + assertFalse("gate did not wake after room was released", gate.isAlive()); + g.close(); + } + finally + { + pool.shutdownAndWait(1, TimeUnit.MINUTES); + } + } + + /** + * The path Memtable.markExtraOnHeapUsed takes, and with it every allocation an already-started mutation + * makes. SAI, SASI and the Accord route index all account index memory from an indexer callback, under the + * base table's memtable-internal locks, so this path must record and return however far over the limit the + * pool is; parking here is what CASSANDRA-21019 removed. + */ + @Test(timeout = 30_000) + public void accountingDoesNotWaitAboveTheLimit() throws Exception + { + SlabPool pool = new SlabPool(1 << 20, 1 << 20, 1.0f, () -> ImmediateFuture.success(true)); + try + { + MemtableAllocator allocator = pool.newAllocator("test"); - OpOrder.Group g = new OpOrder().start(); + pool.onHeap.allocated(pool.onHeap.limit); - Thread mark = run(() -> allocator.onHeap().allocate(1024, g)); ++ Thread mark = run(() -> allocator.onHeap().allocate(1024)); + mark.join(5_000); + assertFalse("accounting waited for room while the on-heap pool was over its limit", mark.isAlive()); + assertTrue("the accounting was not recorded", allocator.onHeap().owns() >= 1024); - g.close(); + } + finally + { + pool.shutdownAndWait(1, TimeUnit.MINUTES); + } + } + + private static Thread run(Runnable r) + { + Thread t = new Thread(r, "await-room"); + t.setDaemon(true); + t.start(); + return t; + } + } diff --cc test/unit/org/apache/cassandra/utils/memory/NativeAllocatorTest.java index 9655e9c250,3a79c19fd4..1e87abca79 --- a/test/unit/org/apache/cassandra/utils/memory/NativeAllocatorTest.java +++ b/test/unit/org/apache/cassandra/utils/memory/NativeAllocatorTest.java @@@ -99,17 -99,17 +99,17 @@@ public class NativeAllocatorTes verifyUsedReclaiming(10, 0); // confirm adjustment works -- allocator.offHeap().adjust(-10, group); ++ allocator.offHeap().adjust(-10); verifyUsedReclaiming(0, 0); -- allocator.offHeap().adjust(10, group); ++ allocator.offHeap().adjust(10); verifyUsedReclaiming(10, 0); // confirm we cannot allocate negative boolean success = false; try { -- allocator.offHeap().allocate(-10, group); ++ allocator.offHeap().allocate(-10); } catch (AssertionError e) { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
