Suspected threading issues when using JaversAuditableAuditAsync - javers

I am using Javers with Spring Hibernate JPA. The primary database is a MYSQL database with Javers connected to a seperate MongoDB server. I am not using Spring Data.
Everything works great with JaversAuditableAspect but I get the below errors when I use JaversAuditableAspectAsync.
I am using the default setup for the Async Javers.
#Bean
public JaversAuditableAspectAsync javersAuditableAspectAsync() {
return new JaversAuditableAspectAsync(javers(), authorProvider(), new EmptyPropertiesProvider(), javersAsyncAuditExecutor());
}
#Bean
public ExecutorService javersAsyncAuditExecutor() {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("JaversAuditableAsync-%d")
.build();
return Executors.newFixedThreadPool(2, threadFactory);
}
I understand this is an experimental feature but performance is very important in the area I intend to use Javers. Any advice on this would be greatly appreciated.
Errors upon first change attempt:
2020-07-29 10:54:35:497 [asdf#asdf.com E6DD9F] ERROR
DefaultRestExceptionHandler.defaultErrorHandler:120 - Error invoking '/service/form/save'. Exception: org.springframework.transaction.TransactionSystemException. Message: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction.
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
Caused by: javax.persistence.RollbackException: Error while committing the transaction
Caused by: java.util.ConcurrentModificationException: null
Errors upon 2nd or more attempts:
2020-07-29 11:24:48:311 [asdf#asdf.com CD065A] ERROR DefaultRestExceptionHandler.defaultErrorHandler:120 - Error invoking '/service/form/save'. Exception: java.util.ConcurrentModificationException. Message: null.
java.util.ConcurrentModificationException: null

Related

Getting Postgres Table Not Found Error with #ReadOnly Annotation in Spring Boot

I am getting the error PSQLException: ERROR: relation "schema.tableName" does not exist.
I have a GET API in spring which fetches data from a table. This API internally uses a custom method to access data using JpaReporitory. This custom method is annotated with #ReadOnly and #Transactional annotations since this API only fetches data from table. This gives me the table not found error.
But when I remove the #ReadOnly annotation, the API works just fine.
Below are the signatures of the said API and the custom method that it uses:
API:
#GetMapping("/houses/{houseId}/furnitures")
public List<Furnitures> getFurnitureslist(#PathVariable("houseId") long houseId) throws InterruptedException, ExecutionException {
return this.houseService.getFurnituresList(houseId);
}
Custom method:
#Transactional
#ReadOnly
public List<Asset> getTripPicklist(long tripId){
//Bunch of read operations using jpa repository.
}
This gives me an error trace as below:
org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Caused by: org.postgresql.util.PSQLException: ERROR: relation "schema.tableName" does not exist
But when I remove the #ReadOnly annotation from my custom method, the API works fine and I am able fetch the expected results from the database. What am I doing wrong?

Spring Boot + Atomikos: Exception thrown in a transactional JMS listener inside try-catch immediately causes rollback

I'm using Spring Boot 1.5.3 and Atomikos 4.0.6. I have a DefaultMessageListenerContainer setup to listen a JMS queue and handle incoming messages in a transaction using the Spring JTATransactionManager configured to use Atomikos. The message handler calls a transactional service that tries to process the message inside a try-catch-block and in case of exceptions calls other transactional methods for logging purposes. The idea is that the transaction should be rolled back only after everything is logged and the encountered RuntimeException is thrown from the catch-block.
#Transactional
public void handleMessage(UnmarshalledMessage message) {
try {
Thing thing = repository.find(message.getId());
...
}
catch (Exception e) {
// NoResultException translated into EmptyResultDataAccessException
logger.logUsingTransactions(e.getMessage());
throw e;
}
}
However, what happens that the transaction is rolled back immediately after it's originally thrown inside repository.find(). When attempting to read from the database inside the catch-block, an exception is thrown since the transaction has been marked as rollback only:
c.atomikos.jdbc.AtomikosSQLException - Transaction is marked for rollback only or has timed out
com.atomikos.datasource.xa.session.InvalidSessionHandleStateException: Transaction is marked for rollback only or has timed out
at com.atomikos.datasource.xa.session.NotInBranchStateHandler.checkEnlistBeforeUse(NotInBranchStateHandler.java:39)
at com.atomikos.datasource.xa.session.TransactionContext.checkEnlistBeforeUse(TransactionContext.java:70)
at com.atomikos.datasource.xa.session.SessionHandleState.notifyBeforeUse(SessionHandleState.java:160)
at com.atomikos.jdbc.AtomikosConnectionProxy.enlist(AtomikosConnectionProxy.java:207)
at com.atomikos.jdbc.AtomikosConnectionProxy.invoke(AtomikosConnectionProxy.java:122)
at com.sun.proxy.$Proxy129.prepareStatement(Unknown Source)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.prepareStatement(DatabaseAccessor.java:1565)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.prepareStatement(DatabaseAccessor.java:1514)
at org.eclipse.persistence.internal.databaseaccess.DatabaseCall.prepareStatement(DatabaseCall.java:778)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:621)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:560)
at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2055)
at org.eclipse.persistence.sessions.server.ServerSession.executeCall(ServerSession.java:570)
at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:258)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectOneRow(DatasourceCallQueryMechanism.java:714)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectOneRowFromTable(ExpressionQueryMechanism.java:2803)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectOneRow(ExpressionQueryMechanism.java:2756)
at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:555)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1175)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134)
at org.eclipse.persistence.queries.ReadObjectQuery.execute(ReadObjectQuery.java:441)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1222)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1790)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.executeQuery(EntityManagerImpl.java:911)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.findInternal(EntityManagerImpl.java:854)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:730)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:599)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
I'd like to know what is causing this behavior and how to resolve the issue. Note that this setup has been working correctly when run in Weblogic. For additional information, here is a transaction trace log when the exception is first encountered inside the repository method.
DEBUG o.s.t.jta.JtaTransactionManager - Initiating transaction commit
DEBUG o.s.t.jta.JtaTransactionManager - Creating new transaction with name [myMessageListenerContainer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG o.s.t.jta.JtaTransactionManager - Participating in existing transaction
TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [my.transactional.messagehandler.handleMessage]
DEBUG o.s.t.jta.JtaTransactionManager - Participating in existing transaction
TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [my.repository.class.method]
TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [my.repository.class.method] after exception: org.springframework.dao.EmptyResultDataAccessException: ProcessableMessage with id 443e73e7-0905-416b-9e03-4aaa2bbf09fb; nested exception is javax.persistence.NoResultException: ProcessableMessage with id [message-id]
TRACE o.s.t.i.RuleBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on org.springframework.dao.EmptyResultDataAccessException: ProcessableMessage with id 443e73e7-0905-416b-9e03-4aaa2bbf09fb; nested exception is javax.persistence.NoResultException: ProcessableMessage with id [message-id]
TRACE o.s.t.i.RuleBasedTransactionAttribute - Winning rollback rule is: null
TRACE o.s.t.i.RuleBasedTransactionAttribute - No relevant rollback rule found: applying default rules
DEBUG o.s.t.jta.JtaTransactionManager - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.t.jta.JtaTransactionManager - Setting JTA transaction rollback-only
EDIT:
I am using JPA and the NoResultException is initially thrown in a following way:
public static <T> T mandatoryFind(EntityManager em, Class<T> type, Object id) throws NoResultException {
T found = em.find(type, id);
if (found == null) {
throw new NoResultException(type.getSimpleName() +" with id "+ id);
}
return found;
}
Which in turn is called from a class annotated with #Transactional(noRollbackFor = NoResultException.class). The exception is raised in a fairly normal use case - what I'm troubled with is why the transaction is rolled back before I can handle the exception?
It sounds like you do use JPA under the hood (based on your debug logs).
This is a typical behaviour then you do query a single result.
NoResultException:
Thrown by the persistence provider when Query.getSingleResult() or TypedQuery.getSingleResult()is executed on a query and there is no result to return. This exception will not cause the current transaction, if one is active, to be marked for rollback.
In order to avoid that behaviour you do query result as a List.

Spring & Hibernate SessionFactory performance issue

I am facing a performance issue with Hibernate sessionFactory.
It is a Spring Boot - Hibernate app with a SessionFactory configured like this
#Bean
public SessionFactory sessionFactory(HibernateEntityManagerFactory hemf){
return hemf.getSessionFactory();
}
I have also tried all the different ways described in this question Spring Boot - Handle to Hibernate SessionFactory
My DAO looks like this
#Autowired
private SessionFactory sessionFactory;
#Transactional
public List<Type> findAll() {
return sessionFactory.getCurrentSession().createQuery("from Type").list();
}
When the number of concurrent db requests is bigger than the configured maximumPoolSize(10 in this example) then the application becomes unresponsive.
#RequestMapping(value = "/stress-sessionfactory")
public void stressTest(#RequestParam int threadsCount) {
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadsCount; i++) {
final int k = i;
Runnable runnable
= () -> {
List<Type> all = typeDAOHibernate.findAll();
LOG.info("{}:sessionfactory:{} ", k, all.size());
};
Thread t = new Thread(runnable);
threads.add(t);
}
threads.stream().forEach(t -> t.start());
}
You can find a standalone example in github.
The example is configured with maximumPoolSize=10.
So if you just hit
http://localhost:8080/stress-sessionfactory?threadsCount=11 you will get the error I am talking about.
On the other hand a Spring Data repository can easily handle thousands of concurrent requests! (e.g http://localhost:8080/stress-jpa?threadsCount=2000)
I Have tried the same scenario with different datasources(Hikari, Tomcat) , different databases(oracle,h2) and different hibernate
versions( 5.011-Final,v4.3.11-Final) and I always get the same error.
Stacktrace
Exception in thread "Thread-51" Exception in thread "Thread-47" org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:431)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
at com.example.dao.TypeDAOHibernate$$EnhancerBySpringCGLIB$$e6373e2e.findAll(<generated>)
at com.example.controller.StressController.lambda$stressTest$0(StressController.java:36)
at java.lang.Thread.run(Thread.java:745)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:1700)
at org.hibernate.jpa.internal.TransactionImpl.begin(TransactionImpl.java:48)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:189)
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:380)
... 9 more
Caused by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:48)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:90)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:112)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getConnectionForTransactionManagement(LogicalConnectionManagedImpl.java:230)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.begin(LogicalConnectionManagedImpl.java:237)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.begin(JdbcResourceLocalTransactionCoordinatorImpl.java:214)
at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:52)
at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1512)
at org.hibernate.jpa.internal.TransactionImpl.begin(TransactionImpl.java:45)
... 11 more
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30001ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:591)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:194)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:146)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:386)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:87)
... 18 more
[]
I noticed that you are injecting sessionFactory but not using spring-framework transaction features.
If you want to use like this you should close the session you are getting from sessionFactory in dao layer.

