tx-lcn
tx-lcn copied to clipboard
LCN模式下使用mysql主从模式导致事物不提交
我们的场景是 在参与者中使用了多数据源
场景
我们自定义了读写分离的DataSource
,继承了org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
,
问题
AbstractRoutingDataSource
这个类实现了javax.sql.DataSource
,而他的getConnection()
的实现方法中调用了连接池的DataSource
中的getConnection()
方法,如下
package org.springframework.jdbc.datasource.lookup;
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
}
而连接池的DataSource
的getConnection()
方法会一起被DataSourceAspect
这个环绕嵌套触发两次,继而在LcnTransactionResourceProxy
中代理Connection
的时候发生被两次代理
public class LcnTransactionResourceProxy implements TransactionResourceProxy {
// ...
@Override
public Connection proxyConnection(ConnectionCallback connectionCallback) throws Throwable {
String groupId = DTXLocalContext.cur().getGroupId();
try {
return globalContext.getLcnConnection(groupId);
} catch (TCGlobalContextException e) {
// 这里为什么会触发两次我也没有搞清楚
LcnConnectionProxy lcnConnectionProxy = new LcnConnectionProxy(connectionCallback.call());
globalContext.setLcnConnection(groupId, lcnConnectionProxy);
lcnConnectionProxy.setAutoCommit(false);
return lcnConnectionProxy;
}
}
}
最后我们拿到的Connection
变成了new LcnConnectionProxy(new LcnConnectionProxy(originConnection))
,而正确的应该是new LcnConnectionProxy(originConnection)
,这也就是为什么业务中回出现链接无法Commit
和Close
的原因了。
解决
我们尝试性的做了一个简单的容错,虽然不是根本的解决问题,只是可以解决当前场景下的问题,希望可以帮助到问题的定位
我们的处理方式如下
package com.codingapi.txlcn.tc.core.transaction.lcn.resource;
//......
@Service(value = "transaction_lcn")
@Slf4j
public class LcnTransactionResourceProxy implements TransactionResourceProxy {
//...
@Override
public Connection proxyConnection(ConnectionCallback connectionCallback) throws Throwable {
String groupId = DTXLocalContext.cur().getGroupId();
try {
return globalContext.getLcnConnection(groupId);
} catch (TCGlobalContextException e) {
Connection currentConnection = connectionCallback.call();
//这里是我们的容错
if(currentConnection instanceof LcnConnectionProxy){
LcnConnectionProxy lcnConnectionProxy = (LcnConnectionProxy)currentConnection;
globalContext.setLcnConnection(groupId, lcnConnectionProxy );
lcnConnectionProxy.setAutoCommit(false);
return lcnConnectionProxy;
}
LcnConnectionProxy lcnConnectionProxy = new LcnConnectionProxy(connectionCallback.call());
globalContext.setLcnConnection(groupId, lcnConnectionProxy);
lcnConnectionProxy.setAutoCommit(false);
return lcnConnectionProxy;
}
}
}