Как решить HeuristicMixedException в Narayana после обновления стека?

Вопрос или проблема

Я работал над приложением на основе Java Spring, которое управляет транзакциями с использованием Narayana. Кодовая база очень обширна, и нам только что сказали обновить стек. Ни у одного из членов моей команды нет экспертизы и глубоких знаний о кодовой базе. Недавно я обновил наш стек, который включает MySQL, JMS и Narayana, среди прочего. После обновления я столкнулся с неожиданными ошибками HeuristicMixedException во время коммита транзакций. Те же конфигурации работали без проблем в предыдущем стеке.

Обновленные неработающие версии стека:

  • Narayana: 5.10.6
  • MySQL Connector/J: 8.4.0
  • Spring: 5.3.x
  • Hibernate: 5.3.x
  • Java: Amazon Corretto JDK 11

Старый работающий стек:

  • Narayana: 5.9.2
  • MySQL Connector/J: 5.1.44
  • Spring: 4.3.10
  • Hibernate: 4.2.1
  • Java: Amazon Corretto JDK 8

Я не уверен, является ли исключение результатом изменений в конфигурации или чем-то связанным с обновлением.

Подробности проблемы

После обновления я начал видеть ошибки HeuristicMixedException в логах. В этом корпоративном приложении происходит множество действий при запуске сервера, и некоторые из них, похоже, связаны с некоторыми запросами базы данных на основе XA-соединений и некоторыми аспектами spring jms. Что мы наблюдали, так это то, что конфигурация приложения включает:

  1. Подключение MySQL, настроенное как ресурс XA и NON-XA в Narayana.

  2. Подключение JMS, настроенное как ресурс non-XA, которое я намерен оставить без изменений, поскольку это был работающий код в старом стеке. Вот фрагмент стека вызовов из логов:

    2024-11-01 22:15:43,057 DEBUG [org.springframework.transaction.jta.JtaTransactionManager] [Catalina-utility-1] [] Создание новой транзакции с именем [com.abcd.riskmanager.jpa.dao.ConfigDao.findAll]: PROPAGATION_REQUIRES_NEW, ISOLATION_DEFAULT 2024-11-01 22:15:43,057 DEBUG [org.springframework.orm.jpa.EntityManagerFactoryUtils] [Catalina-utility-1] [] Открытие JPA EntityManager 2024-11-01 22:15:43,058 DEBUG [com.abcd.Enterprise.spring.jta.cache.TransactionalContextResourceRegistryCache] [Catalina-utility-1] [] Ресурс недоступен в кэше для ключа: DS20:ffff0aab40c5:a21c:67259939:4ac 2024-11-01 22:15:43,071 DEBUG [com.abcd.Enterprise.spring.jta.cache.TransactionalContextResourceRegistryCache] [Catalina-utility-1] [] Кешированный ресурс оберточный ресурс: com.arjuna.ats.internal.jdbc.ConnectionImple@65b48420 кеширован для ключа DS20:ffff0aab40c5:a21c:67259939:4ac 2024-11-01 22:15:43,075 WARN [com.abcd.rearch.tcserver.jdbc.pool.ENTERPRISEConnectionPoolInterceptor] [Catalina-utility-1] [] Обнаружена активная транзакция < formatId=131077, gtrid_length=31, bqual_length=36, tx_uid=0:ffff0aab40c5:a21c:67259939:4ac, node_name=i91, branch_uid=0:ffff0aab40c5:a21c:67259939:4af, subordinatenodename=null, eis_name=0 > в соединении, PooledConnection[com.mysql.cj.jdbc.ConnectionWrapper@6f0f1e46]. Соединение будет отклонено 2024-11-01 22:15:43,085 DEBUG [org.springframework.transaction.jta.JtaTransactionManager] [Catalina-utility-1] [] Инициация коммита транзакции 2024-11-01 22:15:43,097 WARN [com.abcd.riskmanager.decision.component.config.ConfigurationManager] [Catalina-utility-1] [] ошибка настройки конфигурации по умолчанию в памяти, используется менеджер конфигурации по умолчанию org.springframework.transaction.HeuristicCompletionException: Онтология завершения: состояние результата смешанное; вложенное исключение это javax.transaction.HeuristicMixedException at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1042) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:637) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:390) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:134) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) at com.sun.proxy.$Proxy146.findAll(Unknown Source) at com.abcd.riskmanager.decision.component.config.InMemoryConfigurationManager.(InMemoryConfigurationManager.java:45) at com.abcd.riskmanager.decision.component.config.TreeCacheConfigurationManager.(TreeCacheConfigurationManager.java:36) at com.abcd.riskmanager.decision.component.config.ConfigurationManager.getInstance(ConfigurationManager.java:164) at com.abcd.riskmanager.decision.strategy.TreeCacheStrategyCache.(TreeCacheStrategyCache.java:51) at com.abcd.riskmanager.decision.strategy.TranslatedStrategyCache.(TranslatedStrategyCache.java:28) at com.abcd.riskmanager.decision.component.workflow.WorkflowEngine.dbLookupStrategy(WorkflowEngine.java:674) at com.abcd.riskmanager.decision.component.workflow.PlugInManager.findPlugIns(PlugInManager.java:368) at com.abcd.riskmanager.decision.component.workflow.PlugInManager.init(PlugInManager.java:125) at com.abcd.riskmanager.decision.entry.StartupShutdownServlet.init(StartupShutdownServlet.java:57) at javax.servlet.GenericServlet.init(GenericServlet.java:143) at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1106) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1063) at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:960) at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4641) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4948) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:683) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:658) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:661) at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:680) at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1844) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) at java.base/java.lang.Thread.run(Thread.java:829) Причина: javax.transaction.HeuristicMixedException at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1303) at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126) at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1035) … 36 еще Ст suppressed: com.mysql.cj.jdbc.MysqlXAException: Произошла неопределенная ошибка в базовом соединении – проверьте ваши данные на согласованность at com.mysql.cj.jdbc.MysqlXAConnection.mapXAExceptionFromSQLException(MysqlXAConnection.java:337) at com.mysql.cj.jdbc.MysqlXAConnection.dispatchCommand(MysqlXAConnection.java:319) at com.mysql.cj.jdbc.MysqlXAConnection.commit(MysqlXAConnection.java:296) at com.mysql.cj.jdbc.SuspendableXAConnection.commit(SuspendableXAConnection.java:98) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Нативный метод) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at com.abcd.rearch.tcserver.jdbc.pool.ENTERPRISEDataSourceXAResourceHandler.invoke(ENTERPRISEDataSourceXAResourceHandler.java:63) at com.sun.proxy.$Proxy197.commit(Неизвестный источник) at com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord.topLevelOnePhaseCommit(XAResourceRecord.java:702) at com.arjuna.ats.arjuna.coordinator.BasicAction.onePhaseCommit(BasicAction.java:2395) at com.arjuna.ats.arjuna.coordinator.BasicAction.End(BasicAction.java:1497) at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:96) at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162) at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1295) … 38 еще Причина: java.sql.SQLNonTransientConnectionException: Операции не разрешены после закрытия соединения. at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:102) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:81) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:55) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:65) at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64) at com.mysql.cj.jdbc.ConnectionImpl.createStatement(ConnectionImpl.java:1103) at com.mysql.cj.jdbc.ConnectionImpl.createStatement(ConnectionImpl.java:1094) at com.mysql.cj.jdbc.MysqlXAConnection.dispatchCommand(MysqlXAConnection.java:311) … 52 еще Причина: com.mysql.cj.exceptions.ConnectionIsClosedException: Операции не разрешены после закрытия соединения. at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Нативный метод) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:52) at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:95) at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:140) at com.mysql.cj.NativeSession.checkClosed(NativeSession.java:915) at com.mysql.cj.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:586) at com.mysql.cj.jdbc.ConnectionImpl.createStatement(ConnectionImpl.java) … Конфигурации

