diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 051dd9ee28..12c62b2297 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -95,81 +95,81 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements */ public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L; - protected boolean defaultAutoCommit = true; - protected Boolean defaultReadOnly; - protected Integer defaultTransactionIsolation; - protected String defaultCatalog = null; + protected volatile boolean defaultAutoCommit = true; + protected volatile Boolean defaultReadOnly; + protected volatile Integer defaultTransactionIsolation; + protected volatile String defaultCatalog = null; protected String name; - protected String username; - protected String password; - protected String jdbcUrl; - protected String driverClass; - protected Properties connectionProperties = new Properties(); + protected volatile String username; + protected volatile String password; + protected volatile String jdbcUrl; + protected volatile String driverClass; + protected volatile Properties connectionProperties = new Properties(); - protected PasswordCallback passwordCallback; - protected NameCallback userCallback; + protected volatile PasswordCallback passwordCallback; + protected volatile NameCallback userCallback; protected ConnectionFactory connectionFactory; - protected int initialSize = DEFAULT_INITIAL_SIZE; - protected int maxActive = DEFAULT_MAX_ACTIVE_SIZE; - protected int minIdle = DEFAULT_MIN_IDLE; - protected int maxIdle = DEFAULT_MAX_IDLE; - protected long maxWait = DEFAULT_MAX_WAIT; + protected volatile int initialSize = DEFAULT_INITIAL_SIZE; + protected volatile int maxActive = DEFAULT_MAX_ACTIVE_SIZE; + protected volatile int minIdle = DEFAULT_MIN_IDLE; + protected volatile int maxIdle = DEFAULT_MAX_IDLE; + protected volatile long maxWait = DEFAULT_MAX_WAIT; - protected String validationQuery = DEFAULT_VALIDATION_QUERY; - protected int validationQueryTimeout = -1; - private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW; - private boolean testOnReturn = DEFAULT_TEST_ON_RETURN; - private boolean testWhileIdle = DEFAULT_WHILE_IDLE; - protected boolean poolPreparedStatements = false; - protected boolean sharePreparedStatements = false; - protected int maxPoolPreparedStatementPerConnectionSize = 10; + protected volatile String validationQuery = DEFAULT_VALIDATION_QUERY; + protected volatile int validationQueryTimeout = -1; + private volatile boolean testOnBorrow = DEFAULT_TEST_ON_BORROW; + private volatile boolean testOnReturn = DEFAULT_TEST_ON_RETURN; + private volatile boolean testWhileIdle = DEFAULT_WHILE_IDLE; + protected volatile boolean poolPreparedStatements = false; + protected volatile boolean sharePreparedStatements = false; + protected volatile int maxPoolPreparedStatementPerConnectionSize = 10; - protected boolean inited = false; + protected volatile boolean inited = false; protected PrintWriter logWriter = new PrintWriter( System.out); protected List filters = new CopyOnWriteArrayList(); - protected ExceptionSorter exceptionSorter = null; + protected volatile ExceptionSorter exceptionSorter = null; protected Driver driver; - protected int queryTimeout; - protected int transactionQueryTimeout; + protected volatile int queryTimeout; + protected volatile int transactionQueryTimeout; protected long createErrorCount; protected long createTimespan; - protected int maxWaitThreadCount = -1; + protected volatile int maxWaitThreadCount = -1; - protected boolean accessToUnderlyingConnectionAllowed = true; + protected volatile boolean accessToUnderlyingConnectionAllowed = true; - protected long timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + protected volatile long timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; - protected int numTestsPerEvictionRun = DEFAULT_NUM_TESTS_PER_EVICTION_RUN; + protected volatile int numTestsPerEvictionRun = DEFAULT_NUM_TESTS_PER_EVICTION_RUN; - protected long minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; + protected volatile long minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; - protected boolean removeAbandoned; + protected volatile boolean removeAbandoned; - protected long removeAbandonedTimeoutMillis = 300 * 1000; + protected volatile long removeAbandonedTimeoutMillis = 300 * 1000; - protected boolean logAbandoned; + protected volatile boolean logAbandoned; - protected int maxOpenPreparedStatements = -1; + protected volatile int maxOpenPreparedStatements = -1; - protected List connectionInitSqls; + protected volatile List connectionInitSqls; - protected String dbType; + protected volatile String dbType; - protected long timeBetweenConnectErrorMillis = DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS; + protected volatile long timeBetweenConnectErrorMillis = DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS; - protected ValidConnectionChecker validConnectionChecker = null; + protected volatile ValidConnectionChecker validConnectionChecker = null; protected final AtomicLong errorCount = new AtomicLong(); protected final AtomicLong dupCloseCount = new AtomicLong(); @@ -777,9 +777,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements return poolPreparedStatements; } - public void setPoolPreparedStatements(boolean poolPreparedStatements) { - this.poolPreparedStatements = poolPreparedStatements; - } + public abstract void setPoolPreparedStatements(boolean value); public long getMaxWait() { return maxWait; @@ -837,13 +835,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements return maxActive; } - public void setMaxActive(int maxActive) { - if (inited) { - throw new UnsupportedOperationException(); - } - - this.maxActive = maxActive; - } + public abstract void setMaxActive(int maxActive); public String getUsername() { return username; @@ -974,6 +966,10 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements connectionFactory = createConnectionFactory(); } + public ConnectionFactory getConnectionFactory() { + return connectionFactory; + } + protected ConnectionFactory createConnectionFactory() throws SQLException { return new DruidPoolConnectionFactory(this); } @@ -1219,6 +1215,14 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements abstract void incrementCreateCount(); + public Connection createPhysicalConnection(String url, Properties info) throws SQLException { + if (getProxyFilters().size() == 0) { + return getDriver().connect(url, info); + } + + return new FilterChainImpl(this).connection_connect(info); + } + public static class DruidPoolConnectionFactory implements ConnectionFactory { private final String url; @@ -1286,7 +1290,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements long startNano = System.nanoTime(); try { - conn = connect(); + conn = dataSource.createPhysicalConnection(url, info); if (conn == null) { throw new SQLException("connect error, url " + url); @@ -1321,16 +1325,6 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements return conn; } - public Connection connect() throws SQLException { - Connection conn; - if (dataSource.getProxyFilters().size() != 0) { - conn = new FilterChainImpl(dataSource).connection_connect(info); - } else { - conn = dataSource.getDriver().connect(url, info); - } - return conn; - } - public void initConnection(Connection conn) throws SQLException { conn.setAutoCommit(dataSource.isDefaultAutoCommit()); if (dataSource.getDefaultReadOnly() != null) { diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index ccb8825e30..f50e74fe50 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -76,54 +76,54 @@ import com.alibaba.druid.util.JdbcUtils; */ public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource { - private final static Log LOG = LogFactory.getLog(DruidDataSource.class); + private final static Log LOG = LogFactory.getLog(DruidDataSource.class); - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final Condition notEmpty = lock.newCondition(); - private final Condition empty = lock.newCondition(); + private final Condition notEmpty = lock.newCondition(); + private final Condition empty = lock.newCondition(); // stats - private long connectCount = 0L; - private long closeCount = 0L; - private final AtomicLong connectErrorCount = new AtomicLong(); - private long recycleCount = 0L; - private long createConnectionCount = 0L; - private long destroyCount = 0L; - private long removeAbandonedCount = 0L; - private long notEmptyWaitCount = 0L; - private long notEmptySignalCount = 0L; - private long notEmptyWaitNanos = 0L; + private long connectCount = 0L; + private long closeCount = 0L; + private final AtomicLong connectErrorCount = new AtomicLong(); + private long recycleCount = 0L; + private long createConnectionCount = 0L; + private long destroyCount = 0L; + private long removeAbandonedCount = 0L; + private long notEmptyWaitCount = 0L; + private long notEmptySignalCount = 0L; + private long notEmptyWaitNanos = 0L; - private int activePeak = 0; - private long activePeakTime = 0; - private int poolingPeak = 0; - private long poolingPeakTime = 0; + private int activePeak = 0; + private long activePeakTime = 0; + private int poolingPeak = 0; + private long poolingPeakTime = 0; // store - private ConnectionHolder[] connections; - private int poolingCount = 0; - private int activeCount = 0; - private long discardCount = 0; - private int notEmptyWaitThreadCount = 0; - private int notEmptyWaitThreadPeak = 0; + private volatile ConnectionHolder[] connections; + private int poolingCount = 0; + private int activeCount = 0; + private long discardCount = 0; + private int notEmptyWaitThreadCount = 0; + private int notEmptyWaitThreadPeak = 0; // threads - private CreateConnectionThread createConnectionThread; - private DestroyConnectionThread destoryConnectionThread; + private CreateConnectionThread createConnectionThread; + private DestroyConnectionThread destoryConnectionThread; - private final CountDownLatch initedLatch = new CountDownLatch(2); + private final CountDownLatch initedLatch = new CountDownLatch(2); - private boolean enable = true; + private boolean enable = true; - private boolean resetStatEnable = true; + private boolean resetStatEnable = true; - private String initStackTrace; + private String initStackTrace; - private boolean closed = false; - private long closeTimeMillis = -1L; + private boolean closed = false; + private long closeTimeMillis = -1L; - private JdbcDataSourceStat dataSourceStat; + private JdbcDataSourceStat dataSourceStat; public DruidDataSource(){ } @@ -216,6 +216,19 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } public void setPoolPreparedStatements(boolean value) { + if (this.poolPreparedStatements == value) { + return; + } + + if (!inited) { + this.poolPreparedStatements = value; + return; + } + + if (LOG.isInfoEnabled()) { + LOG.info("set poolPreparedStatements " + this.poolPreparedStatements + " -> " + value); + } + lock.lock(); try { if (this.poolPreparedStatements && (!value)) { @@ -230,7 +243,36 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat connection.getStatementPool().getMap().clear(); } } - super.setPoolPreparedStatements(value); + } finally { + lock.unlock(); + } + } + + public void setMaxActive(int maxActive) { + if (this.maxActive == maxActive) { + return; + } + + if (!inited) { + this.maxActive = maxActive; + return; + } + + if (LOG.isInfoEnabled()) { + LOG.info("set maxActive " + this.maxActive + " -> " + maxActive); + } + + lock.lock(); + try { + int allCount = this.poolingCount + this.activeCount; + + if (maxActive > allCount) { + this.connections = Arrays.copyOf(this.connections, maxActive); + } else { + this.connections = Arrays.copyOf(this.connections, allCount); + } + + this.maxActive = maxActive; } finally { lock.unlock(); } diff --git a/src/main/java/com/alibaba/druid/support/logging/JakartaCommonsLoggingImpl.java b/src/main/java/com/alibaba/druid/support/logging/JakartaCommonsLoggingImpl.java index 0d77caf47a..b4e96dfc68 100644 --- a/src/main/java/com/alibaba/druid/support/logging/JakartaCommonsLoggingImpl.java +++ b/src/main/java/com/alibaba/druid/support/logging/JakartaCommonsLoggingImpl.java @@ -54,4 +54,13 @@ public class JakartaCommonsLoggingImpl implements com.alibaba.druid.support.logg errorCount = 0; } + @Override + public boolean isInfoEnabled() { + return log.isInfoEnabled(); + } + + @Override + public void info(String msg) { + log.info(msg); + } } diff --git a/src/main/java/com/alibaba/druid/support/logging/Jdk14LoggingImpl.java b/src/main/java/com/alibaba/druid/support/logging/Jdk14LoggingImpl.java index 8eeb543769..6c518803c6 100644 --- a/src/main/java/com/alibaba/druid/support/logging/Jdk14LoggingImpl.java +++ b/src/main/java/com/alibaba/druid/support/logging/Jdk14LoggingImpl.java @@ -53,4 +53,14 @@ public class Jdk14LoggingImpl implements Log { public void resetStat() { errorCount = 0; } + + @Override + public boolean isInfoEnabled() { + return log.isLoggable(Level.INFO); + } + + @Override + public void info(String msg) { + log.info(msg); + } } diff --git a/src/main/java/com/alibaba/druid/support/logging/Log.java b/src/main/java/com/alibaba/druid/support/logging/Log.java index 6127466053..4f9544c83d 100644 --- a/src/main/java/com/alibaba/druid/support/logging/Log.java +++ b/src/main/java/com/alibaba/druid/support/logging/Log.java @@ -4,19 +4,23 @@ public interface Log { boolean isDebugEnabled(); - void error(String s, Throwable e); + void error(String msg, Throwable e); - void error(String s); + void error(String msg); - public void debug(String s); + boolean isInfoEnabled(); - public void debug(String s, Throwable e); + void info(String msg); - public void warn(String s); + void debug(String msg); - void warn(String s, Throwable e); + void debug(String msg, Throwable e); + + void warn(String msg); + + void warn(String msg, Throwable e); int getErrorCount(); - + void resetStat(); } diff --git a/src/main/java/com/alibaba/druid/support/logging/Log4jImpl.java b/src/main/java/com/alibaba/druid/support/logging/Log4jImpl.java index 2fa2a7d098..b7cd194fed 100644 --- a/src/main/java/com/alibaba/druid/support/logging/Log4jImpl.java +++ b/src/main/java/com/alibaba/druid/support/logging/Log4jImpl.java @@ -5,8 +5,8 @@ import org.apache.log4j.Logger; public class Log4jImpl implements Log { private Logger log; - - private int errorCount; + + private int errorCount; public Log4jImpl(Class clazz){ log = Logger.getLogger(clazz); @@ -39,7 +39,7 @@ public class Log4jImpl implements Log { public void warn(String s, Throwable e) { log.warn(s, e); } - + public int getErrorCount() { return errorCount; } @@ -48,4 +48,14 @@ public class Log4jImpl implements Log { public void resetStat() { errorCount = 0; } + + @Override + public boolean isInfoEnabled() { + return log.isInfoEnabled(); + } + + @Override + public void info(String msg) { + log.info(msg); + } } diff --git a/src/main/java/com/alibaba/druid/support/logging/NoLoggingImpl.java b/src/main/java/com/alibaba/druid/support/logging/NoLoggingImpl.java index 57fe946003..51d6761bff 100644 --- a/src/main/java/com/alibaba/druid/support/logging/NoLoggingImpl.java +++ b/src/main/java/com/alibaba/druid/support/logging/NoLoggingImpl.java @@ -41,4 +41,14 @@ public class NoLoggingImpl implements Log { public void resetStat() { errorCount = 0; } + + @Override + public boolean isInfoEnabled() { + return false; + } + + @Override + public void info(String s) { + + } } diff --git a/src/test/java/com/alibaba/druid/benckmark/pool/Case0.java b/src/test/java/com/alibaba/druid/benckmark/pool/Case0.java index b9e31c7db2..7ace793354 100644 --- a/src/test/java/com/alibaba/druid/benckmark/pool/Case0.java +++ b/src/test/java/com/alibaba/druid/benckmark/pool/Case0.java @@ -49,14 +49,14 @@ public class Case0 extends TestCase { public final int COUNT = 1000 * 1000 * 1; protected void setUp() throws Exception { -// jdbcUrl = "jdbc:fake:dragoon_v25masterdb"; -// user = "dragoon25"; -// password = "dragoon25"; -// driverClass = "com.alibaba.druid.mock.MockDriver"; + jdbcUrl = "jdbc:fake:dragoon_v25masterdb"; + user = "dragoon25"; + password = "dragoon25"; + driverClass = "com.alibaba.druid.mock.MockDriver"; - jdbcUrl = "jdbc:mysql://10.20.159.104:3306/masterdb"; - user = "dragoon"; - password = "dragoon"; +// jdbcUrl = "jdbc:mysql://10.20.159.104:3306/masterdb"; +// user = "dragoon"; +// password = "dragoon"; } public void test_0() throws Exception { diff --git a/src/test/java/com/alibaba/druid/benckmark/pool/druid/DruidCase0.java b/src/test/java/com/alibaba/druid/benckmark/pool/druid/DruidCase0.java new file mode 100644 index 0000000000..691f8ba57f --- /dev/null +++ b/src/test/java/com/alibaba/druid/benckmark/pool/druid/DruidCase0.java @@ -0,0 +1,42 @@ +package com.alibaba.druid.benckmark.pool.druid; + +import java.sql.Connection; + +import junit.framework.TestCase; + +import com.alibaba.druid.pool.DruidDataSource; + +public class DruidCase0 extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setMaxActive(8); + dataSource.setMinIdle(1); + dataSource.setMinEvictableIdleTimeMillis(1000 * 60 * 5); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_benchmark() throws Exception { + for (int i = 0; i < 10; ++i) { + long startMillis = System.currentTimeMillis(); + benchmark(); + long millis = System.currentTimeMillis() - startMillis; + + System.out.println("millis : " + millis); + } + } + + public void benchmark() throws Exception { + for (int i = 0; i < 1000 * 1000 * 10; ++i) { + Connection conn = dataSource.getConnection(); + conn.close(); + } + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/MaxActiveChangeTest.java b/src/test/java/com/alibaba/druid/bvt/pool/MaxActiveChangeTest.java new file mode 100644 index 0000000000..9145a1a2d8 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/MaxActiveChangeTest.java @@ -0,0 +1,97 @@ +package com.alibaba.druid.bvt.pool; + +import java.sql.Connection; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.pool.GetConnectionTimeoutException; +import com.alibaba.druid.util.JdbcUtils; + +public class MaxActiveChangeTest extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setTestOnBorrow(false); + dataSource.setMaxActive(3); + dataSource.setMinIdle(1); + dataSource.setMinEvictableIdleTimeMillis(1000 * 60 * 5); + dataSource.setMaxWait(20); + dataSource.init(); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_maxActive() throws Exception { + for (int i = 0; i < 10; ++i) { + Assert.assertEquals(1, connect(1)); + Assert.assertEquals(1, dataSource.getPoolingCount()); + } + for (int i = 0; i < 10; ++i) { + Assert.assertEquals(2, connect(2)); + Assert.assertEquals(2, dataSource.getPoolingCount()); + } + for (int i = 0; i < 10; ++i) { + Assert.assertEquals(3, connect(3)); + Assert.assertEquals(3, dataSource.getPoolingCount()); + } + for (int i = 0; i < 10; ++i) { + Assert.assertEquals(3, connect(4)); + Assert.assertEquals(3, dataSource.getPoolingCount()); + } + + dataSource.setMaxActive(4); + + for (int i = 0; i < 10; ++i) { + Assert.assertEquals(4, connect(4)); + Assert.assertEquals(4, dataSource.getPoolingCount()); + } + + dataSource.shrink(); + Assert.assertEquals(1, dataSource.getPoolingCount()); + + for (int i = 0; i < 10; ++i) { + Assert.assertEquals(4, connect(4)); + Assert.assertEquals(4, dataSource.getPoolingCount()); + } + + Assert.assertEquals(4, dataSource.getPoolingCount()); + dataSource.setMaxActive(3); + + Assert.assertEquals(4, dataSource.getPoolingCount()); + + dataSource.shrink(); + Assert.assertEquals(1, dataSource.getPoolingCount()); + + // 确保收缩之后不会再长上去 + for (int i = 0; i < 10; ++i) { + Assert.assertEquals(3, connect(4)); + Assert.assertEquals(3, dataSource.getPoolingCount()); + } + } + + public int connect(int count) throws Exception { + int successCount = 0; + Connection[] connections = new Connection[count]; + for (int i = 0; i < count; ++i) { + try { + connections[i] = dataSource.getConnection(); + successCount++; + } catch (GetConnectionTimeoutException e) { + // skip + } + } + + for (int i = 0; i < count; ++i) { + JdbcUtils.close(connections[i]); + } + + return successCount; + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/basic/DataSourceTest3.java b/src/test/java/com/alibaba/druid/bvt/pool/basic/DataSourceTest3.java index 58b8651614..44ad4cfa45 100644 --- a/src/test/java/com/alibaba/druid/bvt/pool/basic/DataSourceTest3.java +++ b/src/test/java/com/alibaba/druid/bvt/pool/basic/DataSourceTest3.java @@ -148,15 +148,7 @@ public class DataSourceTest3 extends TestCase { conn.close(); - { - Exception error = null; - try { - dataSource.setMaxActive(1); - } catch (Exception ex) { - error = ex; - } - Assert.assertNotNull(error); - } + dataSource.setMaxActive(1); } public void test_error_7() throws Exception {