Spring & Hibernate SessionFactory performance issue - spring

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.

Related

Saving a record using JPA in a Spring Boot Scheduler

I'm using a Spring Boot Scheduler to run a query on the DB daily to find some records based on a condition and update the records returned. Fetching the records using JPA works fine, but when I loop through them, update them, and try to save each updated record I get the following error:
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 at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:81) at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:104) at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:562) ... 30 more Caused by: java.lang.NullPointerException at com.xxx.yyy.config.JpaAuditingConfiguration.auditorProvider$lambda-0(JpaAuditingConfiguration.kt:15) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) at com.sun.proxy.$Proxy168.getCurrentAuditor(Unknown Source) at java.base/java.util.Optional.map(Optional.java:265) at org.springframework.data.auditing.AuditingHandler.getAuditor(AuditingHandler.java:109) at org.springframework.data.auditing.AuditingHandler.markModified(AuditingHandler.java:104) at org.springframework.data.jpa.domain.support.AuditingEntityListener.touchForUpdate(AuditingEntityListener.java:112).
Here is the scheduler code I have. If I run the same code inside my service and call it using an endpoint everything works fine:
#Component
class Scheduler(
private val repository: Repository
) {
#Scheduled(cron = "0 0 2 * * *")
fun expire() {
val records = repository.findRecords()
for (record in records) {
try {
// Call some external API using record.id but this part is commented out for now until the saving works
record.active = false
repository.save(record)
} catch (ex: Exception) {
logger.error("Error expiring record " + record.id)
logger.error("Exception: ${ex.printStackTrace()}")
continue
}
}
}
}
the null pointer exception happens in the JpaAuditingConfiguration config I use for storing the created_at and last_modified_at dates. Here is the code I have for that class:
#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorProvider")
class JpaAuditingConfiguration {
#Bean
fun auditorProvider(): AuditorAware<String> {
return AuditorAware { Optional.of(SecurityContextHolder.getContext().authentication.name) }
}
}
Your JpaAuditingConfiguration requires the security context to be non null when you make modifications. When you're running your task in a scheduler there is no active request, so no active session, and therefore your authentication is null.
Usually, this is solved by making a special app user and manually authenticating them in your scheduled task.

Consumers from Rabbit were stopped and never recover

