diff --git a/src/main/java/com/alibaba/druid/mock/MockConnection.java b/src/main/java/com/alibaba/druid/mock/MockConnection.java index 362a69f12e..dff53f920c 100644 --- a/src/main/java/com/alibaba/druid/mock/MockConnection.java +++ b/src/main/java/com/alibaba/druid/mock/MockConnection.java @@ -38,12 +38,12 @@ import com.alibaba.druid.logging.LogFactory; public class MockConnection implements Connection { - private final static Log LOG = LogFactory.getLog(MockConnection.class); + private final static Log LOG = LogFactory.getLog(MockConnection.class); - private boolean autoCommit = false; - private boolean closed = false; - private boolean readOnly = false; - private String catalog = null; + private boolean autoCommit = false; + private boolean closed = false; + private boolean readOnly = false; + private String catalog = null; private int transactionIsolation; private SQLWarning warning; private int holdability; @@ -54,10 +54,25 @@ public class MockConnection implements Connection { private long id; + private final long createdTimeMillis = System.currentTimeMillis(); + private long lastActiveTimeMillis = System.currentTimeMillis(); + public MockConnection(){ this(null); } + public long getLastActiveTimeMillis() { + return lastActiveTimeMillis; + } + + public void setLastActiveTimeMillis(long lastActiveTimeMillis) { + this.lastActiveTimeMillis = lastActiveTimeMillis; + } + + public long getCreatedTimeMillis() { + return createdTimeMillis; + } + public MockConnection(MockDriver driver){ this(driver, new Properties()); } diff --git a/src/main/java/com/alibaba/druid/mock/MockDriver.java b/src/main/java/com/alibaba/druid/mock/MockDriver.java index 05d44f400d..687bd3245d 100644 --- a/src/main/java/com/alibaba/druid/mock/MockDriver.java +++ b/src/main/java/com/alibaba/druid/mock/MockDriver.java @@ -55,10 +55,20 @@ public class MockDriver implements Driver { private final List connections = new CopyOnWriteArrayList(); + private long idleTimeCount = 1000 * 60 * 3; + static { registerDriver(instance); } + public long getIdleTimeCount() { + return idleTimeCount; + } + + public void setIdleTimeCount(long idleTimeCount) { + this.idleTimeCount = idleTimeCount; + } + public long generateConnectionId() { return connectionIdSeed.incrementAndGet(); } @@ -192,6 +202,13 @@ public class MockDriver implements Driver { MockConnection conn = stmt.getMockConnection(); + long idleTimeMillis = System.currentTimeMillis() - conn.getLastActiveTimeMillis(); + if (idleTimeMillis >= this.idleTimeCount) { + throw new SQLException("connection is idle time count"); + } + + conn.setLastActiveTimeMillis(System.currentTimeMillis()); + if (conn != null) { if (conn.getConnectProperties() != null) { Object propertyValue = conn.getConnectProperties().get("executeSleep"); diff --git a/src/main/java/com/alibaba/druid/pool/ConnectionHolder.java b/src/main/java/com/alibaba/druid/pool/ConnectionHolder.java index f67cc9ec80..b6f564d1db 100644 --- a/src/main/java/com/alibaba/druid/pool/ConnectionHolder.java +++ b/src/main/java/com/alibaba/druid/pool/ConnectionHolder.java @@ -36,7 +36,8 @@ public final class ConnectionHolder { private final List connectionEventListeners = new CopyOnWriteArrayList(); private final List statementEventListeners = new CopyOnWriteArrayList(); private final long connecttimeMillis; - private long lastActiveMillis; + private long lastActiveTimeMillis; + private long lastCheckTimeMillis = 0; private long useCount = 0; private final boolean poolPreparedStatements; @@ -49,7 +50,7 @@ public final class ConnectionHolder { this.conn = conn; this.poolPreparedStatements = dataSource.isPoolPreparedStatements(); this.connecttimeMillis = System.currentTimeMillis(); - this.lastActiveMillis = connecttimeMillis; + this.lastActiveTimeMillis = connecttimeMillis; if (this.poolPreparedStatements) { statementPool = new PreparedStatementPool(); @@ -58,12 +59,20 @@ public final class ConnectionHolder { } } - public long getLastActiveMillis() { - return lastActiveMillis; + public long getLastCheckTimeMillis() { + return lastCheckTimeMillis; } - public void setLastActiveMillis(long lastActiveMillis) { - this.lastActiveMillis = lastActiveMillis; + public void setLastCheckTimeMillis(long lastCheckTimeMillis) { + this.lastCheckTimeMillis = lastCheckTimeMillis; + } + + public long getLastActiveTimeMillis() { + return lastActiveTimeMillis; + } + + public void setLastActiveTimeMillis(long lastActiveMillis) { + this.lastActiveTimeMillis = lastActiveMillis; } public void addTrace(Statement stmt) { diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 66bf808744..dfbd217eaa 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -41,7 +41,6 @@ import com.alibaba.druid.filter.Filter; import com.alibaba.druid.filter.FilterChainImpl; import com.alibaba.druid.pool.DruidDataSource.ActiveConnectionTraceInfo; import com.alibaba.druid.pool.vendor.NullExceptionSorter; -import com.alibaba.druid.proxy.jdbc.ConnectionProxy; import com.alibaba.druid.proxy.jdbc.DataSourceProxy; import com.alibaba.druid.util.ConcurrentIdentityHashMap; import com.alibaba.druid.util.DruidLoaderUtils; @@ -744,18 +743,6 @@ public abstract class DruidAbstractDataSource implements DruidAbstractDataSource } } - public boolean testConnection(Connection conn) { - if (conn instanceof PoolableConnection) { - conn = ((PoolableConnection) conn).getConnection(); - } - - if (conn instanceof ConnectionProxy) { - conn = ((ConnectionProxy) conn).getConnectionRaw(); - } - - return testConnectionInternal(conn); - } - protected boolean testConnectionInternal(Connection conn) { try { if (validConnectionChecker != null) { diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 58c0edf8ff..73749c6127 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -266,6 +266,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat JdbcUtils.close(realConnection); continue; } + poolalbeConnection.getConnectionHolder().setLastCheckTimeMillis(System.currentTimeMillis()); } else { Connection realConnection = poolalbeConnection.getConnection(); if (realConnection.isClosed()) { @@ -395,6 +396,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } return; } + holder.setLastCheckTimeMillis(System.currentTimeMillis()); } lock.lockInterruptibly(); @@ -480,7 +482,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat throw new NullPointerException(); } - e.setLastActiveMillis(System.currentTimeMillis()); + e.setLastActiveTimeMillis(System.currentTimeMillis()); connections[count++] = e; notEmpty.signal(); @@ -716,7 +718,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat continue FOR_0; } - long idleMillis = System.currentTimeMillis() - connection.getLastActiveMillis(); + long idleMillis = System.currentTimeMillis() - connection.getLastActiveTimeMillis(); if (idleMillis >= minEvictableIdleTimeMillis && count - evictList.size() > minIdle) { evictList.add(connection); } else { @@ -735,6 +737,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (!testConnectionInternal(idleConnection.getConnection())) { evictList.add(idleConnection); } else { + idleConnection.setLastCheckTimeMillis(System.currentTimeMillis()); checkList.add(idleConnection); } } @@ -751,7 +754,20 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat lock.unlock(); } if (LOG.isDebugEnabled()) { - LOG.debug("checked " + size); + StringBuilder buf = new StringBuilder(); + buf.append("checked "); + buf.append(size); + buf.append(", idleCount "); + buf.append(count); + buf.append(", ["); + for (int i = 0; i < size; ++i) { + if (i != 0) { + buf.append(","); + } + buf.append(System.identityHashCode(checkList.get(i))); + } + buf.append("]"); + LOG.debug(buf.toString()); } } @@ -761,6 +777,23 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat destroyCount++; } + if (evictList.size() > 0) { + if (LOG.isDebugEnabled()) { + StringBuilder buf = new StringBuilder(); + buf.append("evict "); + buf.append(evictList.size()); + buf.append(", ["); + for (int i = 0; i < size; ++i) { + if (i != 0) { + buf.append(","); + } + buf.append(System.identityHashCode(checkList.get(i))); + } + buf.append("]"); + LOG.debug(buf.toString()); + } + } + idleList.clear(); checkList.clear(); evictList.clear(); diff --git a/src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle.java b/src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle_1.java similarity index 97% rename from src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle.java rename to src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle_1.java index 68ff201dba..04bbe96a0f 100644 --- a/src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle.java +++ b/src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle_1.java @@ -13,7 +13,7 @@ import junit.framework.TestCase; import com.alibaba.druid.TestUtil; import com.alibaba.druid.pool.DruidDataSource; -public class CaseKylin_mysql_idle extends TestCase { +public class CaseKylin_mysql_idle_1 extends TestCase { private String jdbcUrl; private String user; @@ -27,7 +27,7 @@ public class CaseKylin_mysql_idle extends TestCase { private String validationQuery = null; // "SELECT 1"; private int threadCount = 15; private int TEST_COUNT = 3; - final int LOOP_COUNT = 1000 * 1; + final int LOOP_COUNT = 1000 * 10; private boolean testWhileIdle = true; private boolean testOnBorrow = false; private boolean testOnReturn = false; diff --git a/src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle_2.java b/src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle_2.java new file mode 100644 index 0000000000..8d53874160 --- /dev/null +++ b/src/test/java/com/alibaba/druid/pool/benckmark/CaseKylin_mysql_idle_2.java @@ -0,0 +1,143 @@ +package com.alibaba.druid.pool.benckmark; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.text.NumberFormat; +import java.util.concurrent.CountDownLatch; + +import javax.sql.DataSource; + +import junit.framework.TestCase; + +import com.alibaba.druid.TestUtil; +import com.alibaba.druid.mock.MockDriver; +import com.alibaba.druid.pool.DruidDataSource; + +public class CaseKylin_mysql_idle_2 extends TestCase { + + private String jdbcUrl; + private String user; + private String password; + private String driverClass; + private int initialSize = 1; + private int minIdle = 1; + private int maxIdle = 20; + private int maxActive = 20; + private int maxWait = 60000; + private String validationQuery = null; // "SELECT 1"; + private int threadCount = 15; + private int TEST_COUNT = 3; + final int LOOP_COUNT = 10 * 1; + private boolean testWhileIdle = true; + private boolean testOnBorrow = false; + private boolean testOnReturn = false; + + private boolean removeAbandoned = true; + private int removeAbandonedTimeout = 180; + private long timeBetweenEvictionRunsMillis = 60 * 10; + private long minEvictableIdleTimeMillis = 1800 * 10; + private int numTestsPerEvictionRun = 20; + + protected void setUp() throws Exception { + jdbcUrl = "jdbc:fake:dragoon_v25masterdb"; + user = "dragoon25"; + password = "dragoon25"; + driverClass = "com.alibaba.druid.mock.MockDriver"; + + MockDriver.instance.setIdleTimeCount(50 * 60 * 10); + +// jdbcUrl = "jdbc:mysql://10.20.141.150:8066/amoeba"; +// user = "root"; +// password = "12345"; +// driverClass = "com.mysql.jdbc.Driver"; + + // jdbcUrl = "jdbc:oracle:thin:@10.20.149.85:1521:ocnauto"; + // user = "alibaba"; + // password = "ccbuauto"; + // driverClass = "oracle.jdbc.driver.OracleDriver"; + } + + public void test_perf() throws Exception { + druid(); + + } + + public void druid() throws Exception { + DruidDataSource dataSource = new DruidDataSource(); + + dataSource.setInitialSize(initialSize); + dataSource.setMaxActive(maxActive); + dataSource.setMaxIdle(maxIdle); + dataSource.setMinIdle(minIdle); + dataSource.setMaxWait(maxWait); + dataSource.setPoolPreparedStatements(true); + dataSource.setDriverClassName(driverClass); + dataSource.setUrl(jdbcUrl); + dataSource.setPoolPreparedStatements(true); + dataSource.setUsername(user); + dataSource.setPassword(password); + dataSource.setValidationQuery(validationQuery); + dataSource.setTestOnBorrow(testOnBorrow); + dataSource.setTestOnBorrow(testWhileIdle); + dataSource.setTestOnBorrow(testOnReturn); + dataSource.setRemoveAbandoned(removeAbandoned); + dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout); + dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + dataSource.setNumTestsPerEvictionRun(numTestsPerEvictionRun); + + p0(dataSource, "druid", threadCount); + + long startMillis = System.currentTimeMillis(); + for (int i = 0; i < 1000; ++i) { + p0(dataSource, "druid", threadCount); + + long seconds = (System.currentTimeMillis() - startMillis) / 1000L; + System.out.println("seconds : " + seconds); + } + System.out.println(); + } + + private void p0(final DataSource dataSource, String name, int threadCount) throws Exception { + + final CountDownLatch startLatch = new CountDownLatch(1); + final CountDownLatch endLatch = new CountDownLatch(threadCount); + for (int i = 0; i < threadCount; ++i) { + Thread thread = new Thread() { + + public void run() { + try { + startLatch.await(); + + for (int i = 0; i < LOOP_COUNT; ++i) { + Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement("SELECT 1"); + ResultSet rs = stmt.executeQuery(); + rs.next(); + rs.getInt(1); + rs.close(); + stmt.close(); + conn.close(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + endLatch.countDown(); + } + }; + thread.start(); + } + long startMillis = System.currentTimeMillis(); + long startYGC = TestUtil.getYoungGC(); + long startFullGC = TestUtil.getFullGC(); + startLatch.countDown(); + endLatch.await(); + + long millis = System.currentTimeMillis() - startMillis; + long ygc = TestUtil.getYoungGC() - startYGC; + long fullGC = TestUtil.getFullGC() - startFullGC; + + System.out.println("thread " + threadCount + " " + name + " millis : " + NumberFormat.getInstance().format(millis) + ", YGC " + ygc + " FGC " + fullGC); + } +}