Почему исключение в LocalXAResourceImpl.commit() просто выдает предупреждение?

В WildFly 8 я использую распределенную транзакцию, содержащую источник данных Oracle XA и пользовательский ресурс JCA LocalTransaction (подключение к файлу).

В основном это работает так, как ожидалось - если один из ресурсов не фиксируется, вся транзакция откатывается, и ни база данных, ни файл не обновляются/не записываются.

Однако есть особый случай. Используя 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() возвращает экземпляр 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]

Таким образом, требование «все или ничего» удовлетворено, но я бы хотел, чтобы транзакция завершилась ошибкой с исключением в любом случае, чтобы я мог вызвать ошибку.

Источник данных 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)). Но я все же хотел бы понять, почему в журнале регистрируется только предупреждение, если фиксация локальной транзакции не удалась.

Есть ли какое-то свойство, которое я могу установить, чтобы получить исключение даже в том случае, когда в настоящее время я получаю только предупреждение?


person Torsten Römer    schedule 10.12.2015    source источник
comment
Не могли бы вы поделиться своим кодом EJB?   -  person Rémi Bantos    schedule 11.12.2015
comment
Да конечно... готово!   -  person Torsten Römer    schedule 11.12.2015
comment
Не могли бы вы также поделиться своими конфигурациями источников данных? Спасибо   -  person Rémi Bantos    schedule 11.12.2015
comment
Тоже сделано - спасибо за проявленный интерес   -  person Torsten Römer    schedule 11.12.2015
comment
Всегда пожалуйста. Мне интересно, что происходит, когда вы удаляете весь код, связанный с JPA, чтобы сохранить только код записи файла, а затем воспроизводите ошибку записи? Не могли бы вы также поделиться кодом ресурса file-tx?   -  person Rémi Bantos    schedule 12.12.2015
comment
В этом случае RollbackException: ARJUNA016053: Could not commit transaction. выбрасывается, как и ожидалось. Так что оптимизация 1PC неправильно проглатывает исключение. В коде файла-передачи в commit() просто IOException переопределяется как ResourceException.   -  person Torsten Römer    schedule 13.12.2015


Ответы (1)


Я прочитал эту документацию из JBOSS 5 EAP, в котором описывается алгоритм LRCO, используемый при включении одного ресурса с поддержкой одной фазы в транзакцию, которая включает другие ресурсы с поддержкой двух фаз:

Хотя протокол транзакций XA предназначен для предоставления свойств ACID с использованием протокола двухфазной фиксации, модель не всегда подходит. Иногда необходимо разрешить диспетчеру ресурсов, не поддерживающему XA, участвовать в транзакции. Это часто бывает с хранилищами данных, которые не поддерживают распределенные транзакции.

В этой ситуации вы можете использовать метод, известный как оптимизация последней фиксации ресурсов (LRCO). Это иногда называют «последним ресурсным гамбитом». Однофазный ресурс обрабатывается последним на этапе подготовки транзакции, когда предпринимается попытка его фиксации. Если попытка успешна, журнал транзакций записывается, а оставшиеся ресурсы проходят вторую фазу фиксации. Если последний ресурс не может быть зафиксирован, транзакция откатывается. Хотя этот протокол позволяет нормально выполнять большинство транзакций, некоторые ошибки могут привести к несогласованному результату транзакции. По этой причине используйте LRCO в крайнем случае. Когда в транзакции используется сингл, к нему автоматически применяется LRCO. В других ситуациях вы можете указать последний ресурс, используя специальный интерфейс маркера. Дополнительные сведения см. в Руководстве программиста по транзакциям JBoss.

Мое предположение таково: когда вы не изменяете объединенный объект, ничего не записывается в журнал отмены на этапе подготовки двухэтапной транзакции ресурсов.

Затем, когда происходит сбой фиксации однофазной транзакции ресурсов (запись файла), что происходит в конце фазы подготовки, ничего не нужно отменять для двухэтапной транзакции ресурсов. Поэтому вы больше не получаете исключение RollbackException, которое соответствовало откату двухфазной транзакции ресурсов, задействованной в глобальной транзакции.

person Rémi Bantos    schedule 11.12.2015
comment
Ваш ответ очень полезен, он указал мне правильное направление и побудил отлаживать WildFly. Результат этапа подготовки — TwoPhaseOutcome.PREPARE_READONLY, поэтому применяется оптимизация 1PC. Там TwoPhaseOutcome.ONE_PHASE_ERROR, вызванный XAException.XA_RBROLLBACK, эффективно игнорируется, что я считаю ошибкой, поэтому я сообщил об одной: issues.jboss.org/browse/JBTM-2584 - person Torsten Römer; 13.12.2015