Saga Configuration and Run Problem.

Hi all,

I am a new axon user and i would like to congratulate with all axon developers because the framework works really well.
I have a problem that i was not able to solve since documentation lacks of explicit examples.

Said that i do not use any framework like Spring or similar but all my application is coded and configured programmatically with java,
i would like to understand how i can configure al run a saga.

Apart the documented things, what i understood in which things are needed to run a saga correctly is:

  1. the annotated saga
  2. a saga manager
  3. eventually a saga reposistory (i use in memory repository for simplicity and testing)

Now the question is: which code must i write to have a starting and working saga?

How to write an AssociationValueResolver?

InMemorySagaRepository sagaRepository = new InMemorySagaRepository()
SimpleSagaManager simpleSagaManager = new SimpleSagaManager(SagaLavagna.class, sagaRepository, ASSOCIATIONVALUERESOLVER, eventBus);

And after that will this Saga start and run?

Thanks in advance for any halp you will provide.
I am right now stuck figuring out how to run sagas.

Andrea.

Hi Andrea,

the lack of examples in the documentation has been noted. I hope to find some time to add them.

If you’re using annotated sagas (which is highly recommended), then you shouldn’t use the SimpleSagaManager, but the AnnotatedSagaManager.
Configuration should be fairly simple:
SagaManager sagaManager = new AnnotatedSagaManager(sagaRepository, SagaLavagna.class);
If you subscribe the saga manager to the event bus (using eventBus.subscribe(sagaManager)), it will automatically manage the lifecycle of your SagaLavagna instances.

Hope this help.
Cheers,

Allard

Thank you very much indeed.
What I needed was to register the saga manager into the event bus.
Now the saga runs without problem.
Thanks again.

Andrea.

Hi again,

I have a problem handling commands sent by saga to an aggregate.
We use the DefaultCommandGateway and the SimpleCommandBus.
The scenario is the following:

1) a command is executed on aggregate x and event 1 is raised (with sendAndWait)
2) saga listen for event 1 and call a command execution on aggregate y (with send because sendAndWait raises an exception of transaction already active)
3) aggregate y command execution raises an event 2
4) Listener z listens for event 2 and writes a DB

This does not seems to work because DB is never written and it seems that the command on aggregate Y is never executed.
What happens is that aggregate x is updated, event 1 is stored and no more.
What we expect is:

1) aggregate x is updated
2) event 1 is stored
3) aggregate y is updated
4) event 2 is stored
5) DB is written

Can this behaviour be related to nested unit of work?
Andrea.

Hi Andrea,

do you see your Saga handling Event 1?
I don’t think this is related to the UoW nesting issue, as it is only triggered at the third level.

What trigger me is that send works, but sendAndWait throws an exception. This might be an indication that the “send” part doesn’t work either.
You problem might be in the “transaction already active” part. How do you manage transactions?

Cheers,

Allard

Hi Andrea,

do you see your Saga handling Event 1?
I don't think this is related to the UoW nesting issue, as it is only
triggered at the third level.

What trigger me is that send works, but sendAndWait throws an exception.
This might be an indication that the "send" part doesn't work either.
You problem might be in the "transaction already active" part. How do you
manage transactions?

Cheers,

Allard

Hi Allard thanks for quick answer.
Well yes our saga starts and handles the event 1. After that nothing happens anymore.
Yes the sendAndWait that throws an exception puzzles me too. I will check better that part of the code.
But basically we have a very simple JPAtransactionManager.
I attach the code below.

public class JPATransactionManager implements TransactionManager<EntityTransaction> {

    private final ProviderEntityManagerTenant entityManagerProvider;

    public JPATransactionManager(ProviderEntityManagerTenant entityManagerProvider) {
        this.entityManagerProvider = entityManagerProvider;
    }

    @Override
    public EntityTransaction startTransaction() {
        LoggerUtils.getLogger().info("--------------> Avvio la transazione Axon");
        EntityTransaction transaction = entityManagerProvider.getEntityManagerTenant().getTransaction();
        transaction.begin();
        return transaction;
    }

    @Override
    public void commitTransaction(EntityTransaction transaction) {
        LoggerUtils.getLogger().info("--------------> Committo la transazione Axon");
        transaction.commit();
    }

    @Override
    public void rollbackTransaction(EntityTransaction transaction) {
        LoggerUtils.getLogger().info("--------------> Rollback della transazione Axon");
        transaction.rollback();
    }
}

Any other idea?
Andrea.

It looks like your transaction manager doesn’t take into account that a transaction may already be running. In that case, it should (probably) just use the existing transaction and do nothing when a commit is done.

At least, that’s how the SpringTransactionManager works…

Cheers,

Allard

