I have created a function which is trying to save something in DB where version is enabled hence it can give optimistic lock exception. I catch the exception and try it again after fetching from DB. I am also using @transactional annotation to start a new transaction everytime. But I get org.springframework.orm.ObjectOptimisticLockingFailureException even before my save function is called.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleExecution(@NonNull final Execution execution, final Map<String, Object> data, final String pattern) {
log.info("Processing notification pattern {} with data {} for execution {}",pattern,data,execution);
if (ExecutionStatus.PROCESSING.equals(execution.getStatus()) || ExecutionStatus.WAITING.equals(execution.getStatus())) {
//Put the received notification in the execution queue
List<Map<String, Object>> notificationQueue = execution.getNotificationQueue();
Map<String,Object> notificationMap = new HashMap<>();
notificationMap.put(pattern, data);
notificationQueue.add(notificationMap);
execution.setNotificationQueue(notificationQueue);
try
{
executionBuilder.save(execution);
}
catch(OptimisticLockingFailureException e)
{
log.error("Save of execution {} failed due to optimistic locking, retrying",execution);
//Fetch the object again and call again
Optional<Execution> savedExecution = executionBuilder.findOne(execution.getId());
if(savedExecution.isPresent())
{
handleExecution(savedExecution.get(),data, pattern);
}
}
}
}
The detailed exception which I get is:
2018-07-11 08:09:39.408 INFO 8251 --- [thread_pool_executor-45]
o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of
batch it still contained JDBC statements
2018-07-11 08:09:39.408 ERROR 8251 --- [thread_pool_executor-45] reactor.bus.EventBus : Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:320) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521) ~[spring-orm-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at com.gor.platform.process.support.service.builder.ExecutionBuilderImpl$$EnhancerBySpringCGLIB$$a036b2e6.patch(<generated>) ~[process-service-2.0-SNAPSHOT.jar:2.0-SNAPSHOT]
at com.gor.platform.process.support.service.event.receiver.ProcessEventReceiver.accept(ProcessEventReceiver.java:58) ~[process-service-2.0-SNAPSHOT.jar:2.0-SNAPSHOT]
at com.gor.platform.process.support.service.event.receiver.ProcessEventReceiver.accept(ProcessEventReceiver.java:23) ~[process-service-2.0-SNAPSHOT.jar:2.0-SNAPSHOT]
at reactor.bus.EventBus$3.accept(EventBus.java:317) ~[reactor-bus-2.0.8.RELEASE.jar:na]
at reactor.bus.EventBus$3.accept(EventBus.java:310) ~[reactor-bus-2.0.8.RELEASE.jar:na]
at reactor.bus.routing.ConsumerFilteringRouter.route(ConsumerFilteringRouter.java:72) ~[reactor-bus-2.0.8.RELEASE.jar:na]
at reactor.bus.EventBus.accept(EventBus.java:591) [reactor-bus-2.0.8.RELEASE.jar:na]
at reactor.bus.EventBus.accept(EventBus.java:63) [reactor-bus-2.0.8.RELEASE.jar:na]
at reactor.core.dispatch.AbstractLifecycleDispatcher.route(AbstractLifecycleDispatcher.java:160) [reactor-core-2.0.8.RELEASE.jar:na]
at reactor.core.dispatch.MultiThreadDispatcher$MultiThreadTask.run(MultiThreadDispatcher.java:74) [reactor-core-2.0.8.RELEASE.jar:na]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_171]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_171]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_171]
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:67) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:54) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:46) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3082) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2961) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3341) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:145) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:465) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2963) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2339) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:485) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
The executionBuilder.save internally calls dao.save which is not seen in the stack trace. Why is this exception getting thrown even before the save is called. When save is called then the exception is caught and things work fine. But occasionally this exception is thrown without the save and hence doesn't get caught.
My gut feeling says it has something to do with @Transactional.
For the information my application is multithreaded and many thread might modify the same data at the same time.
Any help would be appreciated.
you can handle optimistic lock using spring retry option inside the controller. Download for this class
import org.springframework.retry.annotation.Retryable;
For Example
@RequestMapping(value = CommonConstants.DETAILS_ADD_OR_UPDATE, method = RequestMethod.POST)
@Retryable(value = { StaleObjectStateException.class,
HibernateOptimisticLockingFailureException.class }, maxAttempts = 1, backoff = @Backoff(delay = 5000))
public ResponseJson addOrUpdate(@RequestBody User user, HttpServletRequest request) {
try{
responseJson.setResponse(adminService.update(userId, user));
}
catch (DataIntegrityViolationException | ConstraintViolationException e) {
ErrorInfo errorInfo = errorCodeHelper.getErrorInfo(CommonConstants.E1071_ERROR_CODE,
CommonConstants.E1071_ERROR_DESCRIPTION);
throw new ServiceException(errorInfo);
}
return responseJson;
}
This is the important one add on the top of the method
@Retryable(value = { StaleObjectStateException.class,
HibernateOptimisticLockingFailureException.class }, maxAttempts = 1, backoff = @Backoff(delay = 5000))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With