diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 1bddddb312..84a04fc64b 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -255,6 +255,8 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements protected boolean initVariants = false; protected boolean initGlobalVariants = false; + protected volatile boolean onFatalError = false; + protected volatile int onFatalErrorMaxActive = 0; public DruidAbstractDataSource(boolean lockFair){ lock = new ReentrantLock(lockFair); @@ -1955,6 +1957,18 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements this.failFast = failFast; } + public int getOnFatalErrorMaxActive() { + return onFatalErrorMaxActive; + } + + public void setOnFatalErrorMaxActive(int onFatalErrorMaxActive) { + this.onFatalErrorMaxActive = onFatalErrorMaxActive; + } + + public boolean isOnFatalError() { + return onFatalError; + } + public static class PhysicalConnectionInfo { private Connection connection; private long connectStartNanos; diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 27fc2cd4b2..30fe4a033f 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -90,36 +90,30 @@ import com.alibaba.druid.wall.WallProviderStatValue; */ public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration { - private final static Log LOG = LogFactory.getLog(DruidDataSource.class); - - private static final long serialVersionUID = 1L; - + private final static Log LOG = LogFactory.getLog(DruidDataSource.class); + private static final long serialVersionUID = 1L; // stats - private final AtomicLong recycleErrorCount = new AtomicLong(); - private long connectCount = 0L; - private long closeCount = 0L; - private final AtomicLong connectErrorCount = new AtomicLong(); - private long recycleCount = 0L; - private long removeAbandonedCount = 0L; - private long notEmptyWaitCount = 0L; - private long notEmptySignalCount = 0L; - private long notEmptyWaitNanos = 0L; - - private int keepAliveCheckCount = 0; - - private int activePeak = 0; - private long activePeakTime = 0; - private int poolingPeak = 0; - private long poolingPeakTime = 0; - + private final AtomicLong recycleErrorCount = new AtomicLong(); + private long connectCount = 0L; + private long closeCount = 0L; + private final AtomicLong connectErrorCount = new AtomicLong(); + private long recycleCount = 0L; + private long removeAbandonedCount = 0L; + private long notEmptyWaitCount = 0L; + private long notEmptySignalCount = 0L; + private long notEmptyWaitNanos = 0L; + private int keepAliveCheckCount = 0; + private int activePeak = 0; + private long activePeakTime = 0; + private int poolingPeak = 0; + private long poolingPeakTime = 0; // store private volatile DruidConnectionHolder[] connections; - private int poolingCount = 0; - private int activeCount = 0; - private long discardCount = 0; - private int notEmptyWaitThreadCount = 0; - private int notEmptyWaitThreadPeak = 0; - + private int poolingCount = 0; + private int activeCount = 0; + private long discardCount = 0; + private int notEmptyWaitThreadCount = 0; + private int notEmptyWaitThreadPeak = 0; // private DruidConnectionHolder[] evictConnections; private DruidConnectionHolder[] keepAliveConnections; @@ -135,30 +129,27 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat private LogStatsThread logStatsThread; private int createTaskCount; - private final CountDownLatch initedLatch = new CountDownLatch(2); + private final CountDownLatch initedLatch = new CountDownLatch(2); - private volatile boolean enable = true; + private volatile boolean enable = true; - private boolean resetStatEnable = true; - private final AtomicLong resetCount = new AtomicLong(); + private boolean resetStatEnable = true; + private final AtomicLong resetCount = new AtomicLong(); private String initStackTrace; - private volatile boolean closing = false; - private volatile boolean closed = false; - private long closeTimeMillis = -1L; + private volatile boolean closing = false; + private volatile boolean closed = false; + private long closeTimeMillis = -1L; protected JdbcDataSourceStat dataSourceStat; - private boolean useGlobalDataSourceStat = false; - - private boolean mbeanRegistered = false; - - public static ThreadLocal waitNanosLocal = new ThreadLocal(); - private boolean logDifferentThread = true; - private volatile boolean keepAlive = false; - private boolean asyncInit = false; - + private boolean useGlobalDataSourceStat = false; + private boolean mbeanRegistered = false; + public static ThreadLocal waitNanosLocal = new ThreadLocal(); + private boolean logDifferentThread = true; + private volatile boolean keepAlive = false; + private boolean asyncInit = false; protected boolean killWhenSocketReadTimeout = false; public DruidDataSource(){ @@ -1371,12 +1362,19 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } try { - if (maxWaitThreadCount > 0) { - if (notEmptyWaitThreadCount >= maxWaitThreadCount) { + if (maxWaitThreadCount > 0 + && notEmptyWaitThreadCount >= maxWaitThreadCount) + { connectErrorCount.incrementAndGet(); throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count " + lock.getQueueLength()); - } + } + + if (onFatalError + && onFatalErrorMaxActive > 0 + && activeCount >= onFatalErrorMaxActive) { + connectErrorCount.incrementAndGet(); + throw new SQLException("onFatalError, activeCount " + activeCount + ", onFatalErrorMaxActive " + onFatalErrorMaxActive); } connectCount++; @@ -1482,6 +1480,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat pooledConnection.disable(t); requireDiscard = true; } + + onFatalError = true; } finally { lock.unlock(); } diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java index 4bee47b18f..c604cfd7d5 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java @@ -61,7 +61,7 @@ public class DruidPooledConnection extends PoolableWrapper implements javax.sql. protected TransactionInfo transactionInfo; private final boolean dupCloseLogEnable; protected volatile boolean traceEnable = false; - private boolean disable = false; + private volatile boolean disable = false; private boolean closed = false; protected final Thread ownerThread; private long connectedTimeMillis; @@ -1208,9 +1208,13 @@ public class DruidPooledConnection extends PoolableWrapper implements javax.sql. final void afterExecute() { final DruidConnectionHolder holder = this.holder; - if (holder != null && holder.dataSource.removeAbandoned) { - running = false; - holder.lastActiveTimeMillis = System.currentTimeMillis(); + if (holder != null) { + DruidAbstractDataSource dataSource = holder.dataSource; + if (dataSource.removeAbandoned) { + running = false; + holder.lastActiveTimeMillis = System.currentTimeMillis(); + } + dataSource.onFatalError = false; } } diff --git a/src/test/java/com/alibaba/druid/bvt/pool/OnFatalErrorMaxActiveTest.java b/src/test/java/com/alibaba/druid/bvt/pool/OnFatalErrorMaxActiveTest.java new file mode 100644 index 0000000000..d862d4dba5 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/OnFatalErrorMaxActiveTest.java @@ -0,0 +1,58 @@ +package com.alibaba.druid.bvt.pool; + +import com.alibaba.druid.PoolTestCase; +import com.alibaba.druid.pool.DruidAbstractDataSource; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.JdbcUtils; + +import java.lang.reflect.Field; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class OnFatalErrorMaxActiveTest extends PoolTestCase { + protected DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:"); + dataSource.setOnFatalErrorMaxActive(1); + } + + protected void tearDown() throws Exception { + JdbcUtils.close(dataSource); + } + + public void test_for_fatalError() throws Exception { + Field field = DruidAbstractDataSource.class.getDeclaredField("onFatalError"); + field.setAccessible(true); + + { + Connection conn = dataSource.getConnection(); + conn.close(); + } + + field.set(dataSource, true); + + + Connection conn_0 = dataSource.getConnection(); + assertTrue(dataSource.isOnFatalError()); + + SQLException error = null; + try { + Connection conn_1 = dataSource.getConnection(); + } catch (SQLException ex) { + error = ex; + } + assertNotNull(error); + assertEquals("onFatalError, activeCount 1, onFatalErrorMaxActive 1", error.getMessage()); + + Statement stmt = conn_0.createStatement(); + ResultSet rs = stmt.executeQuery("select 1"); + assertFalse(dataSource.isOnFatalError()); + rs.close(); + stmt.close(); + conn_0.close(); + } +}