Свойства Narayana Вот мои текущие свойства Narayana:

<properties>
>     <entry key="CoordinatorEnvironmentBean.defaultTimeout">9000</entry>
>     <entry key="CoordinatorEnvironmentBean.txReaperTimeout">120000</entry>
>     <entry key="CoordinatorEnvironmentBean.txReaperMode">DYNAMIC</entry>
>     <entry key="CoordinatorEnvironmentBean.commitOnePhase">YES</entry>
>     <entry key="ObjectStoreEnvironmentBean.localOSRoot">defaultStore</entry>
>     <entry key="ObjectStoreEnvironmentBean.objectStoreDir">${arjuna.objectstore}</entry>
>     <entry key="ObjectStoreEnvironmentBean.objectStoreSync">ON</entry>
>     <entry key="ObjectStoreEnvironmentBean.transactionSync">ON</entry>
>     <entry key="CoreEnvironmentBean.nodeIdentifier">i91</entry>
>     <entry key="JTAEnvironmentBean.allowMultipleLastResources">true</entry>
>     <entry key="narayana.transactionManager.enableHeuristicCommit">true</entry>
>     <entry key="narayana.transactionManager.enableHeuristicRollback">true</entry>
> </properties>

Ключевые свойства, которые я добавил, чтобы потенциально решить проблему эвристики, не принесли удачи:

