From 6a4f7cc969368d73a402b15c8af4b2f4997f5914 Mon Sep 17 00:00:00 2001 From: Martin Sillence Date: Thu, 24 Feb 2022 09:06:24 +0000 Subject: [PATCH 1/5] Add metrics for allocated and in-use objects --- src/main/java/stormpot/AllocationController.java | 10 ++++++++++ src/main/java/stormpot/BAllocThread.java | 14 ++++++++++++++ src/main/java/stormpot/BSlot.java | 5 +++++ src/main/java/stormpot/BlazePool.java | 10 ++++++++++ .../java/stormpot/DirectAllocationController.java | 15 +++++++++++++++ .../java/stormpot/InlineAllocationController.java | 15 +++++++++++++++ src/main/java/stormpot/ManagedPool.java | 13 +++++++++++++ src/main/java/stormpot/Pool.java | 4 ++++ .../stormpot/ThreadedAllocationController.java | 10 ++++++++++ src/test/java/stormpot/WhiteboxPoolTest.java | 10 ++++++++++ 10 files changed, 106 insertions(+) diff --git a/src/main/java/stormpot/AllocationController.java b/src/main/java/stormpot/AllocationController.java index ded66fa4..0f1d297a 100644 --- a/src/main/java/stormpot/AllocationController.java +++ b/src/main/java/stormpot/AllocationController.java @@ -54,4 +54,14 @@ abstract class AllocationController { * @see ManagedPool#getLeakedObjectsCount() */ abstract long countLeakedObjects(); + + /** + * @see ManagedPool#getAllocatedSize() + */ + abstract int allocatedSize(); + + /** + * @see ManagedPool#getInUse() + */ + abstract int inUse(); } diff --git a/src/main/java/stormpot/BAllocThread.java b/src/main/java/stormpot/BAllocThread.java index 6cb344ce..12b51adc 100644 --- a/src/main/java/stormpot/BAllocThread.java +++ b/src/main/java/stormpot/BAllocThread.java @@ -390,4 +390,18 @@ long countLeakedObjects() { void offerDeadSlot(BSlot slot) { dead.offer(slot); } + + int allocatedSize() { + return size; + } + + int inUse() { + int inUse = size - live.size(); // slots not in live are in thread locals? + for (BSlot slot: live) { + if (slot.isClaimedOrThreadLocal()) { + inUse++; + } + } + return inUse; + } } diff --git a/src/main/java/stormpot/BSlot.java b/src/main/java/stormpot/BSlot.java index 55ccb4cf..b4a9cce0 100644 --- a/src/main/java/stormpot/BSlot.java +++ b/src/main/java/stormpot/BSlot.java @@ -124,6 +124,11 @@ boolean isLive() { boolean isClaimed() { return get() == CLAIMED; } + + boolean isClaimedOrThreadLocal() { + int state = get(); + return state == CLAIMED || state == TLR_CLAIMED; + } void incrementClaims() { claims++; diff --git a/src/main/java/stormpot/BlazePool.java b/src/main/java/stormpot/BlazePool.java index f753b466..7f8610f4 100644 --- a/src/main/java/stormpot/BlazePool.java +++ b/src/main/java/stormpot/BlazePool.java @@ -377,4 +377,14 @@ public double getDeallocationLatencyPercentile(double percentile) { public long getLeakedObjectsCount() { return allocator.countLeakedObjects(); } + + @Override + public int getAllocatedSize() { + return allocator.allocatedSize(); + } + + @Override + public int getInUse() { + return allocator.inUse(); + } } diff --git a/src/main/java/stormpot/DirectAllocationController.java b/src/main/java/stormpot/DirectAllocationController.java index d979cd60..603732e1 100644 --- a/src/main/java/stormpot/DirectAllocationController.java +++ b/src/main/java/stormpot/DirectAllocationController.java @@ -116,4 +116,19 @@ long getFailedAllocationCount() { long countLeakedObjects() { return -1; } + + @Override + public int allocatedSize() { + return size; + } + + int inUse() { + int inUse = size - live.size(); + for (BSlot slot: live) { + if (slot.isClaimedOrThreadLocal()) { + inUse++; + } + } + return inUse; + } } diff --git a/src/main/java/stormpot/InlineAllocationController.java b/src/main/java/stormpot/InlineAllocationController.java index 5f33b347..adbacfe7 100644 --- a/src/main/java/stormpot/InlineAllocationController.java +++ b/src/main/java/stormpot/InlineAllocationController.java @@ -362,4 +362,19 @@ private void recordObjectLifetimeSample(long nanoseconds) { metricsRecorder.recordObjectLifetimeSampleMillis(milliseconds); } } + + @Override + public int allocatedSize() { + return live.size() - poisonedSlots.get(); + } + + int inUse() { + int inUse = size - live.size(); + for (BSlot slot: live) { + if (slot.isClaimedOrThreadLocal()) { + inUse++; + } + } + return inUse; + } } diff --git a/src/main/java/stormpot/ManagedPool.java b/src/main/java/stormpot/ManagedPool.java index 01045d35..b358be99 100644 --- a/src/main/java/stormpot/ManagedPool.java +++ b/src/main/java/stormpot/ManagedPool.java @@ -181,4 +181,17 @@ public interface ManagedPool { * the pool. */ double getDeallocationLatencyPercentile(double percentile); + + /** + * approximate currently allocated size, this is O(poolsize) to count the number of slots + * @return current allocated pool size + */ + int getAllocatedSize(); + + /** + * approximate number of objects currently in use, this is O(poolsize) to check each slot + * this does not lock the pool so the number of objects in use will change under it as it tries to count + * @return number of objects currently in use + */ + int getInUse(); } diff --git a/src/main/java/stormpot/Pool.java b/src/main/java/stormpot/Pool.java index 9ebd35fd..36ba942e 100644 --- a/src/main/java/stormpot/Pool.java +++ b/src/main/java/stormpot/Pool.java @@ -304,4 +304,8 @@ public T tryClaim() { * @return A thread-local {@link PoolTap}. */ public abstract PoolTap getThreadLocalTap(); + + public abstract int getInUse(); + + public abstract int getAllocatedSize(); } diff --git a/src/main/java/stormpot/ThreadedAllocationController.java b/src/main/java/stormpot/ThreadedAllocationController.java index 087f67f7..5ddf71a9 100644 --- a/src/main/java/stormpot/ThreadedAllocationController.java +++ b/src/main/java/stormpot/ThreadedAllocationController.java @@ -67,4 +67,14 @@ public long getFailedAllocationCount() { public long countLeakedObjects() { return allocator.countLeakedObjects(); } + + @Override + public int allocatedSize() { + return allocator.allocatedSize(); + } + + @Override + public int inUse() { + return allocator.inUse(); + } } diff --git a/src/test/java/stormpot/WhiteboxPoolTest.java b/src/test/java/stormpot/WhiteboxPoolTest.java index 7af10617..3d2e5913 100644 --- a/src/test/java/stormpot/WhiteboxPoolTest.java +++ b/src/test/java/stormpot/WhiteboxPoolTest.java @@ -63,6 +63,16 @@ public Poolable claim(Timeout timeout) { public Poolable tryClaim() { return null; } + + @Override + public int getInUse() { + return 0; + } + + @Override + public int getAllocatedSize() { + return 0; + } }; pool.getThreadSafeTap().claim(longTimeout); assertTrue(delegatedToPool.get()); From 508526ec8e3a1cba6dadb1d9437a3d327e9a8b2e Mon Sep 17 00:00:00 2001 From: Martin Sillence Date: Mon, 28 Feb 2022 14:58:32 +0000 Subject: [PATCH 2/5] tidy up the race around size and seen live items --- src/main/java/stormpot/BAllocThread.java | 8 +++++--- src/main/java/stormpot/DirectAllocationController.java | 6 ++++-- src/main/java/stormpot/InlineAllocationController.java | 8 +++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/stormpot/BAllocThread.java b/src/main/java/stormpot/BAllocThread.java index 12b51adc..ec128898 100644 --- a/src/main/java/stormpot/BAllocThread.java +++ b/src/main/java/stormpot/BAllocThread.java @@ -396,12 +396,14 @@ int allocatedSize() { } int inUse() { - int inUse = size - live.size(); // slots not in live are in thread locals? + int inUse = 0; + int liveSize = 0; for (BSlot slot: live) { + liveSize++; if (slot.isClaimedOrThreadLocal()) { inUse++; } } - return inUse; - } + return size - liveSize + inUse; + } } diff --git a/src/main/java/stormpot/DirectAllocationController.java b/src/main/java/stormpot/DirectAllocationController.java index 603732e1..de731118 100644 --- a/src/main/java/stormpot/DirectAllocationController.java +++ b/src/main/java/stormpot/DirectAllocationController.java @@ -123,12 +123,14 @@ public int allocatedSize() { } int inUse() { - int inUse = size - live.size(); + int inUse = 0; + int liveSize = 0; for (BSlot slot: live) { + liveSize++; if (slot.isClaimedOrThreadLocal()) { inUse++; } } - return inUse; + return size - liveSize + inUse; } } diff --git a/src/main/java/stormpot/InlineAllocationController.java b/src/main/java/stormpot/InlineAllocationController.java index adbacfe7..55f5010a 100644 --- a/src/main/java/stormpot/InlineAllocationController.java +++ b/src/main/java/stormpot/InlineAllocationController.java @@ -369,12 +369,14 @@ public int allocatedSize() { } int inUse() { - int inUse = size - live.size(); + int inUse = 0; + int liveSize = 0; for (BSlot slot: live) { + liveSize++; if (slot.isClaimedOrThreadLocal()) { inUse++; } } - return inUse; - } + return size - liveSize + inUse; + } } From 6bedab24a9e74e2b9aac9949062e62a96102b3ac Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Sun, 2 Jun 2024 13:55:07 -0700 Subject: [PATCH 3/5] Fix javadocs and API compatibility --- src/main/java/stormpot/ManagedPool.java | 29 ++++++++++++++++---- src/main/java/stormpot/Pool.java | 4 --- src/test/java/stormpot/WhiteboxPoolTest.java | 10 ------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/main/java/stormpot/ManagedPool.java b/src/main/java/stormpot/ManagedPool.java index b358be99..c5143e9a 100644 --- a/src/main/java/stormpot/ManagedPool.java +++ b/src/main/java/stormpot/ManagedPool.java @@ -16,6 +16,7 @@ package stormpot; import javax.management.MXBean; +import java.util.concurrent.ConcurrentLinkedQueue; /** * This is the JMX management interface for Stormpot object pools. @@ -183,15 +184,31 @@ public interface ManagedPool { double getDeallocationLatencyPercentile(double percentile); /** - * approximate currently allocated size, this is O(poolsize) to count the number of slots - * @return current allocated pool size + * Get the approximate number of currently allocated objects. + * + * This operation has time complexity O(poolSize) to count the number of slots. + * + * The default implementation of this interface methods returns {@code -1}. + * + * @return The current approximate number of allocated objects. */ - int getAllocatedSize(); + default int getAllocatedSize() { + return -1; + } /** - * approximate number of objects currently in use, this is O(poolsize) to check each slot - * this does not lock the pool so the number of objects in use will change under it as it tries to count + * Get the approximate number of objects currently in use. + * + * This operation has time complexity O(poolSize) to check each slot. + * + * The counting operation is "weakly consistent", + * in a similar sense to {@link ConcurrentLinkedQueue#iterator()}. + * + * The default implementation of this interface methods returns {@code -1}. + * * @return number of objects currently in use */ - int getInUse(); + default int getInUse() { + return -1; + } } diff --git a/src/main/java/stormpot/Pool.java b/src/main/java/stormpot/Pool.java index 36ba942e..9ebd35fd 100644 --- a/src/main/java/stormpot/Pool.java +++ b/src/main/java/stormpot/Pool.java @@ -304,8 +304,4 @@ public T tryClaim() { * @return A thread-local {@link PoolTap}. */ public abstract PoolTap getThreadLocalTap(); - - public abstract int getInUse(); - - public abstract int getAllocatedSize(); } diff --git a/src/test/java/stormpot/WhiteboxPoolTest.java b/src/test/java/stormpot/WhiteboxPoolTest.java index 3d2e5913..7af10617 100644 --- a/src/test/java/stormpot/WhiteboxPoolTest.java +++ b/src/test/java/stormpot/WhiteboxPoolTest.java @@ -63,16 +63,6 @@ public Poolable claim(Timeout timeout) { public Poolable tryClaim() { return null; } - - @Override - public int getInUse() { - return 0; - } - - @Override - public int getAllocatedSize() { - return 0; - } }; pool.getThreadSafeTap().claim(longTimeout); assertTrue(delegatedToPool.get()); From dd1fd2d2698eab24db74cf8a2880e3010fd61696 Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Sun, 2 Jun 2024 14:16:33 -0700 Subject: [PATCH 4/5] Add override missing annotations --- src/main/java/stormpot/DirectAllocationController.java | 1 + src/main/java/stormpot/InlineAllocationController.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/stormpot/DirectAllocationController.java b/src/main/java/stormpot/DirectAllocationController.java index de731118..a8128d55 100644 --- a/src/main/java/stormpot/DirectAllocationController.java +++ b/src/main/java/stormpot/DirectAllocationController.java @@ -122,6 +122,7 @@ public int allocatedSize() { return size; } + @Override int inUse() { int inUse = 0; int liveSize = 0; diff --git a/src/main/java/stormpot/InlineAllocationController.java b/src/main/java/stormpot/InlineAllocationController.java index 55f5010a..13321a96 100644 --- a/src/main/java/stormpot/InlineAllocationController.java +++ b/src/main/java/stormpot/InlineAllocationController.java @@ -367,7 +367,8 @@ private void recordObjectLifetimeSample(long nanoseconds) { public int allocatedSize() { return live.size() - poisonedSlots.get(); } - + + @Override int inUse() { int inUse = 0; int liveSize = 0; From aee9fe38415bc91d7fb35dd2d41557aea5883288 Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Sun, 2 Jun 2024 15:02:21 -0700 Subject: [PATCH 5/5] Add tests for ManagedPool#getCurrentAllocatedCount and getCurrentInUseCount --- src/main/java/stormpot/AllocationController.java | 4 ++-- src/main/java/stormpot/BlazePool.java | 4 ++-- src/main/java/stormpot/ManagedPool.java | 7 +++++-- src/test/java/blackbox/AbstractPoolTest.java | 12 ++++++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/stormpot/AllocationController.java b/src/main/java/stormpot/AllocationController.java index 0f1d297a..751923b9 100644 --- a/src/main/java/stormpot/AllocationController.java +++ b/src/main/java/stormpot/AllocationController.java @@ -56,12 +56,12 @@ abstract class AllocationController { abstract long countLeakedObjects(); /** - * @see ManagedPool#getAllocatedSize() + * @see ManagedPool#getCurrentAllocatedCount() */ abstract int allocatedSize(); /** - * @see ManagedPool#getInUse() + * @see ManagedPool#getCurrentInUseCount() */ abstract int inUse(); } diff --git a/src/main/java/stormpot/BlazePool.java b/src/main/java/stormpot/BlazePool.java index 7f8610f4..ffcb6006 100644 --- a/src/main/java/stormpot/BlazePool.java +++ b/src/main/java/stormpot/BlazePool.java @@ -379,12 +379,12 @@ public long getLeakedObjectsCount() { } @Override - public int getAllocatedSize() { + public int getCurrentAllocatedCount() { return allocator.allocatedSize(); } @Override - public int getInUse() { + public int getCurrentInUseCount() { return allocator.inUse(); } } diff --git a/src/main/java/stormpot/ManagedPool.java b/src/main/java/stormpot/ManagedPool.java index c5143e9a..739472ef 100644 --- a/src/main/java/stormpot/ManagedPool.java +++ b/src/main/java/stormpot/ManagedPool.java @@ -190,9 +190,12 @@ public interface ManagedPool { * * The default implementation of this interface methods returns {@code -1}. * + * Unlike {@link #getAllocationCount()}, this returns only the number of objects currently in the pool, + * which typically would be the same as {@link #getTargetSize()}. + * * @return The current approximate number of allocated objects. */ - default int getAllocatedSize() { + default int getCurrentAllocatedCount() { return -1; } @@ -208,7 +211,7 @@ default int getAllocatedSize() { * * @return number of objects currently in use */ - default int getInUse() { + default int getCurrentInUseCount() { return -1; } } diff --git a/src/test/java/blackbox/AbstractPoolTest.java b/src/test/java/blackbox/AbstractPoolTest.java index 2f92287f..ea04c15e 100644 --- a/src/test/java/blackbox/AbstractPoolTest.java +++ b/src/test/java/blackbox/AbstractPoolTest.java @@ -913,6 +913,18 @@ void managedPoolMustGivePoolState() throws Exception { assertTrue(managedPool.isShutDown()); } + @Test + void managedPoolMustGiveNumberOfAllocatedAndInUseObjects() throws Exception { + createOneObjectPool(); + pool.claim(longTimeout).release(); // Ensure the single object is allocated + ManagedPool managedPool = pool.getManagedPool(); + assertThat(managedPool.getCurrentAllocatedCount()).isOne(); + assertThat(managedPool.getCurrentInUseCount()).isZero(); + T obj = pool.claim(longTimeout); + assertThat(managedPool.getCurrentInUseCount()).isOne(); + obj.release(); + } + @ParameterizedTest @EnumSource(Taps.class) void applyMustThrowOnNullTimeout(Taps taps) {