My spring boot application has been running with no erros during months using RabbitMq. In the last weeks, my consumer stopped working and I have to restart my application. Everyday I have to restart the application.
I noticed that it happens after this error in my log:
24-07-2021 07:20:08.138 [SimpleAsyncTaskExecutor-1] WARN o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 17002, SQLState: 08006
24-07-2021 07:20:08.295 [SimpleAsyncTaskExecutor-1] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - IO Error: Socket read interrupted, Authentication lapse 682 ms.
24-07-2021 07:20:10.608 [SimpleAsyncTaskExecutor-1] WARN o.s.a.r.l.ConditionalRejectingErrorHandler - Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener method 'protected void br.jus.tjmg.certidaoEmissao.application.queues.EmissaoCriminalHistoricaConsumer.processaMensagem(br.jus.tjmg.certidao.webservice.ConsultaCertidoesRequest)' threw exception
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:198)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:127)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1477)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1400)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1387)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1366)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:848)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:832)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:78)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1073)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:450)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:378)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at br.jus.tjmg.certidaoEmissao.application.services.EmissaoCertidaoService$$EnhancerBySpringCGLIB$$46f4e1c7.atualizarExecucaoCertidao(<generated>)
at br.jus.tjmg.certidaoEmissao.application.queues.EmissaoCriminalHistoricaConsumer.processaMensagem(EmissaoCriminalHistoricaConsumer.java:35)
at sun.reflect.GeneratedMethodAccessor49.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:181)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:114)
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:51)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:190)
... 10 common frames omitted
Caused by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:115)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:109)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:136)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getConnectionForTransactionManagement(LogicalConnectionManagedImpl.java:254)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.begin(LogicalConnectionManagedImpl.java:262)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.begin(JdbcResourceLocalTransactionCoordinatorImpl.java:214)
at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:56)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:162)
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:400)
... 25 common frames omitted
Caused by: java.sql.SQLRecoverableException: IO Error: Socket read interrupted, Authentication lapse 682 ms.
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:794)
at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:688)
at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:39)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:691)
at oracle.jdbc.pool.OracleDataSource.getPhysicalConnection(OracleDataSource.java:384)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:273)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:198)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:176)
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:35)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:106)
... 32 common frames omitted
Caused by: java.io.IOException: Socket read interrupted, Authentication lapse 682 ms.
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:790)
... 42 common frames omitted
Caused by: java.net.SocketTimeoutException: Socket read interrupted
at oracle.net.nt.TimeoutSocketChannel.read(TimeoutSocketChannel.java:152)
at oracle.net.ns.NIOHeader.readHeaderBuffer(NIOHeader.java:82)
at oracle.net.ns.NIOPacket.readFromSocketChannel(NIOPacket.java:139)
at oracle.net.ns.NIOPacket.readFromSocketChannel(NIOPacket.java:101)
at oracle.net.ns.NIONSDataChannel.readDataFromSocketChannel(NIONSDataChannel.java:80)
at oracle.jdbc.driver.T4CMAREngineNIO.prepareForReading(T4CMAREngineNIO.java:98)
at oracle.jdbc.driver.T4CMAREngineNIO.unmarshalUB1(T4CMAREngineNIO.java:534)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:485)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:252)
at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:499)
at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:1279)
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:663)
... 42 common frames omitted
24-07-2021 07:20:20.230 [SimpleAsyncTaskExecutor-1] ERROR o.s.a.r.l.SimpleMessageListenerContainer - Stopping container from aborted consumer
24-07-2021 07:20:20.525 [SimpleAsyncTaskExecutor-1] INFO o.s.a.r.l.SimpleMessageListenerContainer - Waiting for workers to finish.
24-07-2021 07:20:20.584 [SimpleAsyncTaskExecutor-1] INFO o.s.a.r.l.SimpleMessageListenerContainer - Successfully waited for workers to finish.
I would like to avoid my consumer gets down. I have tried many different solutions, for example to restart my application after some events, but they didn't work.
public static void main(String[] args) {
context = SpringApplication.run(CertidaoApplication.class, args);
}
public static void restart() {
ApplicationArguments args = context.getBean(ApplicationArguments.class);
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(CertidaoApplication.class, args.getSourceArgs());
});
thread.setDaemon(false);
thread.start();
}
#EventListener
public void handleEvent(ListenerContainerConsumerTerminatedEvent e) {
logger.info("Restart spring boot. ListenerContainerConsumerTerminatedEvent");
CertidaoApplication.restart();
}
#EventListener
public void handleEvent(ListenerContainerConsumerFailedEvent e) {
logger.info("Restart spring boot. ListenerContainerConsumerFailedEvent");
CertidaoApplication.restart();
}
#EventListener
public void handleEvent(ContextStoppedEvent e) {
logger.info("Restart spring boot. ContextStoppedEvent");
CertidaoApplication.restart();
}
#EventListener
public void handleEvent(ContextClosedEvent e) {
logger.info("Restart spring boot. ContextClosedEvent");
CertidaoApplication.restart();
}
Here my configuration:
#Bean
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> prefetchRabbitListenerContainerFactory(ConnectionFactory rabbitConnectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory);
factory.setPrefetchCount(2);
factory.setDefaultRequeueRejected(Boolean.FALSE);
factory.setMessageConverter(jsonMessageConverter());
factory.setMissingQueuesFatal(Boolean.FALSE);
return factory;
}
My consumer:
#RabbitListener(queues = "${queue}", concurrency = "1", containerFactory = "prefetchRabbitListenerContainerFactory")
protected void processMessage(ConsultaRequest message) {
logger.info("Test" + message.getNumber());
Thanks a lot.

Spring boot, multi-tenet, multi-module, #Transactional , parallelStream

I am trying to insert some 50k records into db. We have used AbstractRoutingDataSource which resolve Datasource using TenantContext which is a utility class and has a private static final ThreadLocal CURRENT_TENANT = new ThreadLocal<>();
when I am using parallel stream or if I am trying to make the method #Async I am getting the below error
Code:
.parallelStream()
.forEach(row -> {
TenantContext.setCurrentTenant(centerCd);
someDao.insert(row);
});
Error:
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null]
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:305)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:378)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null]
at org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.determineTargetDataSource(AbstractRoutingDataSource.java:207)
at org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:169)
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:262)
... 10 common frames omitted
It works exactly like you described: your TenantContext is exactly ThreadLocal and exists in a thread, which is initiated either by parallelStream() or Async method. (in reality, the call inside of the Async or forEach method is a run from Runnable)
The data source is attempted to be injected/resolved at start of the thread: because your transaction have to be started at the thread creation, before your Runnable gets into a run method. And at this moment of time you haven't yet specified your tenant, call TenantContext.setCurrentTenant(centerCd) is performed later in a run method implementation.
I would suggest applying such structure to your code:
class TenantAwareThread extends Thread {
public TenantAwareThread(Runnable target, TenantData tenantData) {
super(target);
TenantContext.setCurrentTenant(tenantData);
}
}
#Autowired
TaskExecutor executor;
void startTask(TenantData tenantData, RowData row) {
executor.execute(
new TenantAwareThread(() -> {
someDao.insert(row);
},
tenantData));
}
You create a new thread type which is aware of tenant data from the very beginning. And simply wraps your executions into such thread.

