为什么 LocalXAResourceImpl.commit() 中的异常仅给出警告?

Why does an exception in LocalXAResourceImpl.commit() merely give a warning?

在 WildFly 8 中,我正在使用包含 Oracle XA 数据源和自定义 JCA LocalTransaction 资源(到文件的连接)的分布式事务。

基本上它按预期工作 - 如果其中一个资源未能提交,整个事务将回滚并且数据库和文件都不是 updated/written。

不过有一个特例。使用 JPA,如果我正在执行 entityManager.merge(entity); 并且实体的值与数据库中的值相同,并且写入文件失败(即因为它不存在),我得到的只是一个警告和我的 EJB 中没有抛出异常:

@Stateless
public class JCABean {

    @PersistenceContext(unitName = "file-tx") 
    private EntityManager entityManager;

    @Resource(name = "java:/FileDataSource")
    private IDataSource fileDataSource;

    public void insert(
            final long id, 
            final String value, 
            final boolean update) {

        final FileTxTest entity = new FileTxTest();
        entity.setId(id);
        entity.setValue(value);

        if (update) {
            entityManager.merge(entity);
        } else {
            entityManager.persist(entity);
        }

        final File file = new File(FileHelper.BASE_PATH, 
                String.format("%s.txt", id));
        try (final IConnection connection = fileDataSource.getConnection(
                file.getAbsolutePath())) {
            connection.write(String.format("%s%n", value));
        }
    }
}

fileDataSource.getConnection() returns FileConnection 的实例实现 LocalTransaction.

如果 file 不可写,connection.commit() 抛出 ResourceException.

如果给 entity.setValue() 的值等于数据库中的值(和 update == true 所以合并完成),只发出此警告:

16:33:58,276 WARN  [com.arjuna.ats.jta] (default task-4) ARJUNA016039: onePhaseCommit on < formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff7f000101:842ac6d:56699b01:22, node_name=1, branch_uid=0:ffff7f000101:842ac6d:56699b01:2a, subordinatenodename=null, eis_name=java:/FileDataSource > (LocalXAResourceImpl@1e22d60[connectionListener=e1ca39 connectionManager=1b9de9e warned=false currentXid=null productName=Generic JCA productVersion=1.0 jndiName=java:/FileDataSource]) failed with exception XAException.XA_RBROLLBACK: org.jboss.jca.core.spi.transaction.local.LocalXAException: IJ001156: Could not commit local transaction
        at org.jboss.jca.core.tx.jbossts.LocalXAResourceImpl.commit(LocalXAResourceImpl.java:180) [ironjacamar-core-impl-1.1.9.Final.jar:1.1.9.Final]
        at com.arjuna.ats.internal.jta.resources.arjunacore.XAOnePhaseResource.commit(XAOnePhaseResource.java:113)
        at com.arjuna.ats.internal.arjuna.abstractrecords.LastResourceRecord.topLevelPrepare(LastResourceRecord.java:152)

如果被合并的实体有一个更新的值,整个交易会按预期失败:

Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
    at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1178)
    at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
    at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:93) [wildfly-ejb3-8.2.1.Final.jar:8.2.1.Final]

如此有效,满足了 "all or nothing" 要求,但我希望交易在任何情况下都失败并出现异常,以便我可以引发错误。

Oracle XA 数据源:

<xa-datasource jndi-name="java:/OracleDS" pool-name="OracleDS" enabled="true">
    <xa-datasource-property name="URL">
        jdbc:oracle:thin:@host.domain.tld:1521:NAME
    </xa-datasource-property>
    <driver>oracle</driver>
    <xa-pool>
        <min-pool-size>1</min-pool-size>
        <max-pool-size>5</max-pool-size>
        <prefill>true</prefill>
    </xa-pool>
    <security>
        <user-name>user</user-name>
        <password>pass</password>
    </security>
</xa-datasource>
<drivers>
    <driver name="oracle" module="com.oracle">
        <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
    </driver>
</drivers>

和资源适配器:

<resource-adapters>
    <resource-adapter id="file-tx-jca.rar">
        <archive>
            file-tx-jca.rar
        </archive>
        <transaction-support>LocalTransaction</transaction-support>
        <config-property name="Server">
            localhost
        </config-property>
        <config-property name="Port">
            19000
        </config-property>
        <connection-definitions>
            <connection-definition class-name="my.package.GenericManagedConnectionFactory" jndi-name="java:/FileDataSource" pool-name="FileConnectionFactory">
                <pool>
                    <min-pool-size>1</min-pool-size>
                    <max-pool-size>5</max-pool-size>
                </pool>
                <security>
                    <application/>
                </security>
            </connection-definition>
        </connection-definitions>
    </resource-adapter>
</resource-adapters>

注意:我知道我最多可以在一个事务中征用一个 LocalTransaction 资源(最后一个资源)。由于我可能需要在同一事务中处理多个文件,因此我将 FileConnection<resource-adapter> 更改为 XAResource/XATransaction。在这里,如果提交失败(即抛出 XAException(XAException.XA_HEURHAZ)),事务总是会失败。但是我还是想明白为什么本地事务提交失败时只记录警告。

是否有一些 属性 即使在我目前只收到警告的情况下,我也可以设置为获得异常?

我已经从 JBOSS 5 EAP 中读到 this documentation,它描述了当您在涉及其他两阶段感知的事务中征用单个单阶段感知资源时使用的 LRCO 算法资源:

Although the XA transaction protocol is designed to provide ACID properties by using a two-phase commit protocol, model may not always be appropriate. Sometimes it is necessary to allow a non-XA-aware resource manager to participate in a transaction. This is often the case with data stores that do not support distributed transactions.

In this situation, you can use a technique known as Last Resource Commit Optimization (LRCO). This is sometimes called the Last Resource Gambit. The one-phase-aware resource is processed last in the prepare phase of the transaction, at which time an attempt is made to commit it. If the attempt is successful, the transaction log is written and the remaining resources go through the phase-two commit. If the last resource fails to commit, the transaction is rolled back. Although this protocol allows most transactions to complete normally, some errors can cause an inconsistent transaction outcome. For this reason, use LRCO as a last resort. When a single is used in a transaction, the LRCO is automatically applied to it. In other situations, you can designate a last resource by using a special marker interface. Refer to the JBoss Transactions Programmer's Guide for more details.

我的假设如下: 当您不修改合并的实体时,在两阶段感知资源事务的准备阶段,撤消日志中不会写入任何内容。

然后,当准备阶段结束时发生的一阶段资源事务的提交失败(文件写入)时,两阶段资源事务不必撤消任何操作。 所以你不会再得到RollbackException,对应全局事务涉及的两阶段资源事务的回滚。