From efac0c9487154aafd3e344ab6d7737546264958f Mon Sep 17 00:00:00 2001 From: fhanik Date: Fri, 13 Nov 2009 21:53:13 +0000 Subject: [PATCH] Implement suspectTimeout to allow JMX notifications and log events to take place if a connection is checked out for too long. But don't abandon/close the connection. git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@836011 13f79535-47bb-0310-9956-ffa450edef68 --- modules/jdbc-pool/.classpath | 1 + modules/jdbc-pool/doc/jdbc-pool.xml | 17 ++++++++++-- .../apache/tomcat/jdbc/pool/ConnectionPool.java | 32 ++++++++++++++++++++-- .../apache/tomcat/jdbc/pool/DataSourceProxy.java | 17 ++++++++++++ .../apache/tomcat/jdbc/pool/PoolConfiguration.java | 19 +++++++++++++ .../apache/tomcat/jdbc/pool/PoolProperties.java | 26 ++++++++++++++++-- .../apache/tomcat/jdbc/pool/PooledConnection.java | 14 ++++++++++ .../jdbc/pool/interceptor/AbstractQueryReport.java | 6 ++-- .../tomcat/jdbc/pool/jmx/ConnectionPool.java | 19 ++++++++++++- 9 files changed, 140 insertions(+), 11 deletions(-) diff --git a/modules/jdbc-pool/.classpath b/modules/jdbc-pool/.classpath index 6983e0db6..ef54a92d6 100644 --- a/modules/jdbc-pool/.classpath +++ b/modules/jdbc-pool/.classpath @@ -6,5 +6,6 @@ + diff --git a/modules/jdbc-pool/doc/jdbc-pool.xml b/modules/jdbc-pool/doc/jdbc-pool.xml index c692de9a2..83e99b70c 100644 --- a/modules/jdbc-pool/doc/jdbc-pool.xml +++ b/modules/jdbc-pool/doc/jdbc-pool.xml @@ -78,13 +78,16 @@
  • Asynchronous connection retrieval - you can queue your request for a connection and receive a Future<Connection> back.
  • Better idle connection handling. Instead of closing connections directly, it can still pool connections and sizes the idle pool with a smarter algorithm.
  • You can decide at what moment connections are considered abandoned, is it when the pool is full, or directly at a timeout - by specifying a threshold. + by specifying a pool usage threshold.
  • The abandon connection timer will reset upon a statement/query activity. Allowing a connections that is in use for a long time to not timeout. This is achieved using the ResetAbandonedTimer
  • Close connections after they have been connected for a certain time. Age based close upon return to the pool. -
  • + +
  • Get JMX notifications and log entries when connections are suspected for being abandoned. This is similar to + the removeAbandonedTimeout but it doesn't take any action, only reports the information. + This is achieved using the suspectTimeout attribute.
  • @@ -388,6 +391,16 @@ The default value is true.

    + +

    (int) Timeout value in seconds. Default value is 0.
    + Similar to to the removeAbandonedTimeout value but instead of treating the connection + as abandoned, and potentially closing the connection, this simply logs the warning if + logAbandoned is set to true. If this value is equal or less than 0, no suspect + checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and + the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets + logged and a JMX notification gets sent once. +

    +
    diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java index 1e25d926e..f44c0a5e9 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java @@ -475,6 +475,32 @@ public class ConnectionPool { con.unlock(); } } + + /** + * thread safe way to abandon a connection + * signals a connection to be abandoned. + * this will disconnect the connection, and log the stack trace if logAbanded=true + * @param con PooledConnection + */ + protected void suspect(PooledConnection con) { + if (con == null) + return; + if (con.isSuspect()) + return; + try { + con.lock(); + String trace = con.getStackTrace(); + if (getPoolProperties().isLogAbandoned()) { + log.warn("Connection has been marked suspect, possibly abandoned " + con + "["+(System.currentTimeMillis()-con.getTimestamp())+" ms.]:" + trace); + } + if (jmxPool!=null) { + jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_ABANDONED_NOTIFICATION, trace); + } + con.setSuspect(true); + } finally { + con.unlock(); + } + } /** * thread safe way to release a connection @@ -786,8 +812,8 @@ public class ConnectionPool { public void checkAbandoned() { try { if (busy.size()==0) return; - if (!shouldAbandon()) return; Iterator locked = busy.iterator(); + int sto = getPoolProperties().getSuspectTimeout(); while (locked.hasNext()) { PooledConnection con = locked.next(); boolean setToNull = false; @@ -799,10 +825,12 @@ public class ConnectionPool { continue; long time = con.getTimestamp(); long now = System.currentTimeMillis(); - if ((now - time) > con.getAbandonTimeout()) { + if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) { busy.remove(con); abandon(con); setToNull = true; + } else if (sto > 0 && (now - time) > (sto*1000)) { + suspect(con); } else { //do nothing } //end if diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java index 093dd707a..1db507d29 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java @@ -516,6 +516,23 @@ public class DataSourceProxy implements PoolConfiguration { } + + /** + * {@inheritDoc} + */ + @Override + public int getSuspectTimeout() { + return getPoolProperties().getSuspectTimeout(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setSuspectTimeout(int seconds) { + getPoolProperties().setSuspectTimeout(seconds); + } + //=============================================================================== // Expose JMX attributes through Tomcat's dynamic reflection //=============================================================================== diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java index 667ae627b..d325323f0 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java @@ -698,5 +698,24 @@ public interface PoolConfiguration { * @param useLock set to true if a lock should be used on connection operations */ public void setUseLock(boolean useLock); + + /** + * Similar to {@link #setRemoveAbandonedTimeout(int)} but instead of treating the connection + * as abandoned, and potentially closing the connection, this simply logs the warning if + * {@link #isLogAbandoned()} returns true. If this value is equal or less than 0, no suspect + * checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and + * the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets + * logged and a JMX notification gets sent once. + * @param seconds - the amount of time in seconds that has to pass before a connection is marked suspect. + */ + public void setSuspectTimeout(int seconds); + + /** + * Returns the time in seconds to pass before a connection is marked an abanoned suspect. + * Any value lesser than or equal to 0 means the check is disabled. + * @return Returns the time in seconds to pass before a connection is marked an abanoned suspect. + */ + public int getSuspectTimeout(); + } \ No newline at end of file diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java index 0f5661705..62b31c556 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java @@ -74,6 +74,8 @@ public class PoolProperties implements PoolConfiguration { protected long maxAge = 0; protected boolean useLock = false; private InterceptorDefinition[] interceptors = null; + protected int suspectTimeout = 0; + /** * {@inheritDoc} @@ -718,14 +720,32 @@ public class PoolProperties implements PoolConfiguration { return defaultReadOnly; } + + /** + * {@inheritDoc} + */ + @Override + public int getSuspectTimeout() { + return this.suspectTimeout; + } + + /** + * {@inheritDoc} + */ + @Override + public void setSuspectTimeout(int seconds) { + this.suspectTimeout = seconds; + } + /** * {@inheritDoc} */ @Override public boolean isPoolSweeperEnabled() { - boolean result = getTimeBetweenEvictionRunsMillis()>0; - result = result && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0); - result = result || (isTestWhileIdle() && getValidationQuery()!=null); + boolean timer = getTimeBetweenEvictionRunsMillis()>0; + boolean result = timer && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0); + result = result || (timer && getSuspectTimeout()>0); + result = result || (timer && isTestWhileIdle() && getValidationQuery()!=null); return result; } diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java index ba8edbf21..eff788dcd 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java @@ -111,6 +111,8 @@ public class PooledConnection { private AtomicBoolean released = new AtomicBoolean(false); + private volatile boolean suspect = false; + /** * Constructor * @param prop - pool properties @@ -390,6 +392,18 @@ public class PooledConnection { */ public void setTimestamp(long timestamp) { this.timestamp = timestamp; + setSuspect(false); + } + + + + + public boolean isSuspect() { + return suspect; + } + + public void setSuspect(boolean suspect) { + this.suspect = suspect; } /** diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java index 412885e01..31b7ed2d3 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java @@ -73,7 +73,7 @@ public abstract class AbstractQueryReport extends AbstractCreateStatementInterce * Invoked when a query execution, a call to execute/executeQuery or executeBatch failed. * @param query the query that was executed and failed * @param args the arguments to the execution - * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#executes} + * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)} * @param start the time the query execution started * @param t the exception that happened * @return - the SQL that was executed or the string "batch" if it was a batch execution @@ -92,7 +92,7 @@ public abstract class AbstractQueryReport extends AbstractCreateStatementInterce * Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was within the timing threshold * @param query the query that was executed and failed * @param args the arguments to the execution - * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#executes} + * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)} * @param start the time the query execution started * @param delta the time the execution took * @return - the SQL that was executed or the string "batch" if it was a batch execution @@ -111,7 +111,7 @@ public abstract class AbstractQueryReport extends AbstractCreateStatementInterce * Invoked when a query execution, a call to execute/executeQuery or executeBatch succeeded and was exceeded the timing threshold * @param query the query that was executed and failed * @param args the arguments to the execution - * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#executes} + * @param name the name of the method used to execute {@link AbstractCreateStatementInterceptor#isExecute(Method, boolean)} * @param start the time the query execution started * @param delta the time the execution took * @return - the SQL that was executed or the string "batch" if it was a batch execution diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java index fbfb21b8c..d53e491f9 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java @@ -72,6 +72,7 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co public static final String NOTIFY_ABANDON = "CONNECTION ABANDONED"; public static final String SLOW_QUERY_NOTIFICATION = "SLOW QUERY"; public static final String FAILED_QUERY_NOTIFICATION = "FAILED QUERY"; + public static final String SUSPECT_ABANDONED_NOTIFICATION = "SUSPECT CONNETION ABANDONED"; @@ -86,7 +87,7 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co } public static MBeanNotificationInfo[] getDefaultNotificationInfo() { - String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION, FAILED_QUERY_NOTIFICATION}; + String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION, FAILED_QUERY_NOTIFICATION, SUSPECT_ABANDONED_NOTIFICATION}; String name = Notification.class.getName(); String description = "A connection pool error condition was met."; MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description); @@ -553,5 +554,21 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co // TODO Auto-generated method stub } + + /** + * {@inheritDoc} + */ + @Override + public int getSuspectTimeout() { + return getPoolProperties().getSuspectTimeout(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setSuspectTimeout(int seconds) { + //no op + } } -- 2.11.0