Skip to content

Commit

Permalink
feat(DruidDataSource): Add configuration version tracking and validation
Browse files Browse the repository at this point in the history
Add a configuration version (`configVersion`) to track changes in DataSource configurations. Update methods to increment the configuration version when settings are modified. Enhance connection handling logic to validate the configuration version before returning connections to clients, discarding outdated connections as necessary.
  • Loading branch information
wenshao committed Feb 21, 2025
1 parent c9b6600 commit 21aa6d8
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 49 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/com/alibaba/druid/VERSION.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
public final class VERSION {
public static final int MajorVersion = 1;
public static final int MinorVersion = 2;
public static final int RevisionVersion = 24;
public static final int RevisionVersion = 25;

public static String getVersionNumber() {
return VERSION.MajorVersion + "." + VERSION.MinorVersion + "." + VERSION.RevisionVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements
protected volatile int maxPoolPreparedStatementPerConnectionSize = 10;

protected volatile boolean inited;
protected volatile int configVersion;
protected volatile boolean initExceptionThrow = true;

protected PrintWriter logWriter = new PrintWriter(System.out);
Expand Down Expand Up @@ -190,6 +191,8 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements

private volatile FilterChainImpl filterChain;

static final AtomicIntegerFieldUpdater<DruidAbstractDataSource> versionUpdater = AtomicIntegerFieldUpdater.newUpdater(DruidAbstractDataSource.class, "configVersion");

static final AtomicLongFieldUpdater<DruidAbstractDataSource> errorCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "errorCount");
static final AtomicLongFieldUpdater<DruidAbstractDataSource> dupCloseCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "dupCloseCount");
static final AtomicLongFieldUpdater<DruidAbstractDataSource> startTransactionCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidAbstractDataSource.class, "startTransactionCount");
Expand Down Expand Up @@ -1170,7 +1173,7 @@ public void setUsername(String username) {
}

if (inited) {
throw new UnsupportedOperationException();
versionUpdater.incrementAndGet(this);
}

this.username = username;
Expand Down Expand Up @@ -1238,7 +1241,7 @@ public void setUrl(String jdbcUrl) {
}

if (inited) {
throw new UnsupportedOperationException();
versionUpdater.incrementAndGet(this);
}