Hikaricp Oracle connection issue

I have the following code to get connection to the oracle database. But using hikaricp I am getting the exception.
java.sql.SQLException: Invalid Oracle URL specified: OracleDataSource.makeURL
Code:
private static HikariDataSource dataSource() {
final HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setMaximumPoolSize(100);
hikariDataSource.setMinimumIdle(10);
hikariDataSource.setDataSourceClassName("oracle.jdbc.pool.OracleDataSource");
Properties properties = new Properties();
properties.put("user", "user");
properties.put("password", "pass");
properties.put("databaseName", "XE");
properties.put("serverName", "192.168.21.13");
properties.put("portNumber", "1521");
hikariDataSource.setDataSourceProperties(properties);
//Additionally I am setting connection test query and max life time also
return hikariDataSource;
}
The complete stacktrace is
Exception in thread "main" java.lang.RuntimeException: Fail-fast during pool initialization
at com.zaxxer.hikari.pool.HikariPool.fillPool(HikariPool.java:499)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:162)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:113)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:102)
at java.lang.reflect.Method.invoke(Method.java:597)
Caused by: java.sql.SQLException: Invalid Oracle URL specified: OracleDataSource.makeURL
at oracle.jdbc.pool.OracleDataSource.makeURL(OracleDataSource.java:1277)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:185)
at oracle.jdbc.pool.OracleDataSource.getConnection(OracleDataSource.java:157)
at com.zaxxer.hikari.pool.HikariPool.addConnection(HikariPool.java:418)
at com.zaxxer.hikari.pool.HikariPool.fillPool(HikariPool.java:498)
... 11 more
How do I establish successful pool? I am using oracle jar
http://download.oracle.com/otn/utilities_drivers/jdbc/111070/ojdbc6.jar
and the jdk is 1.6. Hikaricp version is 2.2.5 for java 6.
try to set the driver type to thin.
properties.put("driverType", "thin");

Scheduler stopped when deadlock happened in Spring Transaction

