From: fhanik Date: Thu, 30 Apr 2009 19:41:07 +0000 (+0000) Subject: Add the following features X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=82d2f17448db2c1e569802e4ab1ba35db30ac1fb;p=tomcat7.0 Add the following features - max age for a connection kept alive - an ability to reset the abandon timer when queries are executed - an abandoned test, abandon when a percentage of the pool has been utilized git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@770411 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/jdbc-pool/doc/jdbc-pool.xml b/modules/jdbc-pool/doc/jdbc-pool.xml index 84cf4eac9..a0a54bfb7 100644 --- a/modules/jdbc-pool/doc/jdbc-pool.xml +++ b/modules/jdbc-pool/doc/jdbc-pool.xml @@ -73,6 +73,15 @@ that has over 200 source files(last time we checked), Tomcat jdbc has a core of 8 files, the connection pool itself is about half that. As bugs may occur, they will be faster to track down, and easier to fix. Complexity reduction has been a focus from inception.
  • 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. +
  • +
  • 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. +
  • @@ -338,6 +347,14 @@ as removeAbandonedTimeout has been reached.

    + +

    (long) Time in milliseconds to keep this connection. When a connection is returned to the pool, + the pool will check to see if the now - time-when-connected > maxAge has been reached, + and if so, it closes the connection rather than returning it to the pool. + The default value is 0, which implies that connections will be left open and no age check + will be done upon returning the connection to the pool.

    +
    +

    (boolean) Set to true if you wish the ProxyConnection class to use String.equals instead of == when comparing method names. This property does not apply to added interceptors as those are configured individually. 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 f5a39a255..1989d5e88 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 @@ -609,6 +609,17 @@ public class ConnectionPool { } } + protected boolean shouldClose(PooledConnection con, int action) { + if (con.isDiscarded()) return true; + if (isClosed()) return true; + if (!con.validate(action)) return true; + if (getPoolProperties().getMaxAge()>0 ) { + return (System.currentTimeMillis()-con.getLastConnected()) > getPoolProperties().getMaxAge(); + } else { + return false; + } + } + /** * Returns a connection to the pool * @param con PooledConnection @@ -626,8 +637,8 @@ public class ConnectionPool { con.lock(); if (busy.remove(con)) { - if ((!con.isDiscarded()) && (!isClosed()) && - con.validate(PooledConnection.VALIDATE_RETURN)) { + + if (!shouldClose(con,PooledConnection.VALIDATE_RETURN)) { con.setStackTrace(null); con.setTimestamp(System.currentTimeMillis()); if (((idle.size()>=poolProperties.getMaxIdle()) && !poolProperties.isPoolSweeperEnabled()) || (!idle.offer(con))) { @@ -654,12 +665,11 @@ public class ConnectionPool { } //end if } //checkIn - public boolean shouldAbandon() { + protected boolean shouldAbandon() { if (poolProperties.getAbandonWhenPercentageFull()==0) return true; float used = (float)busy.size(); float max = (float)poolProperties.getMaxActive(); float perc = (float)poolProperties.getAbandonWhenPercentageFull(); - System.out.println("Abandon rate:"+(used/max*100f)); return (used/max*100f)>=perc; } diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java index 65eb82ddd..c4cc4af8a 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSource.java @@ -249,6 +249,14 @@ public class DataSource extends DataSourceProxy implements MBeanRegistration,jav throw new RuntimeException(x); } } + + public long getMaxAge() { + try { + return createPool().getPoolProperties().getMaxAge(); + }catch (SQLException x) { + throw new RuntimeException(x); + } + } public String getName() { try { diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java index ac09e202c..54e45c858 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java @@ -78,6 +78,7 @@ public class DataSourceFactory implements ObjectFactory { protected final static String PROP_MINIDLE = "minIdle"; protected final static String PROP_INITIALSIZE = "initialSize"; protected final static String PROP_MAXWAIT = "maxWait"; + protected final static String PROP_MAXAGE = "maxAge"; protected final static String PROP_TESTONBORROW = "testOnBorrow"; protected final static String PROP_TESTONRETURN = "testOnReturn"; @@ -149,7 +150,8 @@ public class DataSourceFactory implements ObjectFactory { PROP_FAIR_QUEUE, PROP_USE_EQUALS, OBJECT_NAME, - PROP_ABANDONWHENPERCENTAGEFULL + PROP_ABANDONWHENPERCENTAGEFULL, + PROP_MAXAGE }; // -------------------------------------------------- ObjectFactory Methods @@ -417,6 +419,11 @@ public class DataSourceFactory implements ObjectFactory { poolProperties.setAbandonWhenPercentageFull(Integer.parseInt(value)); } + value = properties.getProperty(PROP_MAXAGE); + if (value != null) { + poolProperties.setMaxAge(Long.parseLong(value)); + } + return poolProperties; } 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 37e01d244..9d47b9b02 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 @@ -71,6 +71,7 @@ public class PoolProperties { protected boolean fairQueue = true; protected boolean useEquals = false; protected int abandonWhenPercentageFull = 0; + protected long maxAge = 0; private InterceptorDefinition[] interceptors = null; @@ -523,4 +524,14 @@ public class PoolProperties { this.useEquals = useEquals; } + public long getMaxAge() { + return maxAge; + } + + public void setMaxAge(long maxAge) { + this.maxAge = maxAge; + } + + + } 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 bc051d342..429e6af1a 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 @@ -86,6 +86,10 @@ public class PooledConnection { */ protected boolean discarded = false; /** + * The Timestamp when the last time the connect() method was called successfully + */ + protected volatile long lastConnected = -1; + /** * timestamp to keep track of validation intervals */ protected long lastValidated = System.currentTimeMillis(); @@ -163,6 +167,7 @@ public class PooledConnection { if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation()); } this.discarded = false; + this.lastConnected = System.currentTimeMillis(); } /** @@ -193,6 +198,7 @@ public class PooledConnection { } } connection = null; + lastConnected = -1; if (finalize) parent.finalize(this); } @@ -381,6 +387,12 @@ public class PooledConnection { public java.sql.Connection getConnection() { return this.connection; } + + + + public long getLastConnected() { + return lastConnected; + } /** * Returns the first handler in the interceptor chain 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 cdc18a1fc..577d887fd 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 @@ -276,5 +276,8 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co public int getAbandonWhenPercentageFull() { return pool.getPoolProperties().getAbandonWhenPercentageFull(); } + public long getMaxAge() { + return pool.getPoolProperties().getMaxAge(); + } } diff --git a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java index 1b8711bfa..aab9f24ad 100644 --- a/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java +++ b/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java @@ -118,4 +118,6 @@ public interface ConnectionPoolMBean { public int getAbandonWhenPercentageFull(); + public long getMaxAge(); + } diff --git a/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java b/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java new file mode 100644 index 000000000..3f30b6be1 --- /dev/null +++ b/modules/jdbc-pool/test/org/apache/tomcat/jdbc/test/AbandonPercentageTest.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomcat.jdbc.test; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer; + +public class AbandonPercentageTest extends DefaultTestCase { + + public AbandonPercentageTest(String name) { + super(name); + } + + public void testDefaultAbandon() throws Exception { + this.init(); + this.datasource.setMaxActive(100); + this.datasource.setMaxIdle(100); + this.datasource.setInitialSize(0); + this.datasource.getPoolProperties().setAbandonWhenPercentageFull(0); + this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100); + this.datasource.getPoolProperties().setRemoveAbandoned(true); + this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1); + Connection con = datasource.getConnection(); + long start = System.currentTimeMillis(); + assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive()); + Thread.sleep(2000); + assertEquals("Number of connections active/busy should be 0",0,datasource.getPool().getActive()); + con.close(); + } + + public void testMaxedOutAbandon() throws Exception { + int size = 100; + this.init(); + this.datasource.setMaxActive(size); + this.datasource.setMaxIdle(size); + this.datasource.setInitialSize(0); + this.datasource.getPoolProperties().setAbandonWhenPercentageFull(100); + this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100); + this.datasource.getPoolProperties().setRemoveAbandoned(true); + this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1); + Connection con = datasource.getConnection(); + long start = System.currentTimeMillis(); + assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive()); + Thread.sleep(2000); + assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive()); + con.close(); + } + + public void testResetConnection() throws Exception { + int size = 1; + this.init(); + this.datasource.setMaxActive(size); + this.datasource.setMaxIdle(size); + this.datasource.setInitialSize(0); + this.datasource.getPoolProperties().setAbandonWhenPercentageFull(100); + this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(100); + this.datasource.getPoolProperties().setRemoveAbandoned(true); + this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1); + this.datasource.getPoolProperties().setJdbcInterceptors(ResetAbandonedTimer.class.getName()); + Connection con = datasource.getConnection(); + long start = System.currentTimeMillis(); + assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive()); + for (int i=0; i<20; i++) { + Thread.sleep(200); + con.isClosed(); + } + assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive()); + con.close(); + } + + public void testHalfway() throws Exception { + int size = 100; + this.init(); + this.datasource.setMaxActive(size); + this.datasource.setMaxIdle(size); + this.datasource.setInitialSize(0); + this.datasource.getPoolProperties().setAbandonWhenPercentageFull(50); + this.datasource.getPoolProperties().setTimeBetweenEvictionRunsMillis(500); + this.datasource.getPoolProperties().setRemoveAbandoned(true); + this.datasource.getPoolProperties().setRemoveAbandonedTimeout(1); + Connection[] con = new Connection[size]; + con[0] = datasource.getConnection(); + long start = System.currentTimeMillis(); + assertEquals("Number of connections active/busy should be 1",1,datasource.getPool().getActive()); + for (int i=1; i<25; i++) { + con[i] = datasource.getConnection(); + } + assertEquals("Number of connections active/busy should be 25",25,datasource.getPool().getActive()); + Thread.sleep(2500); + assertEquals("Number of connections active/busy should be 25",25,datasource.getPool().getActive()); + for (int i=25; i