Reputation: 6331
I have an issue where I get a StaleStateException
because a certain delete of an entity is executed in 2 threads. This is because a thread started by a REST call is deleting the object and calls another service to clean some things up there, but this other service puts some data on a Kafka stream as a result. This Kafka stream is constantly being read from another thread and also results in the object being deleted. But depending who gets there first I can get this exception:
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: HikariProxyPreparedStatement@821401258 wrapping delete from notification_setting where id='f47ef4b1-eb54-4d3a-a6f0-fc4518704288'::uuid and db_version=0
Now in this particular case with the delete being done by a system action, I really don't care about the StateStateException
and resulting OptimisticLockingException
. Both actions just have to delete the object and I don't mind who gets to it first. But I don't want to remove the @Version property entirely from this entity. Can I temporarily disable the optimistic locking for a particular delete?
The code isn't too relevant but here it is. I already tried deleting by id but a deleteAll
has the same issue.
eventLevelSubscriptions.forEach(
eventLevelSubscription -> notificationEventLevelSubscriptionRepo.deleteById(eventLevelSubscription.getId()));
notificationSettings.forEach(notificationSetting -> notificationSettingRepo.deleteById(notificationSetting.getId()));
em.flush();
for (NotificationSettingKey notificationSettingKey : notificationSettingKeys) {
kafkaWritingService.removeNotificationSetting(producer, notificationSettingKey);
}
Note the flush because we need to make sure this part succeeds before updating another stream on Kafka.
I tried simply try/catching the whole code because I don't even need to perform the deletes in the second thread as they're already done in the first thread, but then Hibernate seems to get in a corrupt state where an exception still is thrown in the end.
Exception in thread "Thread-6" org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:631)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
...
at java.base/java.lang.Thread.run(Unknown Source) (note: this is my kafka consumer thread)
Upvotes: 2
Views: 2377
Reputation: 5207
A) You cannot disable optimistic locking. Why are you using it at all? This exception may be an indicator that your logic is inconsistent. Check if your logic is really correct.
B) If after reviewing you still want to keep the logic as it is, it means you should treat this exception in such cases as a correct behavior (otherwise back to A and review/change the logic), which means you should catch the exception.
C) If your current code is running within a single transaction: Don't delete all entities within a single step, because some of them might have been deleted, the others have not. A solution can be following:
deleteById()
to a new methodforEach()
call this method instead of calling the repositoryD) Consider also a non object oriented approach: Use JPQL or SQL to delete entries. Advantage: There will be no exception if some entries have already been deleted earlier.
Upvotes: 2