diff --git a/CHANGES.txt b/CHANGES.txt index 203a00d20c..30a09c9284 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,8 @@ 1.1.12 * Add retry mechanism to OTC for non-droppable_verbs (CASSANDRA-5393) + * Use allocator information to improve memtable memory usage estimate + (CASSANDRA-5497) + 1.1.11 * Fix trying to load deleted row into row cache on startup (CASSANDRA-4463) diff --git a/src/java/org/apache/cassandra/db/Memtable.java b/src/java/org/apache/cassandra/db/Memtable.java index 347b997242..a713cac2f0 100644 --- a/src/java/org/apache/cassandra/db/Memtable.java +++ b/src/java/org/apache/cassandra/db/Memtable.java @@ -120,9 +120,18 @@ public class Memtable public long getLiveSize() { + // 25% fudge factor on the base throughput * liveRatio calculation. (Based on observed // pre-slabbing behavior -- not sure what accounts for this. May have changed with introduction of slabbing.) - return (long) (currentSize.get() * cfs.liveRatio * 1.25); + long estimatedSize = (long) (currentSize.get() * cfs.liveRatio * 1.25); + + // cap the estimate at both ends by what the allocator can tell us + if (estimatedSize < allocator.getMinimumSize()) + return allocator.getMinimumSize(); + if (estimatedSize > allocator.getMaximumSize()) + return allocator.getMaximumSize(); + + return estimatedSize; } public long getSerializedSize() diff --git a/src/java/org/apache/cassandra/utils/SlabAllocator.java b/src/java/org/apache/cassandra/utils/SlabAllocator.java index 6e477fe627..24a2f656c8 100644 --- a/src/java/org/apache/cassandra/utils/SlabAllocator.java +++ b/src/java/org/apache/cassandra/utils/SlabAllocator.java @@ -21,6 +21,7 @@ package org.apache.cassandra.utils; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import com.google.common.base.Preconditions; @@ -49,7 +50,8 @@ public class SlabAllocator extends Allocator private final static int MAX_CLONED_SIZE = 128 * 1024; // bigger than this don't go in the region private final AtomicReference currentRegion = new AtomicReference(); - private volatile int regionCount; + private volatile int regionCount = 0; + private AtomicLong unslabbed = new AtomicLong(0); public ByteBuffer allocate(int size) { @@ -60,7 +62,10 @@ public class SlabAllocator extends Allocator // 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) + { + unslabbed.addAndGet(size); return ByteBuffer.allocate(size); + } while (true) { @@ -105,6 +110,22 @@ public class SlabAllocator extends Allocator } } + /** + * @return a lower bound on how much space has been allocated + */ + public long getMinimumSize() + { + return unslabbed.get() + (regionCount - 1) * REGION_SIZE; + } + + /** + * @return an upper bound on how much space has been allocated + */ + public long getMaximumSize() + { + return unslabbed.get() + regionCount * REGION_SIZE; + } + /** * A region of memory out of which allocations are sliced. *