JTAEnvironmentBean.allowMultipleLastResources: установлено в true, чтобы разрешить единственному non-XA ресурсу быть “последним ресурсом” в XA транзакциях.
narayana.transactionManager.enableHeuristicCommit и narayana.transactionManager.enableHeuristicRollback: установлены в true, чтобы обрабатывать случаи, когда требуется частичный коммит или откат.

Конфигурация прослушивателя JMS в Spring Моя конфигурация прослушивателя JMS в Spring является non-XA, но работает в том же контексте транзакции, что и операции с XA базой данных:

Настройка EnterpriseDataSourceFactory У меня также есть класс EnterpriseDataSourceFactory для обработки XA соединений. Ниже представлена старая реализация, которая вызвала ошибку:

org.springframework.jdbc.CannotGetJdbcConnectionException: Не удалось получить JDBC соединение; вложенное исключение: java.sql.SQLException: java.sql.SQLException: java.sql.SQLException: Соединение из пула не реализует javax.sql.XAConnection at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:82) at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:646)

Поэтому мне пришлось изменить реализации этого класса, чтобы он работал в новом стеке. Не уверен, почему произошла ошибка, и связано ли это с текущим исключением эвристики, которое я получаю.

Старая реализация работала в старом стеке, а в новом стекe дала сбой:

import java.util.Properties;
import javax.naming.Context;
import javax.sql.DataSource;

import org.apache.tomcat.jdbc.pool.DataSourceFactory;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;

import com.mysql.cj.jdbc.MysqlXADataSource;

public class EnterpriseDataSourceFactory extends DataSourceFactory{
@Override
    public DataSource createDataSource(Properties properties,Context context, boolean XA) throws Exception {
        PoolConfiguration poolProperties = DataSourceFactory.parsePoolProperties(properties);
        if (poolProperties.getDataSourceJNDI()!=null && poolProperties.getDataSource()==null) {
            performJNDILookup(context, poolProperties);
        }
        org.apache.tomcat.jdbc.pool.DataSource dataSource = XA?
                new EnterpriseDataSource(poolProperties) :
                new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
        dataSource.createPool();
        return dataSource;
    }
}

Новая реализация:

public class EnterpriseDataSourceFactory extends DataSourceFactory{
    @Override
public DataSource createDataSource(Properties properties, Context context, boolean XA) throws Exception {
        PoolConfiguration poolProperties = DataSourceFactory.parsePoolProperties(properties);
         // Выполните JNDI поиск, если это необходимо
        if (poolProperties.getDataSourceJNDI() != null && poolProperties.getDataSource() == null) {
            performJNDILookup(context, poolProperties);
        }

    org.apache.tomcat.jdbc.pool.DataSource dataSource;
    if (XA) {
        MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
        mysqlXADataSource.setUrl(poolProperties.getUrl());
        mysqlXADataSource.setUser(poolProperties.getUsername());
        mysqlXADataSource.setPassword(poolProperties.getPassword());
        dataSource = new EnterpriseDataSource(poolProperties);
        ((ENTERPRISEDataSource) dataSource).setDataSource(mysqlXADataSource);
        } else {
        dataSource = new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
    }

    dataSource.createPool();
    return dataSource;
}}

Записи JNDI в server.xml:

