QuartzDeadlineManager (Scheduler) непреднамеренно заменяет настроенный TransactionManager - PullRequest
0 голосов
/ 28 мая 2020
Документация по

Axon содержит ошибки в описании нюансов между EventScheduler и DeadlineManager, а также в том, в каком сценарии вы должны использовать каждый из них. В испытании огнем я попытался использовать EventScheduler в одном из немногих реализованных Saga. Я понял, что он не запускает @EventSourcingHandler на Aggregate, поэтому был вынужден перейти на DeadlineManager. (Подсказка: это можно было бы лучше описать в документации)

Теперь первой задачей было настроить DeadlineManager без какого-либо понимания. Вот последний код, который я закончил:

@Configuration
public class AxonConfiguration {
    @Bean
    public DeadlineManager deadlineManager(
        final Scheduler scheduler,
        // Disregard "Could not autowire. No beans of 'AxonConfiguration' type found." complain from IntelliJ.
        // This class is an @Configuration, which is a @Component by itself.
        final org.axonframework.spring.config.AxonConfiguration configuration,
        final TransactionManager transactionManager,
        final Serializer serializer
    ) {
        return QuartzDeadlineManager.builder()
            .scheduler(scheduler)
            .serializer(serializer)
            .scopeAwareProvider(new ConfigurationScopeAwareProvider(configuration))
            .transactionManager(transactionManager)
            .build();
    }
}

Как вы можете видеть, при использовании уважаемой среды IDE он жалуется, что AxonConfiguration не является Bean, поэтому вы должны жить с предупреждением вечно.

Когда я ввел DeadlineManager в свой Saga, я сделал следующее:

@Slf4j
@Saga
public class MySaga {
    static final Long DELAY_INITIALIZE = 60L;
    static final String INITIALIZE_DEADLINE_NAME = "MyInitialize";

    @Autowired
    private transient DeadlineManager deadlineManager;

    @Autowired
    private transient CommandGateway commandGateway;

    private String scheduleName;
    private String scheduleId;

    @StartSaga
    @SagaEventHandler(associationProperty = "correlationId")
    protected void on(final ScheduleEvent event, @Timestamp final Instant eventInstant) {
        final Instant timerInitialize = eventInstant.plus(Duration.ofSeconds(DELAY_INITIALIZE));

        this.scheduleName = INITIALIZE_DEADLINE_NAME;
        this.scheduleId = this.deadlineManager.schedule(
            timerInitialize,
            INITIALIZE_DEADLINE_NAME,
            InitializeEvent.builder()
                .id(event.getId())
                .build()
        );
    }

    @DeadlineHandler(deadlineName = INITIALIZE_DEADLINE_NAME)
    protected void onDeadline(final InitializeEvent command) {
        this.commandGateway.send(command);
    }

    // ...
}

Я абстрагировал нерелевантный код. Имейте в виду, что нигде в кодовой базе мы не изменяем экземпляр StdScheduler, внедренный как зависимость Bean для DeadlineManager.

Однако при выполнении моего кода я продолжаю получать следующее исключение:

2020-05-28 03:26:13.001 ERROR 1 --- [eduler_Worker-1] o.a.deadline.quartz.DeadlineJob          : Exception occurred during processing a deadline job which will be retried [com.eblock.simulcast.bidding_engine_axon.auction_event.command.InitializeAuctionEvent]

javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413) ~[hibernate-core-5.4.15.Final.jar!/:5.4.15.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1608) ~[hibernate-core-5.4.15.Final.jar!/:5.4.15.Final]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:409) ~[spring-orm-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
    at com.sun.proxy.$Proxy175.executeUpdate(Unknown Source) ~[na:na]
    at org.axonframework.modelling.saga.repository.jpa.JpaSagaStore.updateSaga(JpaSagaStore.java:272) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.modelling.saga.repository.AnnotatedSagaRepository.updateSaga(AnnotatedSagaRepository.java:208) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.modelling.saga.repository.AnnotatedSagaRepository.commit(AnnotatedSagaRepository.java:174) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.modelling.saga.repository.AnnotatedSagaRepository.lambda$doLoad$2(AnnotatedSagaRepository.java:121) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.MessageProcessingContext.notifyHandlers(MessageProcessingContext.java:71) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.notifyHandlers(DefaultUnitOfWork.java:106) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.changePhase(AbstractUnitOfWork.java:222) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commitAsRoot(AbstractUnitOfWork.java:83) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commit(AbstractUnitOfWork.java:71) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.executeWithResult(DefaultUnitOfWork.java:92) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.UnitOfWork.executeWithResult(UnitOfWork.java:328) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.deadline.quartz.DeadlineJob.execute(DeadlineJob.java:134) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar!/:na]
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) ~[quartz-2.3.2.jar!/:na]

2020-05-28 03:26:13.001  INFO 1 --- [eduler_Worker-1] org.quartz.core.JobRunShell              : Job InitializeAuctionEvent.deadline-7e5d9eec-2c7d-4312-9dde-acb9a56abc6b threw a JobExecutionException: 