It looks like your transaction manager doesn't take into account that a
transaction may already be running. In that case, it should (probably) just
use the existing transaction and do nothing when a commit is done.

At least, that's how the SpringTransactionManager works...

Cheers,

Allard

Thank you very much.
It worked. I just had to add the check for transaction activness.
Keep up the good work.

Andrea.

Hi everyone,

Could anyone please send me the example for Saga manager for In memorysagaRepository.

I am new to the CQRS saga process manager.

Please help me.

Srinivas

Hi,

there are a few examples in the Axon quickstart samples:

https://github.com/AxonFramework/AxonFramework/blob/axon-2.4.5/quickstart/src/main/java/org/axonframework/quickstart/RunSaga.java

or if you use spring:
https://github.com/AxonFramework/AxonFramework/blob/axon-2.4.5/quickstart/src/main/java/org/axonframework/quickstart/RunSagaWithSpring.java

Cheers,

Allard

Hi,

Thank you for posting the examples.

Do you have any other examples for saga-springBoot-Inmemory examples

Thank you.

Is there anything specific you’re looking for? In terms of configuration, there is really not much more to it than what is shown in the quickstart examples…

Cheers,

Allard

Hi Allard,

I am new to Axon and SAGA framework.

While saga is trying to persist I am getting the below exception :-

javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process ‘persist’ call

at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:282)

at com.sun.proxy.$Proxy127.persist(Unknown Source)

at org.axonframework.saga.repository.jpa.JpaSagaRepository.storeAssociationValue(JpaSagaRepository.java:150)

at org.axonframework.saga.repository.AbstractSagaRepository.add(AbstractSagaRepository.java:48)

at org.axonframework.saga.AbstractSagaManager.startNewSaga(AbstractSagaManager.java:154)

at org.axonframework.saga.AbstractSagaManager.handle(AbstractSagaManager.java:95)

at org.axonframework.eventhandling.async.EventProcessor.doHandle(EventProcessor.java:276)

at org.axonframework.eventhandling.async.EventProcessor.processNextEntry(EventProcessor.java:225)

at org.axonframework.eventhandling.async.EventProcessor.run(EventProcessor.java:198)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

at java.lang.Thread.run(Thread.java:745)

Seems like it is looking for an artive transaction before it can persist.

I am not able to work out where transaction manager needs to be hooked.

I am using Annotation based approach.

Regards :

Harinder Singh

Hi Harinder,

it looks like you’re using Axon 2. If you’re new and not stuck to an existing implementation that depends on Axon 2, I would highly recommend using Axon 3 instead. The most recent version is Axon 3.0.6.

The TransactionManager would have to be configured in the component that initiates the asynchronous process. I haven’t touched Axon 2 code for a while now, but it is probably in the Asynchronous Event Listener.

Hope this helps.
Cheers,

Allard

Hi Allard,

Thanks for your response !!
Yes it helped !!

I have registered the SpringTransactionManager in AsynchronousCluster constructor and bean defined as below :-

@Bean public SpringTransactionManager springTransactionManager() { return new SpringTransactionManager(new JpaTransactionManager()); }

Though the transaction error is gone , but I am getting the below error WARN log and events are not getting inserted in DB.

Hibernate: select domaineven0_.event_identifier as col_0_0_, domaineven0_.aggregate_identifier as col_1_0_, domaineven0_.sequence_number as col_2_0_, domaineven0_.time_stamp as col_3_0_, domaineven0_.payload_type as col_4_0_, domaineven0_.payload_revision as col_5_0_, domaineven0_.payload as col_6_0_, domaineven0_.meta_data as col_7_0_ from domain_event_entry domaineven0_ where domaineven0_.aggregate_identifier=? and domaineven0_.type=? and domaineven0_.sequence_number>=? order by domaineven0_.sequence_number ASC limit ?

2017-09-18 13:25:08.134 WARN 12700 — [ taskExecutor-1] o.a.e.async.DefaultErrorHandler : Got a [java.lang.IllegalArgumentException: Resource must not be null] while handling an event of type [] in [the Unit of Work]. Will retry in 2000 millis

2017-09-18 13:25:08.134 WARN 12700 — [ taskExecutor-1] o.a.eventhandling.async.EventProcessor : The provided executor does not seem to support delayed execution. Scheduling for immediate processing and expecting processing to wait if scheduled to soon.

2017-09-18 13:25:08.134 WARN 12700 — [ taskExecutor-1] o.a.eventhandling.async.EventProcessor : Event processing started before delay expired. Forcing thread to sleep for 2000 millis.

I assume it is referring to the Data Source as Resource.?

Regards :

Harinder Singh

Hi,

