mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
add double check for mysql testWhileIdle.
This commit is contained in:
@@ -90,11 +90,7 @@ import com.alibaba.druid.stat.JdbcSqlStat;
|
||||
import com.alibaba.druid.stat.JdbcSqlStatValue;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JMXUtils;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
import com.alibaba.druid.util.StringUtils;
|
||||
import com.alibaba.druid.util.Utils;
|
||||
import com.alibaba.druid.util.*;
|
||||
import com.alibaba.druid.wall.WallFilter;
|
||||
import com.alibaba.druid.wall.WallProviderStatValue;
|
||||
|
||||
@@ -169,14 +165,11 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
private boolean mbeanRegistered = false;
|
||||
|
||||
public static ThreadLocal<Long> waitNanosLocal = new ThreadLocal<Long>();
|
||||
|
||||
private boolean logDifferentThread = true;
|
||||
|
||||
private volatile boolean keepAlive = false;
|
||||
|
||||
private boolean asyncInit = false;
|
||||
|
||||
protected boolean killWhenSocketReadTimeout = false;
|
||||
protected boolean killWhenSocketReadTimeout = false;
|
||||
|
||||
public DruidDataSource(){
|
||||
this(false);
|
||||
@@ -224,13 +217,13 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
{
|
||||
Boolean value = getBoolean(properties, "druid.testWhileIdle");
|
||||
if (value != null) {
|
||||
this.setTestWhileIdle(value);
|
||||
this.testWhileIdle = value;
|
||||
}
|
||||
}
|
||||
{
|
||||
Boolean value = getBoolean(properties, "druid.testOnBorrow");
|
||||
if (value != null) {
|
||||
this.setTestOnBorrow(value);
|
||||
this.testOnBorrow = value;
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -1043,7 +1036,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
}
|
||||
|
||||
private void validationQueryCheck() {
|
||||
if (!(isTestOnBorrow() || isTestOnReturn() || isTestWhileIdle())) {
|
||||
if (!(testOnBorrow || testOnReturn || testWhileIdle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1051,21 +1044,21 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getValidationQuery() != null && this.getValidationQuery().length() > 0) {
|
||||
if (this.validationQuery != null && this.validationQuery.length() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String errorMessage = "";
|
||||
|
||||
if (isTestOnBorrow()) {
|
||||
if (testOnBorrow) {
|
||||
errorMessage += "testOnBorrow is true, ";
|
||||
}
|
||||
|
||||
if (isTestOnReturn()) {
|
||||
if (testOnReturn) {
|
||||
errorMessage += "testOnReturn is true, ";
|
||||
}
|
||||
|
||||
if (isTestWhileIdle()) {
|
||||
if (testWhileIdle) {
|
||||
errorMessage += "testWhileIdle is true, ";
|
||||
}
|
||||
|
||||
@@ -1269,15 +1262,19 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
}
|
||||
|
||||
if (testWhileIdle) {
|
||||
final long currentTimeMillis = System.currentTimeMillis();
|
||||
final long lastActiveTimeMillis = poolableConnection.holder.lastActiveTimeMillis;
|
||||
final long idleMillis = currentTimeMillis - lastActiveTimeMillis;
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
long lastActiveTimeMillis = poolableConnection.holder.lastActiveTimeMillis;
|
||||
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
|
||||
|
||||
long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
|
||||
|
||||
if (timeBetweenEvictionRunsMillis <= 0) {
|
||||
timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
|
||||
}
|
||||
|
||||
if (idleMillis >= timeBetweenEvictionRunsMillis) {
|
||||
if (idleMillis >= timeBetweenEvictionRunsMillis
|
||||
|| idleMillis < 0 // unexcepted branch
|
||||
) {
|
||||
boolean validate = testConnectionInternal(poolableConnection.conn);
|
||||
if (!validate) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
@@ -1287,6 +1284,17 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
discardConnection(realConnection);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JdbcConstants.MYSQL_DRIVER.equals(driverClass)) { // unexcepted branch
|
||||
long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(realConnection);
|
||||
long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
|
||||
if (lastPacketReceivedTimeMs > 0 //
|
||||
&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) {
|
||||
discardConnection(realConnection);
|
||||
LOG.warn("discard long time none received connection. millis : " + mysqlIdleMillis);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2029,9 +2037,9 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
value.setValidConnectionCheckerClassName(this.getValidConnectionCheckerClassName());
|
||||
value.setExceptionSorterClassName(this.getExceptionSorterClassName());
|
||||
|
||||
value.setTestOnBorrow(this.isTestOnBorrow());
|
||||
value.setTestOnReturn(this.isTestOnReturn());
|
||||
value.setTestWhileIdle(this.isTestWhileIdle());
|
||||
value.setTestOnBorrow(this.testOnBorrow);
|
||||
value.setTestOnReturn(this.testOnReturn);
|
||||
value.setTestWhileIdle(this.testWhileIdle);
|
||||
|
||||
value.setDefaultAutoCommit(this.isDefaultAutoCommit());
|
||||
|
||||
@@ -2138,7 +2146,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
// addLast
|
||||
lock.lock();
|
||||
try {
|
||||
if (closed) {
|
||||
if (closed || closing) {
|
||||
createTaskCount--;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2198,6 +2207,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
}
|
||||
|
||||
this.errorCount = 0; // reset errorCount
|
||||
if (closing || closed) {
|
||||
createTaskCount--;
|
||||
return;
|
||||
}
|
||||
createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);
|
||||
return;
|
||||
}
|
||||
@@ -2228,6 +2241,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
}
|
||||
|
||||
this.errorCount = 0; // reset errorCount
|
||||
if (closing || closed) {
|
||||
createTaskCount--;
|
||||
return;
|
||||
}
|
||||
createSchedulerFuture = createScheduler.schedule(this, timeBetweenConnectErrorMillis, TimeUnit.MILLISECONDS);
|
||||
return;
|
||||
}
|
||||
@@ -2247,6 +2264,9 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
// unknow fatal exception
|
||||
setFailContinuous(true);
|
||||
break;
|
||||
} catch (Throwable e) {
|
||||
LOG.error("create connection unexecpted error.", e);
|
||||
break;
|
||||
}
|
||||
|
||||
if (physicalConnection == null) {
|
||||
@@ -2685,7 +2705,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
}
|
||||
|
||||
if (validate) {
|
||||
holer.setLastActiveTimeMillis(System.currentTimeMillis());
|
||||
holer.lastActiveTimeMillis = System.currentTimeMillis();
|
||||
put(holer);
|
||||
} else {
|
||||
JdbcUtils.close(connection);
|
||||
@@ -2964,7 +2984,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
|
||||
// 15 - 19
|
||||
map.put("TestOnReturn", this.isTestOnReturn());
|
||||
map.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis());
|
||||
map.put("MinEvictableIdleTimeMillis", this.minEvictableIdleTimeMillis);
|
||||
map.put("ConnectErrorCount", this.getConnectErrorCount());
|
||||
map.put("CreateTimespanMillis", this.getCreateTimespanMillis());
|
||||
map.put("DbType", this.dbType);
|
||||
@@ -3110,8 +3130,8 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat
|
||||
dataMap.put("MaxWaitThreadCount", this.getMaxWaitThreadCount());
|
||||
dataMap.put("PoolPreparedStatements", this.isPoolPreparedStatements());
|
||||
dataMap.put("MaxPoolPreparedStatementPerConnectionSize", this.getMaxPoolPreparedStatementPerConnectionSize());
|
||||
dataMap.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis());
|
||||
dataMap.put("MaxEvictableIdleTimeMillis", this.getMaxEvictableIdleTimeMillis());
|
||||
dataMap.put("MinEvictableIdleTimeMillis", this.minEvictableIdleTimeMillis);
|
||||
dataMap.put("MaxEvictableIdleTimeMillis", this.maxEvictableIdleTimeMillis);
|
||||
|
||||
dataMap.put("LogDifferentThread", isLogDifferentThread());
|
||||
dataMap.put("RecycleErrorCount", getRecycleErrorCount());
|
||||
|
||||
@@ -1206,16 +1206,16 @@ public class DruidPooledConnection extends PoolableWrapper implements javax.sql.
|
||||
|
||||
final void beforeExecute() {
|
||||
final DruidConnectionHolder holder = this.holder;
|
||||
if (holder != null && holder.getDataSource().isRemoveAbandoned()) {
|
||||
if (holder != null && holder.dataSource.removeAbandoned) {
|
||||
running = true;
|
||||
}
|
||||
}
|
||||
|
||||
final void afterExecute() {
|
||||
final DruidConnectionHolder holder = this.holder;
|
||||
if (holder != null && holder.getDataSource().isRemoveAbandoned()) {
|
||||
if (holder != null && holder.dataSource.removeAbandoned) {
|
||||
running = false;
|
||||
holder.setLastActiveTimeMillis(System.currentTimeMillis());
|
||||
holder.lastActiveTimeMillis = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement
|
||||
|
||||
return poolableResultSet;
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -253,6 +255,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement
|
||||
try {
|
||||
return stmt.executeUpdate();
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -493,6 +497,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement
|
||||
try {
|
||||
return stmt.execute();
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -559,6 +565,8 @@ public class DruidPooledPreparedStatement extends DruidPooledStatement implement
|
||||
try {
|
||||
return stmt.executeBatch();
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.alibaba.druid.pool;
|
||||
|
||||
import com.alibaba.druid.VERSION;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
@@ -225,6 +226,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
|
||||
return poolableResultSet;
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -242,12 +245,33 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.executeUpdate(sql);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void errorCheck(Throwable t) {
|
||||
String errorClassName = t.getClass().getName();
|
||||
if (errorClassName.endsWith(".CommunicationsException")
|
||||
&& conn.holder != null
|
||||
&& conn.holder.dataSource.testWhileIdle)
|
||||
{
|
||||
DruidConnectionHolder holder = conn.holder;
|
||||
DruidAbstractDataSource dataSource = holder.dataSource;
|
||||
|
||||
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
|
||||
long idleMillis = System.currentTimeMillis() - lastActiveTimeMillis;
|
||||
|
||||
LOG.error("CommunicationsException, druid version " + VERSION.getVersionNumber()
|
||||
+ ", jdbcUrl : " + dataSource.jdbcUrl
|
||||
+ ", testWhileIdle " + dataSource.testWhileIdle
|
||||
+ ", idle millis " + idleMillis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
checkOpen();
|
||||
@@ -259,6 +283,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.executeUpdate(sql, autoGeneratedKeys);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -276,6 +302,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.executeUpdate(sql, columnIndexes);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -293,6 +321,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.executeUpdate(sql, columnNames);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -310,6 +340,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.execute(sql, autoGeneratedKeys);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -327,6 +359,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.execute(sql, columnIndexes);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -344,6 +378,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.execute(sql, columnNames);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
@@ -496,6 +532,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.execute(sql);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
}
|
||||
}
|
||||
@@ -653,6 +691,8 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
try {
|
||||
return stmt.executeBatch();
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
|
||||
throw checkException(t);
|
||||
} finally {
|
||||
conn.afterExecute();
|
||||
|
||||
@@ -16,15 +16,11 @@
|
||||
package com.alibaba.druid.util;
|
||||
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.ast.SQLStatement;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
|
||||
import com.mysql.jdbc.ConnectionImpl;
|
||||
import com.mysql.jdbc.MySQLConnection;
|
||||
import com.mysql.jdbc.MysqlIO;
|
||||
|
||||
import javax.sql.XAConnection;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.*;
|
||||
@@ -234,4 +230,78 @@ public class MySqlUtils {
|
||||
}
|
||||
return SQLUtils.toSQLString(stmtList, JdbcConstants.MYSQL);
|
||||
}
|
||||
|
||||
private static Class class_connectionImpl = null;
|
||||
private static boolean class_connectionImpl_Error = false;
|
||||
private static Method method_getIO = null;
|
||||
private static boolean method_getIO_error = false;
|
||||
private static Class class_MysqlIO = null;
|
||||
private static boolean class_MysqlIO_Error = false;
|
||||
private static Method method_getLastPacketReceivedTimeMs = null;
|
||||
private static boolean method_getLastPacketReceivedTimeMs_error = false;
|
||||
|
||||
public static long getLastPacketReceivedTimeMs(Connection conn) throws SQLException {
|
||||
if (class_connectionImpl == null && !class_connectionImpl_Error) {
|
||||
try {
|
||||
class_connectionImpl = Utils.loadClass("com.mysql.jdbc.ConnectionImpl");
|
||||
} catch (Throwable error){
|
||||
class_connectionImpl_Error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (class_connectionImpl == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (method_getIO == null && !method_getIO_error) {
|
||||
try {
|
||||
method_getIO = class_connectionImpl.getMethod("getIO");
|
||||
} catch (Throwable error){
|
||||
method_getIO_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (method_getIO == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (class_MysqlIO == null && !class_MysqlIO_Error) {
|
||||
try {
|
||||
class_MysqlIO = Utils.loadClass("com.mysql.jdbc.MysqlIO");
|
||||
} catch (Throwable error){
|
||||
class_MysqlIO_Error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (class_MysqlIO == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (method_getLastPacketReceivedTimeMs == null && !method_getLastPacketReceivedTimeMs_error) {
|
||||
try {
|
||||
Method method = class_MysqlIO.getDeclaredMethod("getLastPacketReceivedTimeMs");
|
||||
method.setAccessible(true);
|
||||
method_getLastPacketReceivedTimeMs = method;
|
||||
} catch (Throwable error){
|
||||
method_getLastPacketReceivedTimeMs_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (method_getLastPacketReceivedTimeMs == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
Object connImpl = conn.unwrap(class_connectionImpl);
|
||||
Object mysqlio = method_getIO.invoke(connImpl);
|
||||
Long ms = (Long) method_getLastPacketReceivedTimeMs.invoke(mysqlio);
|
||||
return ms.longValue();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new SQLException("getLastPacketReceivedTimeMs error", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SQLException("getLastPacketReceivedTimeMs error", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new SQLException("getLastPacketReceivedTimeMs error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user