org.quartz.JobExecutionException: javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.axonframework.deadline.quartz.DeadlineJob.execute(DeadlineJob.java:143) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar!/:na]
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) ~[quartz-2.3.2.jar!/:na]
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413) ~[hibernate-core-5.4.15.Final.jar!/:5.4.15.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1608) ~[hibernate-core-5.4.15.Final.jar!/:5.4.15.Final]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:409) ~[spring-orm-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
    at com.sun.proxy.$Proxy175.executeUpdate(Unknown Source) ~[na:na]
    at org.axonframework.modelling.saga.repository.jpa.JpaSagaStore.updateSaga(JpaSagaStore.java:272) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.modelling.saga.repository.AnnotatedSagaRepository.updateSaga(AnnotatedSagaRepository.java:208) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.modelling.saga.repository.AnnotatedSagaRepository.commit(AnnotatedSagaRepository.java:174) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.modelling.saga.repository.AnnotatedSagaRepository.lambda$doLoad$2(AnnotatedSagaRepository.java:121) ~[axon-modelling-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.MessageProcessingContext.notifyHandlers(MessageProcessingContext.java:71) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.notifyHandlers(DefaultUnitOfWork.java:106) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.changePhase(AbstractUnitOfWork.java:222) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commitAsRoot(AbstractUnitOfWork.java:83) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.AbstractUnitOfWork.commit(AbstractUnitOfWork.java:71) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.DefaultUnitOfWork.executeWithResult(DefaultUnitOfWork.java:92) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.messaging.unitofwork.UnitOfWork.executeWithResult(UnitOfWork.java:328) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    at org.axonframework.deadline.quartz.DeadlineJob.execute(DeadlineJob.java:134) ~[axon-messaging-4.3.3.jar!/:4.3.3]
    ... 2 common frames omitted

Я решил отладить и потратить несколько часов, пытаясь сузить проблему. Я понял, что каким-то образом вне моей кодовой базы (вероятно, Axon magi c) StdScheduler.getContext().get(DeadlineJob.TRANSACTION_MANAGER_KEY) переопределяется.

Вот 2 снимка экрана, которые подчеркивают проблему:

enter image description here

Как видите, номер экземпляра StdScheduler 13137 содержит SpringTransactionManager во время инициализации ().

Когда я добавляю точку останова прямо в мою строку планирования, вот скриншот:

enter image description here

Экземпляр StdScheduler по-прежнему 13137, а QuartzDeadlineManager.transactionManager по-прежнему содержит экземпляр SpringTransactionManager. Но теперь StdScheduler.getContext().get(DeadlineJob.TRANSACTION_MANAGER_KEY) содержит экземпляр NoTransactionManager, что затем приводит к прерыванию моего выполнения из-за TransactionRequiredException.

Кто-нибудь испытывал этот сценарий? Как это было смягчено? Прямо сейчас я просматриваю весь код Axon, пытаясь понять, где происходит замена экземпляра.

Спасибо,

1 Ответ

0 голосов
/ 28 мая 2020

В итоге я выяснил, в чем проблема. В моем классе AxonConfiguration метод для EventScheduler был оставлен на тот случай, если нам придется вернуться или использовать этот планировщик в ближайшем будущем. EventScheduler был определен следующим образом:

    @Bean
    public QuartzEventSchedulerFactoryBean eventScheduler() {
        return new QuartzEventSchedulerFactoryBean();
    }

Поскольку QuartzEventScheduler был создан FactoryBean, он извлекал StdScheduler из ApplicationContext и заново присваивал соответствующие переменные . Поскольку у меня не было настроенного TransactionManager, значение по умолчанию (NoTransactionManager) присваивалось QuartzEventScheduler, которое затем изменяет контекст QuartzScheduler, вызывая проблему.

Чтобы исправить это, я расширил my Bean на следующее:

    @Bean
    public QuartzEventSchedulerFactoryBean eventScheduler(
        final ApplicationContext applicationContext,
        @Qualifier("eventStore") final EventBus eventBus,
        final Scheduler scheduler,
        final PlatformTransactionManager transactionManager
    ) {
        final QuartzEventSchedulerFactoryBean factoryBean = new QuartzEventSchedulerFactoryBean();

        factoryBean.setApplicationContext(applicationContext);
        factoryBean.setEventBus(eventBus);
        factoryBean.setScheduler(scheduler);
        factoryBean.setTransactionManager(transactionManager);
        factoryBean.setTransactionDefinition(new DefaultTransactionDefinition());

        return factoryBean;
    }

Вот мой запрос для команды Axon:

Для QuartzEventScheduler(FactoryBean) требуется базовый PlatformTransactionManager, и создает новый SpringTransactionManager для назначения. Было бы намного проще, если бы они сохранили тот же подход, что и QuartzDeadlineManager, используя напрямую autowired TransactionManager, вместо внутренней обработки экземпляра (и замены созданного axon-spring-autoconfigure).

...