Author: markt Date: Wed Aug 17 17:03:35 2011 New Revision: 1158831 URL: http://svn.apache.org/viewvc?rev=1158831&view=rev Log: POOL-98. Add additional attributes for moniroting (also available via JMX)
Modified: commons/proper/pool/trunk/src/changes/changes.xml commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMBean.java commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPoolMBean.java commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/PooledObject.java Modified: commons/proper/pool/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/changes/changes.xml?rev=1158831&r1=1158830&r2=1158831&view=diff ============================================================================== --- commons/proper/pool/trunk/src/changes/changes.xml (original) +++ commons/proper/pool/trunk/src/changes/changes.xml Wed Aug 17 17:03:35 2011 @@ -77,6 +77,9 @@ <action dev="markt" type="update" issue="POOL-172"> Expose GOP and GKOP attributes via JMX. </action> + <action dev="markt" type="update" issue="POOL-98"> + Add additional attributes (also accessible via JMX) for monitoring. + </action> </release> <release version="1.5.6" date="2011-04-03" description="This is a patch release, including bugfixes only."> <action dev="markt" type="fix" issue="POOL-179" due-to="Axel Grossmann"> Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java?rev=1158831&r1=1158830&r2=1158831&view=diff ============================================================================== --- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java (original) +++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java Wed Aug 17 17:03:35 2011 @@ -19,8 +19,10 @@ package org.apache.commons.pool2.impl; import java.lang.management.ManagementFactory; import java.util.ArrayList; +import java.util.Deque; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -253,6 +255,8 @@ public class GenericKeyedObjectPool<K,T> startEvictor(getMinEvictableIdleTimeMillis()); + initStats(); + // JMX Registration if (config.isJmxEnabled()) { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); @@ -734,6 +738,7 @@ public class GenericKeyedObjectPool<K,T> boolean blockWhenExhausted = this.blockWhenExhausted; boolean create; + long waitTime = 0; ObjectDeque<T> objectDeque = register(key); try { @@ -751,8 +756,10 @@ public class GenericKeyedObjectPool<K,T> if (borrowMaxWait < 1) { p = objectDeque.getIdleObjects().takeFirst(); } else { + waitTime = System.currentTimeMillis(); p = objectDeque.getIdleObjects().pollFirst( borrowMaxWait, TimeUnit.MILLISECONDS); + waitTime = System.currentTimeMillis() - waitTime; } } if (p == null) { @@ -806,6 +813,7 @@ public class GenericKeyedObjectPool<K,T> if (!validate) { try { destroy(key, p, true); + destroyedByBorrowValidationCount.incrementAndGet(); } catch (Exception e) { // Ignore - validation failure is more important } @@ -823,7 +831,21 @@ public class GenericKeyedObjectPool<K,T> } finally { deregister(key); } - return p.getObject(); + + borrowedCount.incrementAndGet(); + synchronized (idleTimes) { + idleTimes.add(Long.valueOf(p.getIdleTimeMillis())); + idleTimes.poll(); + } + synchronized (waitTimes) { + waitTimes.add(Long.valueOf(waitTime)); + waitTimes.poll(); + } + synchronized (maxBorrowWaitTimeMillisLock) { + if (waitTime > maxBorrowWaitTimeMillis) { + maxBorrowWaitTimeMillis = waitTime; + } + } return p.getObject(); } @@ -857,6 +879,8 @@ public class GenericKeyedObjectPool<K,T> "Returned object not currently part of this pool"); } + long activeTime = p.getActiveTimeMillis(); + if (getTestOnReturn()) { if (!_factory.validateObject(key, t)) { try { @@ -864,6 +888,7 @@ public class GenericKeyedObjectPool<K,T> } catch (Exception e) { // TODO - Ignore? } + updateStatsReturn(activeTime); return; } } @@ -876,6 +901,7 @@ public class GenericKeyedObjectPool<K,T> } catch (Exception e) { // TODO - Ignore? } + updateStatsReturn(activeTime); return; } @@ -901,9 +927,19 @@ public class GenericKeyedObjectPool<K,T> idleObjects.addLast(p); } } + updateStatsReturn(activeTime); } + private void updateStatsReturn(long activeTime) { + returnedCount.incrementAndGet(); + synchronized (activeTimes) { + activeTimes.add(Long.valueOf(activeTime)); + activeTimes.poll(); + } + } + + /** * {@inheritDoc} * <p>Activation of this method decrements the active count associated with @@ -1195,6 +1231,7 @@ public class GenericKeyedObjectPool<K,T> if (idleEvictTime < underTest.getIdleTimeMillis()) { destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); } else { if (testWhileIdle) { boolean active = false; @@ -1204,17 +1241,20 @@ public class GenericKeyedObjectPool<K,T> active = true; } catch (Exception e) { destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!_factory.validateObject(evictionKey, underTest.getObject())) { destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); } else { try { _factory.passivateObject(evictionKey, underTest.getObject()); } catch (Exception e) { destroy(evictionKey, underTest, true); + destroyedByEvictorCount.incrementAndGet(); } } } @@ -1269,6 +1309,7 @@ public class GenericKeyedObjectPool<K,T> } PooledObject<T> p = new PooledObject<T>(t); + createdCount.incrementAndGet(); objectDeque.getAllObjects().put(t, p); return p; } @@ -1289,6 +1330,7 @@ public class GenericKeyedObjectPool<K,T> _factory.destroyObject(key, toDestory.getObject()); } finally { objectDeque.getCreateCount().decrementAndGet(); + destroyedCount.incrementAndGet(); numTotal.decrementAndGet(); } return true; @@ -1556,6 +1598,34 @@ public class GenericKeyedObjectPool<K,T> //--- JMX specific attributes ---------------------------------------------- + + private void initStats() { + for (int i = 0; i < AVERAGE_TIMING_STATS_CACHE_SIZE; i++) { + activeTimes.add(null); + idleTimes.add(null); + waitTimes.add(null); + } + } + + private long getMeanFromStatsCache(Deque<Long> cache) { + List<Long> times = new ArrayList<Long>(AVERAGE_TIMING_STATS_CACHE_SIZE); + synchronized (cache) { + times.addAll(cache); + } + double result = 0; + int counter = 0; + Iterator<Long> iter = times.iterator(); + while (iter.hasNext()) { + Long time = iter.next(); + if (time != null) { + counter++; + result = result * ((counter - 1) / counter) + + time.longValue()/counter; + } + } + return (long) result; + } + public Map<String,Integer> getNumActivePerKey() { HashMap<String,Integer> result = new HashMap<String,Integer>(); @@ -1574,7 +1644,47 @@ public class GenericKeyedObjectPool<K,T> } return result; } - + + public long getBorrowedCount() { + return borrowedCount.get(); + } + + public long getReturnedCount() { + return returnedCount.get(); + } + + public long getCreatedCount() { + return createdCount.get(); + } + + public long getDestroyedCount() { + return destroyedCount.get(); + } + + public long getDestroyedByEvictorCount() { + return destroyedByEvictorCount.get(); + } + + public long getDestroyedByBorrowValidationCount() { + return destroyedByBorrowValidationCount.get(); + } + + public long getMeanActiveTimeMillis() { + return getMeanFromStatsCache(activeTimes); + } + + public long getMeanIdleTimeMillis() { + return getMeanFromStatsCache(idleTimes); + } + + public long getMeanBorrowWaitTimeMillis() { + return getMeanFromStatsCache(waitTimes); + } + + public long getMaxBorrowWaitTimeMillis() { + return maxBorrowWaitTimeMillis; + } + //--- inner classes ---------------------------------------------- /** @@ -1826,6 +1936,20 @@ public class GenericKeyedObjectPool<K,T> */ private K evictionKey = null; + // JMX specific attributes + private static final int AVERAGE_TIMING_STATS_CACHE_SIZE = 100; + private AtomicLong borrowedCount = new AtomicLong(0); + private AtomicLong returnedCount = new AtomicLong(0); + private AtomicLong createdCount = new AtomicLong(0); + private AtomicLong destroyedCount = new AtomicLong(0); + private AtomicLong destroyedByEvictorCount = new AtomicLong(0); + private AtomicLong destroyedByBorrowValidationCount = new AtomicLong(0); + private final Deque<Long> activeTimes = new LinkedList<Long>(); + private final Deque<Long> idleTimes = new LinkedList<Long>(); + private final Deque<Long> waitTimes = new LinkedList<Long>(); + private Object maxBorrowWaitTimeMillisLock = new Object(); + private volatile long maxBorrowWaitTimeMillis = 0; + private static final String ONAME_BASE = "org.apache.commoms.pool2:type=GenericKeyedObjectPool,name="; } Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMBean.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMBean.java?rev=1158831&r1=1158830&r2=1158831&view=diff ============================================================================== --- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMBean.java (original) +++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPoolMBean.java Wed Aug 17 17:03:35 2011 @@ -38,4 +38,14 @@ public interface GenericKeyedObjectPoolM boolean isClosed(); // JMX specific attributes Map<String,Integer> getNumActivePerKey(); + long getBorrowedCount(); + long getReturnedCount(); + long getCreatedCount(); + long getDestroyedCount(); + long getDestroyedByEvictorCount(); + long getDestroyedByBorrowValidationCount(); + long getMeanActiveTimeMillis(); + long getMeanIdleTimeMillis(); + long getMeanBorrowWaitTimeMillis(); + long getMaxBorrowWaitTimeMillis(); } Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java?rev=1158831&r1=1158830&r2=1158831&view=diff ============================================================================== --- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java (original) +++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java Wed Aug 17 17:03:35 2011 @@ -18,7 +18,11 @@ package org.apache.commons.pool2.impl; import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Deque; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.TimerTask; @@ -205,6 +209,8 @@ public class GenericObjectPool<T> extend startEvictor(timeBetweenEvictionRunsMillis); + initStats(); + // JMX Registration if (config.isJmxEnabled()) { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); @@ -722,6 +728,7 @@ public class GenericObjectPool<T> extend boolean blockWhenExhausted = this.blockWhenExhausted; boolean create; + long waitTime = 0; while (p == null) { create = false; @@ -735,8 +742,10 @@ public class GenericObjectPool<T> extend if (borrowMaxWait < 1) { p = idleObjects.takeFirst(); } else { + waitTime = System.currentTimeMillis(); p = idleObjects.pollFirst(borrowMaxWait, TimeUnit.MILLISECONDS); + waitTime = System.currentTimeMillis() - waitTime; } } if (p == null) { @@ -788,6 +797,7 @@ public class GenericObjectPool<T> extend if (!validate) { try { destroy(p); + destroyedByBorrowValidationCount.incrementAndGet(); } catch (Exception e) { // Ignore - validation failure is more important } @@ -803,6 +813,20 @@ public class GenericObjectPool<T> extend } } + borrowedCount.incrementAndGet(); + synchronized (idleTimes) { + idleTimes.add(Long.valueOf(p.getIdleTimeMillis())); + idleTimes.poll(); + } + synchronized (waitTimes) { + waitTimes.add(Long.valueOf(waitTime)); + waitTimes.poll(); + } + synchronized (maxBorrowWaitTimeMillisLock) { + if (waitTime > maxBorrowWaitTimeMillis) { + maxBorrowWaitTimeMillis = waitTime; + } + } return p.getObject(); } @@ -834,6 +858,8 @@ public class GenericObjectPool<T> extend "Returned object not currently part of this pool"); } + long activeTime = p.getActiveTimeMillis(); + if (getTestOnReturn()) { if (!factory.validateObject(obj)) { try { @@ -841,6 +867,7 @@ public class GenericObjectPool<T> extend } catch (Exception e) { // TODO - Ignore? } + updateStatsReturn(activeTime); return; } } @@ -853,6 +880,7 @@ public class GenericObjectPool<T> extend } catch (Exception e) { // TODO - Ignore? } + updateStatsReturn(activeTime); return; } @@ -875,6 +903,15 @@ public class GenericObjectPool<T> extend idleObjects.addLast(p); } } + updateStatsReturn(activeTime); + } + + private void updateStatsReturn(long activeTime) { + returnedCount.incrementAndGet(); + synchronized (activeTimes) { + activeTimes.add(Long.valueOf(activeTime)); + activeTimes.poll(); + } } /** @@ -1040,6 +1077,7 @@ public class GenericObjectPool<T> extend (idleSoftEvictTime < underTest.getIdleTimeMillis() && getMinIdle() < idleObjects.size())) { destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); } else { if (testWhileIdle) { boolean active = false; @@ -1048,15 +1086,18 @@ public class GenericObjectPool<T> extend active = true; } catch (Exception e) { destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!factory.validateObject(underTest.getObject())) { destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); } else { try { factory.passivateObject(underTest.getObject()); } catch (Exception e) { destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); } } } @@ -1089,6 +1130,7 @@ public class GenericObjectPool<T> extend } PooledObject<T> p = new PooledObject<T>(t); + createdCount.incrementAndGet(); allObjects.put(t, p); return p; } @@ -1099,6 +1141,7 @@ public class GenericObjectPool<T> extend try { factory.destroyObject(toDestory.getObject()); } finally { + destroyedCount.incrementAndGet(); createCount.decrementAndGet(); } } @@ -1212,6 +1255,75 @@ public class GenericObjectPool<T> extend } } + //--- JMX specific attributes ---------------------------------------------- + + private void initStats() { + for (int i = 0; i < AVERAGE_TIMING_STATS_CACHE_SIZE; i++) { + activeTimes.add(null); + idleTimes.add(null); + waitTimes.add(null); + } + } + + private long getMeanFromStatsCache(Deque<Long> cache) { + List<Long> times = new ArrayList<Long>(AVERAGE_TIMING_STATS_CACHE_SIZE); + synchronized (cache) { + times.addAll(cache); + } + double result = 0; + int counter = 0; + Iterator<Long> iter = times.iterator(); + while (iter.hasNext()) { + Long time = iter.next(); + if (time != null) { + counter++; + result = result * ((counter - 1) / counter) + + time.longValue()/counter; + } + } + return (long) result; + } + + public long getBorrowedCount() { + return borrowedCount.get(); + } + + public long getReturnedCount() { + return returnedCount.get(); + } + + public long getCreatedCount() { + return createdCount.get(); + } + + public long getDestroyedCount() { + return destroyedCount.get(); + } + + public long getDestroyedByEvictorCount() { + return destroyedByEvictorCount.get(); + } + + public long getDestroyedByBorrowValidationCount() { + return destroyedByBorrowValidationCount.get(); + } + + public long getMeanActiveTimeMillis() { + return getMeanFromStatsCache(activeTimes); + } + + public long getMeanIdleTimeMillis() { + return getMeanFromStatsCache(idleTimes); + } + + public long getMeanBorrowWaitTimeMillis() { + return getMeanFromStatsCache(waitTimes); + } + + public long getMaxBorrowWaitTimeMillis() { + return maxBorrowWaitTimeMillis; + } + // --- inner classes ---------------------------------------------- /** @@ -1424,6 +1536,20 @@ public class GenericObjectPool<T> extend /** An iterator for {@link #idleObjects} that is used by the evictor. */ private Iterator<PooledObject<T>> evictionIterator = null; + // JMX specific attributes + private static final int AVERAGE_TIMING_STATS_CACHE_SIZE = 100; + private AtomicLong borrowedCount = new AtomicLong(0); + private AtomicLong returnedCount = new AtomicLong(0); + private AtomicLong createdCount = new AtomicLong(0); + private AtomicLong destroyedCount = new AtomicLong(0); + private AtomicLong destroyedByEvictorCount = new AtomicLong(0); + private AtomicLong destroyedByBorrowValidationCount = new AtomicLong(0); + private final Deque<Long> activeTimes = new LinkedList<Long>(); + private final Deque<Long> idleTimes = new LinkedList<Long>(); + private final Deque<Long> waitTimes = new LinkedList<Long>(); + private Object maxBorrowWaitTimeMillisLock = new Object(); + private volatile long maxBorrowWaitTimeMillis = 0; + private static final String ONAME_BASE = "org.apache.commoms.pool2:type=GenericObjectPool,name="; } Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPoolMBean.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPoolMBean.java?rev=1158831&r1=1158830&r2=1158831&view=diff ============================================================================== --- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPoolMBean.java (original) +++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPoolMBean.java Wed Aug 17 17:03:35 2011 @@ -33,4 +33,15 @@ public interface GenericObjectPoolMBean boolean getTestWhileIdle(); long getTimeBetweenEvictionRunsMillis(); boolean isClosed(); + // JMX specific attributes + long getBorrowedCount(); + long getReturnedCount(); + long getCreatedCount(); + long getDestroyedCount(); + long getDestroyedByEvictorCount(); + long getDestroyedByBorrowValidationCount(); + long getMeanActiveTimeMillis(); + long getMeanIdleTimeMillis(); + long getMeanBorrowWaitTimeMillis(); + long getMaxBorrowWaitTimeMillis(); } Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/PooledObject.java URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/PooledObject.java?rev=1158831&r1=1158830&r2=1158831&view=diff ============================================================================== --- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/PooledObject.java (original) +++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/PooledObject.java Wed Aug 17 17:03:35 2011 @@ -25,7 +25,8 @@ public class PooledObject<T> implements private T object = null; private volatile PooledObjectState state = PooledObjectState.IDLE; - private long lastActiveTime = System.currentTimeMillis(); + private long lastBorrowTime = System.currentTimeMillis(); + private long lastReturnTime = System.currentTimeMillis(); public PooledObject(T object) { this.object = object; @@ -40,19 +41,38 @@ public class PooledObject<T> implements } /** - * Obtain the time in milliseconds since this object was last active. + * Obtain the time in milliseconds that this object last spend in the the + * active state (it may still be active in which case subsequent calls will + * return an increased value). + */ + public long getActiveTimeMillis() { + if (lastReturnTime > lastBorrowTime) { + return lastReturnTime - lastBorrowTime; + } else { + return System.currentTimeMillis() - lastBorrowTime; + } + } + + /** + * Obtain the time in milliseconds that this object last spend in the the + * idle state (it may still be idle in which case subsequent calls will + * return an increased value). */ public long getIdleTimeMillis() { - return System.currentTimeMillis() - lastActiveTime; + return System.currentTimeMillis() - lastReturnTime; } - public long getLastActiveTime() { - return lastActiveTime; + public long getLastBorrowTime() { + return lastBorrowTime; + } + + public long getLastReturnTime() { + return lastReturnTime; } public int compareTo(PooledObject<T> other) { final long lastActiveDiff = - this.getLastActiveTime() - other.getLastActiveTime(); + this.getLastReturnTime() - other.getLastReturnTime(); if (lastActiveDiff == 0) { // make sure the natural ordering is consistent with equals // see java.lang.Comparable Javadocs @@ -104,6 +124,7 @@ public class PooledObject<T> implements public synchronized boolean allocate() { if (state == PooledObjectState.IDLE) { state = PooledObjectState.ALLOCATED; + lastBorrowTime = System.currentTimeMillis(); return true; } else if (state == PooledObjectState.MAINTAIN_EVICTION) { // TODO Allocate anyway and ignore eviction test @@ -118,7 +139,7 @@ public class PooledObject<T> implements public synchronized boolean deallocate() { if (state == PooledObjectState.ALLOCATED) { state = PooledObjectState.IDLE; - lastActiveTime = System.currentTimeMillis(); + lastReturnTime = System.currentTimeMillis(); return true; }