-
Suggestion
-
Resolution: Unresolved
-
None
-
4
-
Confluence doesn't have generic retry mechanism for handling Transaction rollbacks caused by HibernateOptimisticLockingFailureException or StaleObjectStateException.
When there is a race condition, one thread is bound to see such exceptions, Confluence should try again to perform the operation instead of throwing this error to user. We need some level of guidelines and handling on UserActions.
One other example could be a job with modifying 100s of page and in parallel if some user alters a page, one of job/user can see Optimistic Lock exceptions.
This is similar to AllPage delete operation in a space which currently doesn't have batching.
Ideally, for there cases the jobs should have batching and thats a programmer's choice.
But, a user action shouldn't be rolled-back instead it should be retried or handled in a graceful manner.
Example trace:
Caused by: org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException: Object of class [com.atlassian.confluence.pages.Page] with identifier [6947404]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.atlassian.confluence.pages.Page#6947404] at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:284) at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:392) at org.springframework.orm.hibernate5.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:351) at com.atlassian.confluence.core.persistence.hibernate.HibernateObjectDao.findNamedQueryStringParams(HibernateObjectDao.java:486) at com.atlassian.confluence.core.persistence.hibernate.HibernateObjectDao.findNamedQueryStringParam(HibernateObjectDao.java:426) at com.atlassian.confluence.pages.persistence.dao.hibernate.AbstractHibernateAttachmentDao.getLatestVersionsOfAttachmentsWithAnyStatus(AbstractHibernateAttachmentDao.java:163) at com.atlassian.confluence.pages.attachments.AbstractDelegatingAttachmentDao.getLatestVersionsOfAttachmentsWithAnyStatus(AbstractDelegatingAttachmentDao.java:90) at com.atlassian.confluence.pages.DefaultAttachmentManager.getLatestVersionsOfAttachmentsWithAnyStatus(DefaultAttachmentManager.java:399) at com.atlassian.confluence.pages.DelegatorAttachmentManager.getLatestVersionsOfAttachmentsWithAnyStatus(DelegatorAttachmentManager.java:71) at com.atlassian.confluence.impl.pages.attachments.ReadThroughCachingAttachmentManager.getLatestVersionsOfAttachmentsWithAnyStatus(ReadThroughCachingAttachmentManager.java:71) at sun.reflect.GeneratedMethodAccessor1698.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy249.getLatestVersionsOfAttachmentsWithAnyStatus(Unknown Source) at com.atlassian.confluence.pages.AbstractPage.getSearchableDependants(AbstractPage.java:75) at com.atlassian.confluence.search.lucene.QueuingConfluenceIndexer$InternalConfluenceIndexer.unIndexIncludingDependents(QueuingConfluenceIndexer.java:161) at com.atlassian.confluence.search.lucene.QueuingConfluenceIndexer.unIndexIncludingDependents(QueuingConfluenceIndexer.java:81) at com.atlassian.confluence.pages.DefaultPageManager.lambda$trashPage$11(DefaultPageManager.java:960) at com.atlassian.confluence.impl.search.IndexerEventPublisher.lambda$publishCallbackEvent$0(IndexerEventPublisher.java:29) at com.atlassian.confluence.impl.search.IndexerEventListener.onEvent(IndexerEventListener.java:42) at sun.reflect.GeneratedMethodAccessor1709.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.atlassian.event.internal.SingleParameterMethodListenerInvoker.invoke(SingleParameterMethodListenerInvoker.java:42) ... 349 more
Workaround
While the problem cannot be entirely avoided with certainty, there are a few options to reduce the risk of concurrent updates:
- If you have control over the code, introduce batched processing of the requests (e.g. perform the operation by space, on a list of pages)
- For one off tasks, run them during low traffic times (e.g. outside of business hours)
- relates to
-
CONFSERVER-58663 Concurrently purging all items from the trash may throw an error with HibernateOptimisticLockingFailureException
- Closed
-
CONFSERVER-40416 "Saving is not possible at the moment" when copying a page with alot of attachments within the same space.
- Gathering Impact