Author: fhanik
Date: Fri Feb 6 21:49:36 2009
New Revision: 741747
URL: http://svn.apache.org/viewvc?rev=741747&view=rev
Log:
Make sure thread starvation doesn't exist during reclamation of connections
Added:
tomcat/trunk/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/StarvationTest.java
(with props)
Modified:
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
Modified:
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java?rev=741747&r1=741746&r2=741747&view=diff
==============================================================================
---
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
(original)
+++
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
Fri Feb 6 21:49:36 2009
@@ -98,7 +98,12 @@
* reference to mbean
*/
protected org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmxPool = null;
-
+
+ /**
+ * counter to track how many threads are waiting for a connection
+ */
+ protected AtomicInteger waitcount = new AtomicInteger(0);
+
//===============================================================================
// PUBLIC METHODS
//===============================================================================
@@ -142,6 +147,14 @@
public String getName() {
return getPoolProperties().getPoolName();
}
+
+ /**
+ * Return the number of threads waiting for a connection
+ * @return number of threads waiting for a connection
+ */
+ public int getWaitCount() {
+ return waitcount.get();
+ }
/**
* Returns the pool properties associated with this connection pool
@@ -397,6 +410,9 @@
jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_ABANDON,
trace);
}
con.abandon();
+ //we've asynchronously reduced the number of connections
+ //we could have threads stuck in idle.poll(timeout) that will
never be notified
+ if (waitcount.get()>0) idle.offer(new
PooledConnection(poolProperties,this));
} finally {
con.unlock();
}
@@ -457,11 +473,17 @@
maxWait =
(getPoolProperties().getMaxWait()<=0)?Long.MAX_VALUE:getPoolProperties().getMaxWait();
}
long timetowait = Math.max(0, maxWait -
(System.currentTimeMillis() - now));
+ waitcount.incrementAndGet();
try {
//retrieve an existing connection
con = idle.poll(timetowait, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
- Thread.interrupted();
+ Thread.interrupted();//clear the flag, and bail out
+ SQLException sx = new SQLException("Pool wait interrupted.");
+ sx.initCause(ex);
+ throw sx;
+ } finally {
+ waitcount.decrementAndGet();
}
if (maxWait==0 && con == null) { //no wait, return one if we have
one
throw new SQLException("[" +
Thread.currentThread().getName()+"] " +
@@ -529,6 +551,9 @@
boolean setToNull = false;
try {
con.lock();
+ if (!con.isDiscarded() && !con.isInitialized()) {
+ con.connect();
+ }
if ((!con.isDiscarded()) &&
con.validate(PooledConnection.VALIDATE_BORROW)) {
//set the timestamp
con.setTimestamp(now);
@@ -647,7 +672,6 @@
if ((now - time) > con.getAbandonTimeout()) {
busy.remove(con);
abandon(con);
- release(con);
setToNull = true;
} else {
//do nothing
Modified:
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java?rev=741747&r1=741746&r2=741747&view=diff
==============================================================================
---
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
(original)
+++
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
Fri Feb 6 21:49:36 2009
@@ -53,8 +53,8 @@
protected ConnectionPool parent;
protected WeakReference<JdbcInterceptor> handler = null;
-
- public PooledConnection(PoolProperties prop, ConnectionPool parent) throws
SQLException {
+
+ public PooledConnection(PoolProperties prop, ConnectionPool parent) {
instanceCount = counter.addAndGet(1);
poolProperties = prop;
this.parent = parent;
@@ -112,6 +112,10 @@
}
this.discarded = false;
}
+
+ public boolean isInitialized() {
+ return connection!=null;
+ }
protected void reconnect() throws SQLException {
this.disconnect(false);
Modified:
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java?rev=741747&r1=741746&r2=741747&view=diff
==============================================================================
---
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
(original)
+++
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
Fri Feb 6 21:49:36 2009
@@ -233,5 +233,8 @@
public String getJdbcInterceptors() {
return pool.getPoolProperties().getJdbcInterceptors();
}
+ public int getWaitCount() {
+ return pool.getWaitCount();
+ }
}
Added:
tomcat/trunk/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/StarvationTest.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/StarvationTest.java?rev=741747&view=auto
==============================================================================
---
tomcat/trunk/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/StarvationTest.java
(added)
+++
tomcat/trunk/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/StarvationTest.java
Fri Feb 6 21:49:36 2009
@@ -0,0 +1,96 @@
+package org.apache.tomcat.jdbc.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.tomcat.jdbc.test.CheckOutThreadTest.TestThread;
+
+/**
+ * If a connection is abandoned and closed,
+ * then that should free up a spot in the pool, and other threads
+ * that are waiting should not time out and throw an error but be
+ * able to acquire a connection, since one was just released.
+ * @author fhanik
+ *
+ */
+public class StarvationTest extends DefaultTestCase {
+
+ public StarvationTest(String name) {
+ super(name);
+ }
+
+ private void config() {
+ datasource.getPoolProperties().setMaxActive(1);
+ datasource.getPoolProperties().setMaxIdle(1);
+ datasource.getPoolProperties().setInitialSize(1);
+ datasource.getPoolProperties().setRemoveAbandoned(true);
+ datasource.getPoolProperties().setRemoveAbandonedTimeout(5);
+ datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(500);
+ datasource.getPoolProperties().setMaxWait(10000);
+ }
+
+ public void testDBCPConnectionStarvation() throws Exception {
+ init();
+ config();
+ this.transferProperties();
+ this.tDatasource.getConnection().close();
+ javax.sql.DataSource datasource = this.tDatasource;
+ Connection con1 = datasource.getConnection();
+ Connection con2 = null;
+ try {
+ con2 = datasource.getConnection();
+ try {
+ con2.setCatalog("mysql");//make sure connection is valid
+ }catch (SQLException x) {
+ assertFalse("2nd Connection is not
valid:"+x.getMessage(),true);
+ }
+ assertTrue("Connection 1 should be closed.",con1.isClosed());
//first connection should be closed
+ }catch (Exception x) {
+ assertFalse("Connection got starved:"+x.getMessage(),true);
+ }finally {
+ if (con2!=null) con2.close();
+ }
+ }
+
+ public void testConnectionStarvation() throws Exception {
+ init();
+ config();
+ Connection con1 = datasource.getConnection();
+ Connection con2 = null;
+ try {
+ con2 = datasource.getConnection();
+ try {
+ con2.setCatalog("mysql");//make sure connection is valid
+ }catch (SQLException x) {
+ assertFalse("2nd Connection is not
valid:"+x.getMessage(),true);
+ }
+ assertTrue("Connection 1 should be closed.",con1.isClosed());
//first connection should be closed
+ }catch (Exception x) {
+ assertFalse("Connection got starved:"+x.getMessage(),true);
+ }finally {
+ if (con2!=null) con2.close();
+ }
+ }
+
+ public void testFairConnectionStarvation() throws Exception {
+ init();
+ config();
+ datasource.getPoolProperties().setFairQueue(true);
+ Connection con1 = datasource.getConnection();
+ Connection con2 = null;
+ try {
+ con2 = datasource.getConnection();
+ try {
+ con2.setCatalog("mysql");//make sure connection is valid
+ }catch (SQLException x) {
+ assertFalse("2nd Connection is not
valid:"+x.getMessage(),true);
+ }
+ assertTrue("Connection 1 should be closed.",con1.isClosed());
//first connection should be closed
+ }catch (Exception x) {
+ assertFalse("Connection got starved:"+x.getMessage(),true);
+ }finally {
+ if (con2!=null) con2.close();
+ }
+ }
+}
Propchange:
tomcat/trunk/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/StarvationTest.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]