tx-lcn icon indicating copy to clipboard operation
tx-lcn copied to clipboard

LCN模式下使用mysql主从模式导致事物不提交

Open jiangruihao111 opened this issue 4 years ago • 1 comments

jiangruihao111 avatar Mar 24 '20 06:03 jiangruihao111

我们的场景是 在参与者中使用了多数据源

场景

我们自定义了读写分离的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);
	}
}

而连接池的DataSourcegetConnection()方法会一起被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),这也就是为什么业务中回出现链接无法CommitClose的原因了。

解决

我们尝试性的做了一个简单的容错,虽然不是根本的解决问题,只是可以解决当前场景下的问题,希望可以帮助到问题的定位

我们的处理方式如下

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;
        }
    }
}

foolishchow avatar Mar 26 '20 03:03 foolishchow