Author: sandymac
Date: Sat Mar 25 23:13:22 2006
New Revision: 388880

URL: http://svn.apache.org/viewcvs?rev=388880&view=rev
Log:
Detect when making new objects is relatively slow or expensive.
When it is, try to anticipate demand and create idle objects
while allowing concurrent pool access. The idea came from a
discussion with Peter Steijn on ways to reduce pool latency.

Modified:
    
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/CompositeObjectPool.java
    
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/GrowManager.java

Modified: 
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/CompositeObjectPool.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/CompositeObjectPool.java?rev=388880&r1=388879&r2=388880&view=diff
==============================================================================
--- 
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/CompositeObjectPool.java
 (original)
+++ 
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/CompositeObjectPool.java
 Sat Mar 25 23:13:22 2006
@@ -179,10 +179,10 @@
      */
     public void addObject() throws Exception {
         assertOpen();
+        final Object obj = factory.makeObject();
+        factory.passivateObject(obj);
         synchronized (pool) {
-            final Object obj = factory.makeObject();
-            factory.passivateObject(obj);
-            // if the pool is closed, discard returned objects
+            // if the pool was closed between the asserOpen and the 
synchronize then discard returned objects
             if (isOpen()) {
                 manager.returnToPool(obj);
             } else {

Modified: 
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/GrowManager.java
URL: 
http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/GrowManager.java?rev=388880&r1=388879&r2=388880&view=diff
==============================================================================
--- 
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/GrowManager.java
 (original)
+++ 
jakarta/commons/proper/pool/trunk/src/java/org/apache/commons/pool/composite/GrowManager.java
 Sat Mar 25 23:13:22 2006
@@ -19,6 +19,8 @@
 import org.apache.commons.pool.PoolableObjectFactory;
 
 import java.io.Serializable;
+import java.util.TimerTask;
+import java.util.Timer;
 
 /**
  * Grows the pool automatically when it is exhausted.
@@ -33,6 +35,11 @@
 final class GrowManager extends AbstractManager implements Serializable {
 
     private static final long serialVersionUID = 1225746308358794900L;
+    private static final Timer PREFILL_TIMER = 
CompositeObjectPool.COMPOSITE_TIMER;
+
+    private final Object avgLock = new Object();
+    private long activateAvg = 0;
+    private long makeAvg = 0;
 
     /**
      * Retreives the next object from the pool, creating new objects if the 
pool has been exhausted.
@@ -43,6 +50,8 @@
     public Object nextFromPool() throws Exception {
         assert Thread.holdsLock(objectPool.getPool());
         Object obj = null;
+
+        final long startActivateTime = System.currentTimeMillis();
         // Drain until good or empty
         while (objectPool.getLender().size() > 0 && obj == null) {
             obj = objectPool.getLender().borrow();
@@ -65,10 +74,15 @@
                 }
             }
         }
-
-        if (obj == null) {
+        if (obj != null) {
+            updateActivateTimings(startActivateTime, 
System.currentTimeMillis());
+        } else {
+            final long startMakeTime = System.currentTimeMillis();
             obj = objectPool.getFactory().makeObject();
+            updateMakeTimings(startMakeTime, System.currentTimeMillis());
         }
+
+        schedulePrefill();
         return obj;
     }
 
@@ -93,7 +107,74 @@
         return obj;
     }
 
+    /**
+     * Update the moving average of how long it takes to activate and validate 
idle objects.
+     * @param start start of activation and validation
+     * @param end end of activation and validation
+     */
+    private void updateActivateTimings(final long start, final long end) {
+        final long elapsed = end - start;
+        if (elapsed > 0L) {
+            synchronized (avgLock) {
+                activateAvg = (activateAvg * 9L + elapsed) / 10L;
+            }
+        }
+    }
+
+    /**
+     * Update the moving average of how long it takes to make a new objects.
+     * @param start start of makeObject
+     * @param end end of makeObject
+     */
+    private void updateMakeTimings(final long start, final long end) {
+        final long elapsed = end - start;
+        if (elapsed > 0L) {
+            synchronized (avgLock) {
+                makeAvg = (makeAvg * 9L + elapsed) / 10L;
+            }
+        }
+    }
+
+    /**
+     * Does [EMAIL PROTECTED] PoolableObjectFactory#makeObject} take a 
relativly long time compared to
+     * [EMAIL PROTECTED] PoolableObjectFactory#activateObject} and [EMAIL 
PROTECTED] PoolableObjectFactory#validateObject}.
+     * @return <code>true</code> if [EMAIL PROTECTED] 
PoolableObjectFactory#makeObject} takes a long time.
+     */
+    private boolean isMakeObjectExpensive() {
+        synchronized (avgLock) {
+            // XXX: This is a guess at an optimal balance.
+            // Considering makeObject  to be expensive if it takes 3 times 
longer than activation takes.
+            // That is based on a benchmark by Peter Steijn for database 
connection pooling.
+            return activateAvg > 0L && activateAvg * 3L < makeAvg;
+        }
+    }
+
+    /**
+     * Schedule a <code>PrefillTask</code> if the idle object pool is empty
+     * and <code>makeObject</code> is relatively expensive.
+     */
+    private void schedulePrefill() {
+        if (objectPool.getLender().size() == 0 && isMakeObjectExpensive()) {
+            PREFILL_TIMER.schedule(new PrefillTask(), 0L);
+        }
+    }
+
     public String toString() {
-        return "GrowManager{}";
+        return "GrowManager{makeObjectExpensive=" + isMakeObjectExpensive() + 
"}";
+    }
+
+    /**
+     * A <code>TimerTask</code> that will add another object if the pool is 
empty.
+     */
+    private class PrefillTask extends TimerTask {
+        public void run() {
+            try {
+                if (objectPool.getNumIdle() == 0) {
+                    objectPool.addObject();
+                }
+            } catch (Exception e) {
+                // swallowed
+            }
+        }
     }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to