it looks like some “null” parameter is being passed somewhere. Try to find out where the IllegalArgumentException comes from. The stacktrace should point you to the source of the problem.

The other warnings are generated by Axon, because the Executor that was provided to the AsynchronousCluster doesn’t implement ScheduledExecutor (and so doesn’t support delayed execution). It is recommended to configure a ScheduledExecutorService implementation.

Cheers,
Allard

Hi Allard,

The transaction manager required some data resource bean as a resource.

Data source properties are configured from application.properties
I did provide that as below:-

`
@Bean(destroyMethod = “close”)
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
HikariDataSource hikariDataSource = (HikariDataSource) DataSourceBuilder
.create(dataSourceProperties.getClassLoader())
.type(HikariDataSource.class)
.driverClassName(dataSourceProperties.getDriverClassName())
.url(dataSourceProperties.getUrl())
.username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword())
.build();
return hikariDataSource;
}

@Bean(name = “entityManager”)
public EntityManager entityManager(
DataSourceProperties dataSourceProperties) {
return entityManagerFactory(dataSourceProperties).createEntityManager();
}

@Bean(name = “entityManagerFactory”)
public EntityManagerFactory entityManagerFactory(
DataSourceProperties dataSourceProperties) {
LocalContainerEntityManagerFactoryBean emf =
new LocalContainerEntityManagerFactoryBean();

emf.setDataSource(dataSource(dataSourceProperties));
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(jpaVendorAdapter);
emf.setPackagesToScan(“com.abc”);
emf.setPersistenceUnitName(“saga-persistence”);
emf.afterPropertiesSet();
return emf.getObject();
}

@Bean(name = “transactionManager”)
public PlatformTransactionManager transactionManager(
DataSourceProperties dataSourceProperties) {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(entityManagerFactory(dataSourceProperties));
return tm;
}
`

now this transaction manager is referred as below in the event bus cluster:-
`

@Bean
public ClusterSelector clusterSelector(
@Qualifier(“asyncExecutor”) Executor executor,
SpringTransactionManager springTransactionManager) {
Cluster cluster = new AsynchronousCluster(queueName + QUEUE_UNIQUIFIER,
executor, springTransactionManager, new SequentialPerAggregatePolicy());
return new DefaultClusterSelector(cluster);
}

@Bean
public SpringTransactionManager springTransactionManager(
PlatformTransactionManager transactionManager) {
return new SpringTransactionManager(transactionManager);
}
`

But now on application start, I am getting the below exception…
`

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.axonframework.saga.SagaRepository]: Factory method ‘sagaRepository’ threw exception; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: SagaEntry is not mapped [SELECT new org.axonframework.saga.repository.jpa.SerializedSaga(se.serializedSaga, se.sagaType, se.revision) FROM SagaEntry se WHERE se.sagaId = :sagaId]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
… 32 common frames omitted
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: SagaEntry is not mapped [SELECT new org.axonframework.saga.repository.jpa.SerializedSaga(se.serializedSaga, se.sagaType, se.revision) FROM SagaEntry se WHERE se.sagaId = :sagaId]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1679)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347)
at com.sun.proxy.$Proxy120.createQuery(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy120.createQuery(Unknown Source)
at org.axonframework.saga.repository.jpa.JpaSagaRepository.(JpaSagaRepository.java:95)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
… 33 common frames omitted
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: SagaEntry is not mapped [SELECT new org.axonframework.saga.repository.jpa.SerializedSaga(se.serializedSaga, se.sagaType, se.revision) FROM SagaEntry se WHERE se.sagaId = :sagaId]
at org.hibernate.hql.internal.ast.QuerySyntaxException.generateQueryException(QuerySyntaxException.java:79)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:103)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:218)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:142)
at org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:115)
at org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:76)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:150)
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:302)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:240)
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1894)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:291)
… 57 common frames omitted
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: SagaEntry is not mapped
at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:171)
at org.hibernate.hql.internal.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:91)
at org.hibernate.hql.internal.ast.tree.FromClause.addFromElement(FromClause.java:76)
at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:321)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3687)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3576)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:716)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:572)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:309)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:257)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190)
… 65 common frames omitted
`

Regards :
Harinder Singh

Hi Allard,

I am getting the exception while creating the Saga Repository bean under my axon configuration.I have the entityScan annotation as below on that configiration file …

@EntityScan("org.axonframework.saga.repository.jpa")

Regards :
Harinder Singh

Hi Harinder,

this exception occurs when an EntityManager.persist() method is invoked, but there is no active transaction.
Make sure you configure the TransactionManager on all components that handle events asynchronously. Each of these components will have either a setter or a constructor parameter that allows you to set a TransactionManager.

Cheers,

Allard