Spring 3 JDBC transaction manager not working

I am using Spring 3, MYSQL 5.5, tomcat 6. In my app i have 3 DAO methods executing one after another inside a service class method.
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = {
Exception.class, RuntimeException.class })
myService(){
try {
dao1.delete();
dao1.update();
dao1.save();
} catch(){}
}
Dao1 -
delete() throws exp1{ ---- some code ----}
save() throws exp1{ ---- some code ----}
update() throws exp2{ ---- some code ----}
Now even if an exception is raised my transaction gets committed, like if update() raise an exception delete() and save() doesn't get rolledback. I tried looking into spring logs and i can see it has committed transaction after exception
20:00:29,071 DEBUG SQLErrorCodesFactory:198 - Looking up default SQLErrorCodes for DataSource [org.apache.tomcat.dbcp.dbcp.BasicDataSource#44755866]
20:00:29,078 DEBUG SQLErrorCodesFactory:216 - Database product name cached for DataSource [org.apache.tomcat.dbcp.dbcp.BasicDataSource#44755866]: name is 'MySQL'
20:00:29,079 DEBUG SQLErrorCodesFactory:174 - SQL error codes for 'MySQL' found
20:00:29,081 DEBUG SQLErrorCodeSQLExceptionTranslator:399 - Translating SQLException with SQL state '42S02', error code '1146', message [Table 'xxx.xxxx' doesn't exist]; SQL was [DELETE FROM xxxx WHERE xxxx=?] for task [PreparedStatementCallback]
20:00:29,086 DEBUG xxxServiceImpl:1022 - Returning result after deleting product : xxxx.xxxxx.xxxxx.xxx.ResultVO Object {Result: false, Error code: 1016, Error text: Error while deleting data. Please try again later}
20:00:29,094 DEBUG DataSourceTransactionManager:752 - Initiating transaction commit
20:00:29,097 DEBUG DataSourceTransactionManager:264 - Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/xxx?autoReconnect=true, UserName=root#localhost, MySQL-AB JDBC Driver]
20:00:29,113 DEBUG DataSourceTransactionManager:322 - Releasing JDBC Connection [jdbc:mysql://localhost:3306/xxx?autoReconnect=true, UserName=root#localhost, MySQL-AB JDBC Driver] after transaction
20:00:29,115 DEBUG DataSourceUtils:332 - Returning JDBC Connection to DataSource
If i put #Transactionl before DAO methods, transaction gets rolledback but i am getting 500 error, stating that transaction is already marked for rollback. Am i missing something here?
Remove the try {} catch {} block.
The transaction rollback will happen only if the exception is thrown back to the caller from the method.
In your case you are silently killing the exception using a empty try..catch block, so the exception is never propagated to the transaction manager, so the transaction manger never get the signal to rollback.
In case of annotating the dao, when the exception is thrown from the dao layer, the transaction proxy surrounding the dao method marks the attached transaction (created by the service layer) as rollback only, then when the control is returned from the service layer the transaction manager tries to commit the changes but finds that it is marked as read-only. That is why the error is coming.

jpa transaction stop rollback

I have the following code. I want the execution to continue even if there is an exception
#Transactional(noRollbackFor={PersistenceException.class, PSQLException.class,SQLGrammarException.class})
public void executeQuery(String parameterName){
Query query = objectManager.getEntityManager().createNativeQuery("SOME UPDATE QUERY");
Map<String, String> paramMap = (Map) destTableMap.get(parameterName);
query.setParameter("xyz",5);
try{
query.executeUpdate();
}catch(Exception ex){
ex.printStackTrace();
}
}
The exception stack trace that I receive is
Exception in thread "main" org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:73)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467)
... 11 more
From the documentation:
If the Session throws an exception, including any SQLException,
immediately rollback the database transaction, call Session.close()
and discard the Session instance. Certain methods of Session will not
leave the session in a consistent state. No exception thrown by
Hibernate can be treated as recoverable. Ensure that the Session will
be closed by calling close() in a finally block.
The transaction must be rolled back. So, if you want to continue executing if Hibernate throws an exception, you should put the execute the executeQuery method in its own transaction, using the REQUIRES_NEW propagation in the #Transactional annotation. This way, only this short transaction will be rolled back.
I tried to use the following to avoid this TransactionSystemException: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
#Transactional(readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public String foo(Object obj, String tableName, BindingResult result) throws Exception
{
// put some code here
}

Resources