<GlobalNamingResources>
    <!-- Редактируемая база данных пользователей, которую также можно использовать
             UserDatabaseRealm для аутентификации пользователей
    -->
        <Resource name="UserDatabase" 
            auth="Container"
            description="База данных пользователей, которую можно обновлять и сохранять"
            factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
            pathname="conf/tomcat-users.xml"
        />
        <Resource name="jdbc/EnterpriseTracking"
            auth="Container"
            type="javax.sql.DataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbTrackingSchema}${dbProperties}"
            minIdle="5"
            initialSize="5"
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
        />
        <Resource name="jdbc/EnterpriseDecisionLocalDataSource"
            auth="Container"
            type="javax.sql.DataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbDecisionSchema}${dbProperties}"
            minIdle="5"
            initialSize="5"
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
        />
        <Resource name="jdbc/EnterpriseRMDecisionDataSource"
            auth="Container"
            type="javax.sql.DataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbDecisionSchema}${dbProperties}"
            minIdle="5"
            initialSize="5"
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
        />
        <Resource name="jdbc/EnterpriseTrackingLocalDataSource"
            auth="Container"
            type="javax.sql.DataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbTrackingSchema}${dbProperties}"
            minIdle="5"
            initialSize="5"
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
        />
        <Resource name="jdbc/EnterpriseTrackingGenericDS"
            auth="Container"
                        xaDataSourceClassName="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSource"
                        type="com.mysql.cj.jdbc.MysqlXADataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            factory="org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory"
            user="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbTrackingSchema}${dbProperties}"
        />
        <Resource name="jdbc/EnterpriseDecisionGenericDS"
            auth="Container"
            type="com.mysql.cj.jdbc.MysqlXADataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            xaDataSourceClassName="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSource"
            factory="org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory"
            user="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbDecisionSchema}${dbProperties}"
        />
        <Resource name="jdbc/EnterpriseHop.DataSource"
            auth="Container"
            type="javax.sql.XADataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            xaDataSourceClassName="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSource"
            dataSourceJNDI="EnterpriseTrackingGenericDS"
            factory="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSourceFactory"
            jdbcInterceptors="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseConnectionPoolInterceptor"
            minIdle="5"
            initialSize="5" 
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbTrackingSchema}${dbProperties}"
        />
        <Resource name="jdbc/EnterpriseEnterprise.DataSource"
            auth="Container"
            type="javax.sql.XADataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            xaDataSourceClassName="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSource"
            dataSourceJNDI="EnterpriseTrackingGenericDS"
            factory="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSourceFactory"
            jdbcInterceptors="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseConnectionPoolInterceptor"
            minIdle="5"
            initialSize="5"
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbTrackingSchema}${dbProperties}"
        />
        <Resource name="jdbc/EnterpriseEnterpriseDataSource"
            auth="Container"
            type="javax.sql.XADataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            xaDataSourceClassName="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSource"
            dataSourceJNDI="EnterpriseTrackingGenericDS"
            factory="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSourceFactory"
            jdbcInterceptors="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseConnectionPoolInterceptor"
            minIdle="5"
            initialSize="5"
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbTrackingSchema}${dbProperties}"
        />
        <Resource name="jdbc/EnterpriseDecisionDataSource"
            auth="Container"
            type="javax.sql.XADataSource"
            driverClassName="com.mysql.cj.jdbc.Driver"
            xaDataSourceClassName="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSource"
            dataSourceJNDI="EnterpriseDecisionGenericDS"
            factory="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseDataSourceFactory"
            jdbcInterceptors="com.cybersource.rearch.tcserver.jdbc.pool.EnterpriseConnectionPoolInterceptor"
            minIdle="5"
            initialSize="5"
            maxActive="100"
            maxWait="60000"
            validationQuery="SELECT 1"
            validationQueryTimeout="5"
            testOnBorrow="true"
            testOnReturn="true"
            validationInterval="10000"
            timeBetweenEvictionRunsMillis="30000"
            testWhileIdle="true"
            minEvictableIdleTimeMillis="900000"
            removeAbandoned="true"
            removeAbandonedTimeout="300"
            username="${dbUserName}"
            password="${dbPassword}"
            url="${dbBaseUrl}${dbDecisionSchema}${dbProperties}"
        />
</GlobalNamingResources>

Пользовательская реализация источника данных

import java.sql.SQLException;

import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.XADataSource;

/**
 * Создает экземпляр {@link EnterpriseConnectionPool}
 **/
public class EnterpriseDataSource extends XADataSource {

    public EnterpriseDataSource(PoolConfiguration poolProperties) {
        super(poolProperties);
    }

    /**
     * Настраивает пул соединений, создавая пул драйвера.
     * 
     * @return Драйвер
     * @throws SQLException
     */
    public ConnectionPool createPool() throws SQLException {
        if (pool != null) {
            return pool;
        } else {
            return pCreatePool();
        }
    }

    /**
     * Настраивает пул соединений, создавая пул драйвера.
     * 
     * @return Драйвер
     * @throws SQLException
     */
    private synchronized ConnectionPool pCreatePool() throws SQLException {
        if (pool != null) {
            return pool;
        } else {
            pool = new EnterpriseConnectionPool(poolProperties);
            return pool;
        }
    }

}

Код такой обширный, и я не уверен, где именно сосредоточиться, не уверен, что предоставил все необходимые фрагменты для какого-либо руководства. Если необходимо, я мог бы предоставить больше деталей.

