diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index a0d9d2de04..6303b05897 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -796,7 +796,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (exceptionSorter != null && exceptionSorter.isExceptionFatal(sqlEx)) { if (pooledConnection.isTraceEnable()) { synchronized (activeConnections) { - activeConnections.remove(pooledConnection); + if (pooledConnection.isTraceEnable()) { + activeConnections.remove(pooledConnection); + pooledConnection.setTraceEnable(false); + } } } this.discardConnection(holder.getConnection()); @@ -824,11 +827,14 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (pooledConnection.isTraceEnable()) { synchronized (activeConnections) { - Object oldInfo = activeConnections.remove(pooledConnection); - if (oldInfo == null) { - if (LOG.isWarnEnabled()) { - LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size()); + if (pooledConnection.isTraceEnable()) { + Object oldInfo = activeConnections.remove(pooledConnection); + if (oldInfo == null) { + if (LOG.isWarnEnabled()) { + LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size()); + } } + pooledConnection.setTraceEnable(false); } } } @@ -1320,6 +1326,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (timeMillis >= removeAbandonedTimeoutMillis) { iter.remove(); + pooledConnection.setTraceEnable(false); abandonedList.add(pooledConnection); } } diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java index 47085bfc96..4921b79875 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java @@ -60,7 +60,7 @@ public class DruidPooledConnection implements javax.sql.PooledConnection, Connec protected volatile DruidConnectionHolder holder; protected TransactionInfo transactionInfo; private final boolean dupCloseLogEnable; - private boolean traceEnable = false; + private volatile boolean traceEnable = false; private boolean disable = false; private boolean closed = false; private final Thread ownerThread; @@ -212,6 +212,40 @@ public class DruidPooledConnection implements javax.sql.PooledConnection, Connec return; } + if (holder.getDataSource().isRemoveAbandoned()) { + syncClose(); + return; + } + + for (ConnectionEventListener listener : holder.getConnectionEventListeners()) { + listener.connectionClosed(new ConnectionEvent(this)); + } + + DruidAbstractDataSource dataSource = holder.getDataSource(); + List filters = dataSource.getProxyFilters(); + if (filters.size() > 0) { + FilterChainImpl filterChain = new FilterChainImpl(dataSource); + filterChain.dataSource_recycle(this); + } else { + recycle(); + } + + this.disable = true; + } + + public synchronized void syncClose() throws SQLException { + if (this.disable) { + return; + } + + DruidConnectionHolder holder = this.holder; + if (holder == null) { + if (dupCloseLogEnable) { + LOG.error("dup close"); + } + return; + } + for (ConnectionEventListener listener : holder.getConnectionEventListeners()) { listener.connectionClosed(new ConnectionEvent(this)); } diff --git a/src/test/java/com/alibaba/druid/bvt/pool/TestRemoveAbandoned2.java b/src/test/java/com/alibaba/druid/bvt/pool/TestRemoveAbandoned2.java new file mode 100644 index 0000000000..f07c3b631c --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/TestRemoveAbandoned2.java @@ -0,0 +1,92 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.druid.bvt.pool; + +import java.sql.Connection; +import java.util.concurrent.atomic.AtomicLong; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.mock.MockDriver; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.pool.DruidPooledConnection; +import com.alibaba.druid.stat.DruidDataSourceStatManager; + +public class TestRemoveAbandoned2 extends TestCase { + + private MockDriver driver; + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + driver = new MockDriver(); + + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setDriver(driver); + dataSource.setInitialSize(1); + dataSource.setMaxActive(2); + dataSource.setMaxIdle(2); + dataSource.setMinIdle(1); + dataSource.setMinEvictableIdleTimeMillis(300 * 1000); // 300 / 10 + dataSource.setTimeBetweenEvictionRunsMillis(1000 * 300); // 180 / 10 + dataSource.setRemoveAbandoned(true); + dataSource.setRemoveAbandonedTimeoutMillis(0); + dataSource.setTestWhileIdle(true); + dataSource.setTestOnBorrow(false); + dataSource.setValidationQuery("SELECT 1"); + dataSource.setFilters("stat"); + + Assert.assertEquals(0, DruidDataSourceStatManager.getInstance().getDataSourceList().size()); + } + + protected void tearDown() throws Exception { + dataSource.close(); + Assert.assertEquals(0, DruidDataSourceStatManager.getInstance().getDataSourceList().size()); + } + + public void test_removeAbandoned() throws Exception { + { + Connection conn = dataSource.getConnection(); + conn.close(); + } + Assert.assertEquals(0, dataSource.getActiveCount()); + + + Thread abandonThread = new Thread("abandoned") { + + public void run() { + for (;;) { + dataSource.removeAbandoned(); + if (Thread.interrupted()) { + break; + } + } + } + }; + + abandonThread.start(); + + for (int i = 0; i < 1000 * 100; ++i) { + DruidPooledConnection conn = dataSource.getConnection(); + conn.close(); + } + Assert.assertEquals(0, dataSource.getActiveCount()); + abandonThread.interrupt(); + + System.out.println("removeAbandondedCount : " + dataSource.getRemoveAbandonedCount()); + } +}