From 1554d2bce65d87ca6b335d9f02e89a0f61c0ae23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E9=93=81?= Date: Tue, 2 Feb 2016 22:13:46 +0800 Subject: [PATCH] DruidDataSource support failFast --- .../pool/DataSourceNotAvailableException.java | 14 ++++ .../druid/pool/DruidAbstractDataSource.java | 34 ++++++--- .../alibaba/druid/pool/DruidDataSource.java | 34 ++++++++- .../druid/bvt/pool/profile/FailFastTest.java | 76 +++++++++++++++++++ 4 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/pool/DataSourceNotAvailableException.java create mode 100644 src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java diff --git a/src/main/java/com/alibaba/druid/pool/DataSourceNotAvailableException.java b/src/main/java/com/alibaba/druid/pool/DataSourceNotAvailableException.java new file mode 100644 index 0000000000..d116b4cd12 --- /dev/null +++ b/src/main/java/com/alibaba/druid/pool/DataSourceNotAvailableException.java @@ -0,0 +1,14 @@ +package com.alibaba.druid.pool; + +import java.sql.SQLException; + +public class DataSourceNotAvailableException extends SQLException { + + private static final long serialVersionUID = 1L; + + public DataSourceNotAvailableException(Throwable cause){ + super(cause); + } + +} + diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 2d42f3678d..87ad6fabc5 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -244,6 +244,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements private boolean asyncCloseConnectionEnable = false; protected int maxCreateTaskCount = 3; + protected boolean failFast = false; protected ScheduledExecutorService destroyScheduler; protected ScheduledExecutorService createScheduler; @@ -1454,16 +1455,10 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements createError = null; } catch (SQLException ex) { - createErrorCount.incrementAndGet(); - createError = ex; - lastCreateError = ex; - lastCreateErrorTimeMillis = System.currentTimeMillis(); + setCreateError(ex); throw ex; } catch (RuntimeException ex) { - createErrorCount.incrementAndGet(); - createError = ex; - lastCreateError = ex; - lastCreateErrorTimeMillis = System.currentTimeMillis(); + setCreateError(ex); throw ex; } catch (Error ex) { createErrorCount.incrementAndGet(); @@ -1476,6 +1471,19 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos); } + protected void setCreateError(Throwable ex) { + createErrorCount.incrementAndGet(); + long now = System.currentTimeMillis(); + lock.lock(); + try { + createError = ex; + lastCreateError = ex; + lastCreateErrorTimeMillis = now; + } finally { + lock.unlock(); + } + } + public void initPhysicalConnection(Connection conn) throws SQLException { if (conn.getAutoCommit() != defaultAutoCommit) { conn.setAutoCommit(defaultAutoCommit); @@ -1758,7 +1766,15 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements this.maxCreateTaskCount = maxCreateTaskCount; } - static class PhysicalConnectionInfo { + public boolean isFailFast() { + return failFast; + } + + public void setFailFast(boolean failFast) { + this.failFast = failFast; + } + + public static class PhysicalConnectionInfo { private Connection connection; private long connectStartNanos; private long connectedNanos; diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 7ce556208f..46b013d4a5 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -277,6 +277,12 @@ public class DruidDataSource extends DruidAbstractDataSource } } } + { + Boolean value = getBoolean(properties, "druid.failFast"); + if (value != null) { + this.setFailFast(value); + } + } } public boolean isUseGlobalDataSourceStat() { @@ -1450,6 +1456,10 @@ public class DruidDataSource extends DruidAbstractDataSource DruidConnectionHolder takeLast() throws InterruptedException, SQLException { try { while (poolingCount == 0) { + if (failFast && createError != null) { + throw new DataSourceNotAvailableException(createError); + } + emptySignal(); // send signal to CreateThread create connection notEmptyWaitThreadCount++; if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { @@ -1461,7 +1471,7 @@ public class DruidDataSource extends DruidAbstractDataSource notEmptyWaitThreadCount--; } notEmptyWaitCount++; - + if (!enable) { connectErrorCount.incrementAndGet(); throw new DataSourceDisableException(); @@ -1485,6 +1495,10 @@ public class DruidDataSource extends DruidAbstractDataSource for (;;) { if (poolingCount == 0) { + if (failFast && createError != null) { + throw new DataSourceNotAvailableException(createError); + } + emptySignal(); // send signal to CreateThread create connection if (estimate <= 0) { @@ -1810,6 +1824,15 @@ public class DruidDataSource extends DruidAbstractDataSource LOG.error("create connection error, url: " + jdbcUrl, e); errorCount++; + + if (failFast) { + lock.lock(); + try { + notEmpty.signalAll(); + } finally { + lock.lock(); + } + } if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { if (breakAfterAcquireFailure) { @@ -1897,6 +1920,15 @@ public class DruidDataSource extends DruidAbstractDataSource LOG.error("create connection error, url: " + jdbcUrl, e); errorCount++; + + if (failFast) { + lock.lock(); + try { + notEmpty.signalAll(); + } finally { + lock.unlock(); + } + } if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { if (breakAfterAcquireFailure) { diff --git a/src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java b/src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java new file mode 100644 index 0000000000..81a7ab61ba --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/profile/FailFastTest.java @@ -0,0 +1,76 @@ +package com.alibaba.druid.bvt.pool.profile; + +import java.sql.SQLException; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.Assert; + +import com.alibaba.druid.pool.DruidDataSource; + +import junit.framework.TestCase; + +public class FailFastTest extends TestCase { + + private DruidDataSource dataSource; + + private CountDownLatch latch = new CountDownLatch(1); + + @SuppressWarnings("serial") + protected void setUp() throws Exception { + dataSource = new DruidDataSource() { + public PhysicalConnectionInfo createPhysicalConnection() throws SQLException { + try { + latch.await(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + SQLException ex = new SQLException(); + setCreateError(ex); + throw ex; + } + }; + + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setMaxWait(1000 * 1000); + + Properties properties = new Properties(); + properties.setProperty("druid.failFast", "true"); + dataSource.configFromPropety(properties); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void testDefault() throws Exception { + Assert.assertTrue(dataSource.isFailFast()); + + final CountDownLatch connectStartLatch = new CountDownLatch(1); + final CountDownLatch connectEndLatch = new CountDownLatch(1); + Thread connectThread = new Thread() { + public void run() { + connectStartLatch.countDown(); + try { + dataSource.getConnection(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + connectEndLatch.countDown(); + } + } + }; + connectThread.setName("ConnectThread"); + connectThread.start(); + + connectStartLatch.await(); + + latch.countDown(); + + connectEndLatch.await(3, TimeUnit.SECONDS); + } + +}