Вопросы

  1. Что может вызывать HeuristicMixedException после обновления, и есть ли проблема совместимости с последней версией Narayana, spring 5.3, hibernate 5.3 и MySQL Connector/J 8.4.0?
  2. Достаточно ли настроек allowMultipleLastResources и включение эвристического коммита/отката для того, чтобы Narayana управлял транзакциями, или есть дополнительные конфигурации, которые мне нужно учесть?
  3. Как я могу решить исключение XA соединения в EnterpriseDataSourceFactory с последним MySQL Connector/J? Я не уверен, но некоторые исследования предполагают, что исключение эвристики может быть вызвано смешиванием NON XA и XA транзакций. Я не уверен, так ли это в моем случае или нет. Любое руководство будет очень полезным! Спасибо!

Ответ или решение

Как разрешить HeuristicMixedException в Narayana после обновления стека

Введение

После обновления стека, содержащего Narayana, JMS, MySQL и другие компоненты, вы столкнулись с ошибками HeuristicMixedException во время выполнения операций с транзакциями. Это сообщение об ошибке может быть вызвано рядом факторов, связанных как с конфигурацией, так и с особенностями новых версий библиотек. Данная статья поможет вам разобраться в причинах возникновения данной проблемы и предложит рекомендации по ее разрешению.

Возможные причины возникновения HeuristicMixedException

  1. Проблемы с совместимостью: Обновленные версии Narayana (5.10.6), MySQL Connector/J (8.4.0), Spring (5.3.x) и Hibernate (5.3.x) могут иметь известные проблемы совместимости. Убедитесь, что все компоненты вашего стека поддерживают корректные версии библиотек.

  2. Соотношение XA и Non-XA ресурсов: Ошибка может возникать в случае, если в одной транзакции участвуют как XA, так и Non-XA ресурсы. Ваше приложение использует XA-соединение для MySQL и Non-XA для JMS, что может стать причиной возникновения проблемы при обновлении стека, поскольку в версиях Narayana были изменены правила работы с такими ресурсами.

  3. Конфигурационные ошибки: Вероятно, некоторые настройки Narayana, которые использовались в предыдущей версии, требуют изменений. Особенно обратите внимание на настройки, относящиеся к поддержке "последнего ресурса" и управления гандикапом.

Проверка и настройка конфигурации

  1. Обновление конфигурации Narayana:

    • Убедитесь, что следующие свойства установлены:
      • JTAEnvironmentBean.allowMultipleLastResources = true: Это позволит Narayana обрабатывать Non-XA ресурсы корректно.
      • narayana.transactionManager.enableHeuristicCommit = true и narayana.transactionManager.enableHeuristicRollback = true: Они помогут управлять ситуациями, когда возникает необходимость в частичном коммите или откате.
  2. Обратите внимание на код вашего приложения:

    • Если JMS работает не как XA ресурс, убедитесь, что на этапе транзакций не происходит попыток изменения состояния данных, если одна из являющихся XA ресурсов уже была изменена. Это может вызвать MixedOutcome в результате нарушения ACID свойств.

Исправление исключения XA соединения в EnterpriseDataSourceFactory

  1. Обновления класса EnterpriseDataSourceFactory:
    • Проверьте код для создания MysqlXADataSource. Убедитесь, что настройки, такие как URL, имя пользователя и пароль, правильно указаны и соответствуют конфигурации MySQL.
    • Убедитесь, что пул соединений правильно управляет открытием и закрытием соединений, чтобы исключения, как No operations allowed after connection closed, не влияли на поведение вашего приложения.

Рекомендуемые действия

  1. Тесты и отладка: Проведите тесты с различными конфигурациями, чтобы определить, влияют ли настройки XA/Non-XA ресурсов на возникновение ошибок. Тщательно проверьте логи на наличие соотношений между транзакциями и ошибками выполнения соединений.

  2. Журналирование: Увеличьте уровень журналирования для Narayana и JDBC, чтобы получить более детализированную информацию о том, что происходит во время коммита транзакций, и когда именно возникают ошибки.

  3. Обновление библиотек: Если описанные шаги не помогают, рассмотрите возможность откатиться к предыдущим версиям библиотек, которые работали без ошибок, и проводите последовательное обновление с тестированием каждой версии.

  4. Обратитесь в сообщество: Если проблема продолжает сохраняться, обратитесь за поддержкой в форумы Narayana и другие сообщества разработчиков, где вы можете найти опыт других пользователей, которые столкнулись с аналогичными проблемами.

Заключение

Проблема, связанная с HeuristicMixedException после обновления стека, может быть вызвана множеством факторов, включая неправильную конфигурацию и различные версии библиотек. Настройка Narayana, управление соединениями и тщательное тестирование помогут выявить причины ошибки и успешно разрешить их. Соблюдение предложенных рекомендаций обеспечит стабильную работу вашего приложения.

Оцените материал
Добавить комментарий

Капча загружается...