Действительно ли необходима 1 фазовая фиксация (ChainedTransactionManager) в этом сценарии, а не управление транзакциями? - PullRequest
0 голосов
/ 15 октября 2019

У меня есть приложение Spring Boot с @JmsListener, которое получает сообщение из очереди, сохраняет его в базе данных и отправляет в другую очередь.

Я хотел иметь минимальную транзакционную гарантию, поэтому 1-Phase-commit работает для меня. После долгих чтений я обнаружил, что могу использовать ChainedTransactionManager для координации ресурсов DataSource и JMS:

@Configuration
public class TransactionConfiguration {

    @Bean
    public ChainedTransactionManager transactionManager(JpaTransactionManager jpaTm, JmsTransactionManager jmsTm) {
        return new ChainedTransactionManager(jmsTm, jpaTm);
    }

    @Bean
    public JpaTransactionManager jpaTransactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }

    @Bean
    public JmsTransactionManager jmsTransactionManager(ConnectionFactory connectionFactory) {
        return new JmsTransactionManager(connectionFactory);
    }

}

Прослушиватель очереди:

    @Transactional(transactionManager = "transactionManager")
    @JmsListener(...)
    public void process(@Payload String message) {
        //Write to db
        //Send to output queue
    }

START MESSAGING TX
START DB TX
READ MESSAGE
WRITE DB
SEND MESSAGE
COMMIT DB TX
COMMIT MESSAGING TX
  • Если фиксация db завершится неудачно, сообщение будет повторно обработано повторно
  • Если фиксация db завершится успешно, но фиксация обмена сообщениями завершится неудачно, сообщение будет обработано повторно. Это не проблема, поскольку я могу гарантировать идемпотентность операции записи в БД

Теперь я сомневаюсь, давайте предположим, что я не настроил ChainedTransactionManager, и слушатель был таким (нет *)1020 *):

    @JmsListener(...)
    public void process(@Payload String message) {
        //Write to db
        //Send to output queue
    }

Разве это не ведет себя так же, как в другом примере, несмотря на отсутствие координации коммитов? (Я проверил, что для исключений SQL сообщение доставляется)

    RECEIVE MESSAGE
    WRITE DB + COMMIT
    SEND MESSAGE + COMMIT
  • В случае неудачной фиксации БД сообщение будет обработано повторно
  • В случае успеха и операции отправки сообщенияне удалось его повторно обработать.

Так действительно ли необходимо ChainedTransactionManager в этом случае?

UPDATE : отладка автоконфигурации Spring Boot (JmsAnnotationDrivenConfiguration) ...

@Bean
    @ConditionalOnMissingBean(name = "jmsListenerContainerFactory")
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
            DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        return factory;
    }

... DefaultJmsListenerContainerFactoryConfigurer настраивает фабрику с factory.setSessionTransacted(true);, поскольку не определено JtaTransactionManager:

        if (this.transactionManager != null) {
            factory.setTransactionManager(this.transactionManager);
        }
        else {
            factory.setSessionTransacted(true);
        }

С setSessionTransacted (true), в соответствии с Spring doc я получу нужное мне поведение отката и повторной доставки сообщения для БД (или любых) исключений:

Локальные транзакции ресурсов могут быть просто активированы черезфлаг sessionTransacted в определении контейнера слушателя. Каждый вызов прослушивателя сообщений будет затем работать в активной транзакции JMS, при этом прием сообщений будет отменен в случае сбоя при выполнении прослушивателя. Отправка ответного сообщения (через SessionAwareMessageListener) будет частью той же локальной транзакции, но любые другие операции с ресурсами (например, доступ к базе данных) будут работать независимо. Для этого обычно требуется обнаружение дубликатов сообщений в реализации прослушивателя, что относится к случаю, когда обработка базы данных завершилась, но обработка сообщения не удалось зафиксировать.

Это объясняет, что я получаю ожидаемое поведение без необходимости конфигурироватьChainedTransactionManager.

После всего этого, не могли бы вы сказать мне, имеет ли смысл (добавляет некоторую гарантию, что мне не хватает) использовать ChainedTransactionManager в этом случае?

...