if (jdbcUrl != null) {
Expand Down Expand Up @@ -2291,11 +2294,11 @@ public void setInitExceptionThrow(boolean initExceptionThrow) {
}

public static class PhysicalConnectionInfo {
private Connection connection;
private long connectStartNanos;
private long connectedNanos;
private long initedNanos;
private long validatedNanos;
private final Connection connection;
private final long connectStartNanos;
private final long connectedNanos;
private final long initedNanos;
private final long validatedNanos;
private Map<String, Object> vairiables;
private Map<String, Object> globalVairiables;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public final class DruidConnectionHolder {
protected boolean underlyingAutoCommit;
protected volatile boolean discard;
protected volatile boolean active;
protected final int configVersion;
protected final Map<String, Object> variables;
protected final Map<String, Object> globalVariables;
final ReentrantLock lock = new ReentrantLock();
Expand All @@ -99,6 +100,18 @@ public DruidConnectionHolder(DruidAbstractDataSource dataSource, PhysicalConnect
);
}

public DruidConnectionHolder(DruidAbstractDataSource dataSource, PhysicalConnectionInfo pyConnectInfo, int configVersion)
throws SQLException {
this(
dataSource,
pyConnectInfo.getPhysicalConnection(),
pyConnectInfo.getConnectNanoSpan(),
configVersion,
pyConnectInfo.getVairiables(),
pyConnectInfo.getGlobalVairiables()
);
}

public DruidConnectionHolder(DruidAbstractDataSource dataSource, Connection conn, long connectNanoSpan)
throws SQLException {
this(dataSource, conn, connectNanoSpan, null, null);
Expand All @@ -110,12 +123,24 @@ public DruidConnectionHolder(
long connectNanoSpan,
Map<String, Object> variables,
Map<String, Object> globalVariables
) throws SQLException {
this(dataSource, conn, connectNanoSpan, 0, variables, globalVariables);
}

public DruidConnectionHolder(
DruidAbstractDataSource dataSource,
Connection conn,
long connectNanoSpan,
int configVersion,
Map<String, Object> variables,
Map<String, Object> globalVariables
) throws SQLException {
this.dataSource = dataSource;
this.conn = conn;
this.createNanoSpan = connectNanoSpan;
this.variables = variables;
this.globalVariables = globalVariables;
this.configVersion = configVersion;

this.connectTimeMillis = System.currentTimeMillis();
this.lastActiveTimeMillis = connectTimeMillis;
Expand Down
65 changes: 44 additions & 21 deletions core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,26 @@ public void restart(Properties properties) throws SQLException {
this.closed = false;

if (properties != null) {
configFromPropeties(properties);
DruidDataSourceUtils.configFromProperties(this, properties);
}
} finally {
lock.unlock();
}
}

public void config(Properties properties) throws SQLException {
if (properties == null || properties.isEmpty()) {
return;
}

lock.lock();
try {
DruidDataSourceUtils.configFromProperties(this, properties);
} finally {
lock.unlock();
}
}

public void resetStat() {
if (!isResetStatEnable()) {
return;
Expand Down Expand Up @@ -598,7 +611,7 @@ public void init() throws SQLException {
while (poolingCount < initialSize) {
try {
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo, configVersion);
connections[poolingCount++] = holder;
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
Expand Down Expand Up @@ -1178,9 +1191,9 @@ public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLE
int notFullTimeoutRetryCnt = 0;
for (; ; ) {
// handle notFullTimeoutRetry
DruidPooledConnection poolableConnection;
DruidPooledConnection conn;
try {
poolableConnection = getConnectionInternal(maxWaitMillis);
conn = getConnectionInternal(maxWaitMillis);
} catch (GetConnectionTimeoutException ex) {
if (notFullTimeoutRetryCnt < this.notFullTimeoutRetryCount && !isFull()) {
notFullTimeoutRetryCnt++;
Expand All @@ -1192,24 +1205,29 @@ public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLE
throw ex;
}

if (conn.holder.configVersion < configVersion) {
discardConnection(conn.holder);
continue;
}

if (testOnBorrow) {
boolean validated = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
boolean validated = testConnectionInternal(conn.holder, conn.conn);
if (!validated) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validated connection.");
}

discardConnection(poolableConnection.holder);
discardConnection(conn.holder);
continue;
}
} else {
if (poolableConnection.conn.isClosed()) {
discardConnection(poolableConnection.holder); // 传入null,避免重复关闭
if (conn.conn.isClosed()) {
discardConnection(conn.holder); // 传入null,避免重复关闭
continue;
}

if (testWhileIdle) {
final DruidConnectionHolder holder = poolableConnection.holder;
final DruidConnectionHolder holder = conn.holder;
long currentTimeMillis = System.currentTimeMillis();
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
long lastExecTimeMillis = holder.lastExecTimeMillis;
Expand All @@ -1229,13 +1247,13 @@ public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLE
if (idleMillis >= timeBetweenEvictionRunsMillis
|| idleMillis < 0 // unexcepted branch
) {
boolean validated = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
boolean validated = testConnectionInternal(conn.holder, conn.conn);
if (!validated) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validated connection.");
}

discardConnection(poolableConnection.holder);
discardConnection(conn.holder);
continue;
}
}
Expand All @@ -1244,23 +1262,23 @@ public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLE

if (removeAbandoned) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
poolableConnection.connectStackTrace = stackTrace;
poolableConnection.setConnectedTimeNano();
poolableConnection.traceEnable = true;
conn.connectStackTrace = stackTrace;
conn.setConnectedTimeNano();
conn.traceEnable = true;

activeConnectionLock.lock();
try {
activeConnections.put(poolableConnection, PRESENT);
activeConnections.put(conn, PRESENT);
} finally {
activeConnectionLock.unlock();
}
}

if (!this.defaultAutoCommit) {
poolableConnection.setAutoCommit(false);
conn.setAutoCommit(false);
}

return poolableConnection;
return conn;
}
}

Expand Down Expand Up @@ -1379,7 +1397,7 @@ private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLExce
createStartNanosUpdater.set(this, System.nanoTime());
if (creatingCountUpdater.compareAndSet(this, 0, 1)) {
PhysicalConnectionInfo pyConnInfo = DruidDataSource.this.createPhysicalConnection();
holder = new DruidConnectionHolder(this, pyConnInfo);
holder = new DruidConnectionHolder(this, pyConnInfo, configVersion);

creatingCountUpdater.decrementAndGet(this);
directCreateCountUpdater.incrementAndGet(this);
Expand Down Expand Up @@ -1767,7 +1785,8 @@ protected void recycle(DruidPooledConnection pooledConnection) throws SQLExcepti
return;
}

if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
if (holder.configVersion < this.configVersion
|| (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount)) {
discardConnection(holder);
return;
}
Expand Down Expand Up @@ -2296,7 +2315,7 @@ public long getRemoveAbandonedCount() {
protected boolean put(PhysicalConnectionInfo physicalConnectionInfo) {
DruidConnectionHolder holder = null;
try {
holder = new DruidConnectionHolder(DruidDataSource.this, physicalConnectionInfo);
holder = new DruidConnectionHolder(DruidDataSource.this, physicalConnectionInfo, configVersion);
} catch (SQLException ex) {
lock.lock();
try {
Expand Down Expand Up @@ -3297,6 +3316,10 @@ public String getVersion() {
return VERSION.getVersionNumber();
}

public int getConfigVersion() {
return this.configVersion;
}

@Override
public JdbcDataSourceStat getDataSourceStat() {
return dataSourceStat;
Expand Down Expand Up @@ -3634,7 +3657,7 @@ public int fill(int toCount) throws SQLException {
DruidConnectionHolder holder;
try {
PhysicalConnectionInfo pyConnInfo = createPhysicalConnection();
holder = new DruidConnectionHolder(this, pyConnInfo);
holder = new DruidConnectionHolder(this, pyConnInfo, configVersion);
} catch (SQLException e) {
LOG.error("fill connection error, url: " + sanitizedUrl(this.jdbcUrl), e);
connectErrorCountUpdater.incrementAndGet(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,10 @@ public void test_prepareStatement_error() throws Exception {

conn.close();

{
Exception error = null;
try {
dataSource.setUsername("xxx");
} catch (Exception ex) {
error = ex;
}
Assert.assertNotNull(error);
}
int configVersion = dataSource.getConfigVersion();
dataSource.setUsername("xxx");

assertEquals(configVersion + 1, dataSource.getConfigVersion());
}

public void test_error_1() throws Exception {
Expand Down Expand Up @@ -214,20 +209,30 @@ public void test_error_10() throws Exception {
}

public void test_error_11() throws Exception {
DruidPooledConnection conn = dataSource.getConnection().unwrap(DruidPooledConnection.class);

conn.close();

{
long discardCount = dataSource.getDiscardCount();
DruidPooledConnection conn = dataSource.getConnection().unwrap(DruidPooledConnection.class);
conn.close();
assertEquals(discardCount, dataSource.getDiscardCount());
}
dataSource.getUrl();

{
Exception error = null;
try {
dataSource.setUrl("x");
} catch (Exception ex) {
error = ex;
}
Assert.assertNotNull(error);
int configVersion = dataSource.getConfigVersion();
dataSource.setUrl("jdbc:mock:xxx1");
assertEquals(configVersion + 1, dataSource.getConfigVersion());
}

{
DruidPooledConnection conn = dataSource.getConnection().unwrap(DruidPooledConnection.class);

long discardCount = dataSource.getDiscardCount();
int configVersion = dataSource.getConfigVersion();
dataSource.setUrl("jdbc:mock:xxx2");
assertEquals(configVersion + 1, dataSource.getConfigVersion());

conn.close();
assertEquals(1, dataSource.getDiscardCount() - discardCount);
}
}

Expand Down

0 comments on commit 21aa6d8

Please sign in to comment.