I've developed a monitoring application that uses Spring-3.0 Hibernate-3.6.5 with SpringHibernateTemplate as DAO. Database SQL Server 2008. This app has long running transaction every day.
#Repository("areaDaoHibernate")
public class AreaDAO implements IArea {
protected HibernateTemplate template = null;
#Autowired #Required
public void setSessionFactory(SessionFactory sessionFactory) {
template = new HibernateTemplate(sessionFactory);
}
// basic dao method
}
This app has some scheduling (implements Runable) via springbeans.
#Component("dailyTask")
#Scope("prototype")
public class DailyTask implements Runnable {
#Override
public void run() {
// running task
}
}
This app uses Apache DBCP for pooling.
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="CustodyDataSource" />
</property>
…
</bean>
<bean id = "CustodyDataSource"
class = "org.apache.commons.dbcp.BasicDataSource"
p:driverClassName = "${jdbc1.driverClassName}"
p:url = "${jdbc1.url}"
p:username = "${jdbc1.username}"
p:password = "${jdbc1.password}"
p:maxIdle = "${jdbc1.maxIdle}"
p:maxActive = "${jdbc1.maxActive}"
p:validationQuery = "${jdbc1.validationQuery}"
p:testWhileIdle = "${jdbc1.testWhileIdle}" />
<bean id = "transactionManager"
class = "org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref = "sessionFactory" />
I have problem when this app deadlock, the scheduling stop to run.
The error shows:
2012-12-27 12:57:20,861 WARN (JDBCExceptionReporter:233) - SQL Error: 1205, SQLState: 40001
2012-12-27 12:57:20,862 ERROR (JDBCExceptionReporter:234) - Transaction (Process ID 55) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
2012-12-27 12:57:20,866 ERROR (DailyTask:238) - org.springframework.dao.CannotAcquireLockException: could not update: [com.btpn.custody.entity.MasterData#00176ed4-b7a2-4da5-a266-ffaab3654050]; SQL [update T_CIF set __UPDATED_DATE=?, STATUS_MD=? where __SID=?]; nested exception is org.hibernate.exception.LockAcquisitionException: could not update: [com.btpn.custody.entity.MasterData#00176ed4-b7a2-4da5-a266-ffaab3654050]
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:633)
at org.springframework.orm.hibernate3.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:793)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:664)
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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy33.relateWithBranch(Unknown Source)
at com.btpn.custody.bean.DailyTask.relateMasterDataWithBranch(DailyTask.java:1166)
at com.btpn.custody.bean.DailyTask.manualInitial(DailyTask.java:213)
at com.btpn.custody.bean.DailyTask.run(DailyTask.java:197)
at java.lang.Thread.run(Unknown Source)
Caused by: org.hibernate.exception.LockAcquisitionException: could not update: [com.btpn.custody.entity.MasterData#00176ed4-b7a2-4da5-a266-ffaab3654050]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:107)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2612)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2494)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2821)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
... 11 more
Caused by: java.sql.SQLException: Transaction (Process ID 55) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
at net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:368)
at net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:2820)
at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2258)
at net.sourceforge.jtds.jdbc.TdsCore.getMoreResults(TdsCore.java:632)
at net.sourceforge.jtds.jdbc.JtdsStatement.processResults(JtdsStatement.java:584)
at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQL(JtdsStatement.java:546)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeUpdate(JtdsPreparedStatement.java:504)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:102)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:46)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2590)
... 23 more
So my questions:
Should I change connection pool to avoid deadlock problem?
Why my scheduling bean stop run after deadlock happen?
The deadlock occurred in SQL Server. This is neither Spring, Hibernate, the connection pool nor SQL Servers fault. The problem is that your application is locking rows that either your application, or other applications are also trying to lock (in a deadlock manner).
The easiest fix is probably to have a retry-strategy for your job (just retry the whole transaction if it fails). You could also try to figure out exactly which applications / users were involved in the deadlock by looking at the SQL Server logs.

Resources