Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/java/org/apache/cassandra/tools/nodetool/Info.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public void execute(NodeProbe probe)
try
{
out.printf("%-23s: size %s, overflow size: %s, capacity %s%n", "Network Cache",
FileUtils.stringifyFileSize((long) probe.getBufferPoolMetric("networking", "Size")),
FileUtils.stringifyFileSize((long) probe.getBufferPoolMetric("networking", "UsedSize")),
FileUtils.stringifyFileSize((long) probe.getBufferPoolMetric("networking", "OverflowSize")),
FileUtils.stringifyFileSize((long) probe.getBufferPoolMetric("networking", "Capacity")));
}
Expand Down
6 changes: 1 addition & 5 deletions src/java/org/apache/cassandra/utils/memory/BufferPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -882,16 +882,12 @@ public void putUnusedPortion(ByteBuffer buffer)
{
Chunk chunk = Chunk.getParentChunk(buffer);
int originalCapacity = buffer.capacity();
int size = originalCapacity - buffer.limit();

if (chunk == null)
{
updateOverflowMemoryUsage(-size);
return;
}

chunk.freeUnusedPortion(buffer);
// Calculate the actual freed bytes which may be different from `size` when pooling is involved
// The actual freed bytes may differ from (originalCapacity - limit) when pooling is involved
memoryInUse.inc(buffer.capacity() - originalCapacity);
}

Expand Down
47 changes: 47 additions & 0 deletions test/unit/org/apache/cassandra/metrics/BufferPoolMetricsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

package org.apache.cassandra.metrics;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.junit.Before;
Expand Down Expand Up @@ -242,6 +245,50 @@ public void testFailedRequestsDontChangeMetrics()
.isGreaterThanOrEqualTo(65536);
}

@Test
public void testOverflowAccountingBalancesAfterPutUnusedPortionAndPut()
{
assertEquals(0, metrics.overflowSize.getValue().longValue());
assertEquals(0, metrics.usedSize.getValue().longValue());

// Buffer larger than NORMAL_CHUNK_SIZE bypasses the pool and goes to overflow
int bufferSize = BufferPool.NORMAL_CHUNK_SIZE + 1;
ByteBuffer buffer = bufferPool.get(bufferSize, BufferType.OFF_HEAP);
assertEquals(bufferSize, metrics.overflowSize.getValue().longValue());

// Simulate partial use: caller only fills half the buffer
buffer.limit(bufferSize / 2);
bufferPool.putUnusedPortion(buffer);

// Full return — overflowSize must return to zero, not go negative
bufferPool.put(buffer);
assertEquals(0, metrics.overflowSize.getValue().longValue());
assertEquals(0, metrics.usedSize.getValue().longValue());
}

@Test
public void testUsedSizeIsZeroWhenPoolIsIdle()
{
assertEquals(0, metrics.usedSize.getValue().longValue());

// Allocate pooled buffers until the pool is fully expanded
int bufferSize = BufferPool.NORMAL_CHUNK_SIZE - 1;
List<ByteBuffer> buffers = new ArrayList<>();
while (bufferPool.sizeInBytes() < bufferPool.memoryUsageThreshold())
buffers.add(bufferPool.get(bufferSize, BufferType.OFF_HEAP));

assertThat(metrics.usedSize.getValue().longValue()).isGreaterThan(0);

// Return all buffers — pool stays expanded but usedSize must drop to zero
for (ByteBuffer buf : buffers)
bufferPool.put(buf);

assertEquals(0, metrics.usedSize.getValue().longValue());
// sizeInBytes remains at capacity (memoryAllocated is never decremented),
// confirming that usedSize and sizeInBytes measure different things
assertThat(bufferPool.sizeInBytes()).isGreaterThan(0);
}

private void tryRequestNegativeBufferSize()
{
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(
Expand Down