-
Bug
-
Resolution: Fixed
-
Low
-
6.15.4
-
20
-
Severity 3 - Minor
-
7
-
Issue Summary
The Space administrator can purge all pages that are in the Space Trash as described in Empty the trash or permanently delete a page.
Depending on the amount of content to be purged, this operation may take some time. This depends on how many pages, history versions, attachments, images and other items that are associated to deleted content.
If the purge all functionality is triggered again for the same set of content (by the same user or a different one), then a system error with a stack trace is sent to the space administrator.
Steps to Reproduce
- Install a vanilla instance of Confluence.
- This was validated in Confluence Server 6.15.4.
- Create two users as part of the confluence-administrators group – say it admin and admin1.
- It is expected you run the following steps as a Confluence administrator.
- Create a sample blank Space.
- Space Name: Purge Content; Space Key: PC
- Under the Purge Content Home page, create a new blank page.
- Page name: Purge Content - Root Page
- Create 1985 pages as child of Purge Content - Root Page.
- Since we are dealing with a vanilla instance, then we are reproducing the issue with an exaggerated number of pages; in a production environment this could happen with hundreds of pages considering history versions, attachments and so on.
- The following shell script may help you to create the child pages.
USRNAME=admin USRPWD=admin CONFBASEURL=http://localhost:26154/c6154 SPACE_KEY=PC PARENT_PAGEID=1476569 PAGE_TITLE_PRE=RootPage MAX_PAGES=1985 for PAGE_NUM in $(seq 1 $MAX_PAGES); do curl --user $USRNAME:$USRPWD -H "Content-Type: application/json" -H "Accept: application/json" -d '{"type":"page","title":"'$PAGE_TITLE_PRE$PAGE_NUM'", "ancestors":[{"type":"page","id":'$PARENT_PAGEID'}], "space":{"key":"'$SPACE_KEY'"},"body":{"storage":{"value":"<p>This is <br/> another new page</p>","representation": "storage"}}}' -X POST $CONFBASEURL/rest/api/content >/dev/null 2>&1 echo "$PAGE_TITLE_PRE$PAGE_NUM created" done
- Delete Purge Content - Root Page and all of its child pages.
- Go to Purge Content - Root Page > ... > Delete
- Choose Also delete child pages and then click on Next.
- Click on Delete on the confirmation page.
- Simultaneously access <Confluence Base URL>/pages/viewtrash.action?key=PC using both administrators users created a few steps before.
- You may do this using different browsers.
- As admin, click on the Purge All link.
- Don't click on the OK button yet – wait for it.
- As admin1, click on the Purge All link.
- As admin1, click on the OK button to confirm the purge operation.
- The delete operation starts, but there's no meaningful information about it.
- Quickly switch to the admin browser window and click on the OK button to confirm the purge operation.
Expected Results
On the second time the operation is confirmed (in this case triggered by admin, Confluence would notify the user the same operation is already occurring, gracefully dealing with concurrent (same) tasks.
To admin1, it would be expected to have a meaningful feedback in the UI to advise the operation was ongoing (and maybe lock the UI). Maybe something similar when a large set of pages are being deleted as in the image below.
Actual Results
Confluence sends admin to a System Error page with a stack trace similar to the below.
org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException: 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.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:283) at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:368) at org.springframework.orm.hibernate5.HibernateTemplate.execute(HibernateTemplate.java:315) at com.atlassian.confluence.mail.notification.persistence.dao.hibernate.HibernateNotificationDao.findNotificationsByContent(HibernateNotificationDao.java:117) at com.atlassian.confluence.internal.notification.persistence.DelegatingNotificationDaoInternal.findNotificationsByContent(DelegatingNotificationDaoInternal.java:99) at com.atlassian.confluence.mail.notification.DefaultNotificationManager.getNotificationsByContent(DefaultNotificationManager.java:124) at sun.reflect.GeneratedMethodAccessor1067.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:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at com.atlassian.spring.interceptors.SpringProfilingInterceptor.invoke(SpringProfilingInterceptor.java:16) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at com.atlassian.confluence.util.profiling.ConfluenceMonitoringMethodInterceptor.invoke(ConfluenceMonitoringMethodInterceptor.java:34) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy72.getNotificationsByContent(Unknown Source) at com.atlassian.confluence.core.DefaultContentEntityManager.removeContentEntityInternal(DefaultContentEntityManager.java:227) at com.atlassian.confluence.core.DefaultContentEntityManager.removeContentEntity(DefaultContentEntityManager.java:259) at sun.reflect.GeneratedMethodAccessor1532.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:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at com.atlassian.spring.interceptors.SpringProfilingInterceptor.invoke(SpringProfilingInterceptor.java:16) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at com.atlassian.confluence.util.profiling.ConfluenceMonitoringMethodInterceptor.invoke(ConfluenceMonitoringMethodInterceptor.java:34) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy118.removeContentEntity(Unknown Source) at com.atlassian.confluence.pages.AbstractPage.remove(AbstractPage.java:92) at com.atlassian.confluence.pages.Page.remove(Page.java:231) at com.atlassian.confluence.pages.DefaultTrashManager.deleteContentEntity(DefaultTrashManager.java:159) at com.atlassian.confluence.pages.DefaultTrashManager.access$100(DefaultTrashManager.java:27) at com.atlassian.confluence.pages.DefaultTrashManager$1.doInTransaction(DefaultTrashManager.java:145) at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) at com.atlassian.confluence.pages.DefaultTrashManager.deleteBlock(DefaultTrashManager.java:126) at com.atlassian.confluence.pages.DefaultTrashManager.emptyTrash(DefaultTrashManager.java:54) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 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:333) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at com.atlassian.spring.interceptors.SpringProfilingInterceptor.invoke(SpringProfilingInterceptor.java:16) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at com.atlassian.confluence.util.profiling.ConfluenceMonitoringMethodInterceptor.invoke(ConfluenceMonitoringMethodInterceptor.java:34) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy202.emptyTrash(Unknown Source) at com.atlassian.confluence.pages.actions.EmptyTrashAction.execute(EmptyTrashAction.java:11) (...) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) 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) at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:54) at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:46) at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3315) at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3552) at org.hibernate.action.internal.EntityDeleteAction.execute(EntityDeleteAction.java:99) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:586) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:460) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:50) at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1389) at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1474) at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1441) at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1410) at com.atlassian.confluence.mail.notification.persistence.dao.hibernate.HibernateNotificationDao.lambda$findNotificationsByContent$1(HibernateNotificationDao.java:121) at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:361) ... 440 more
Notes
The purge operation will not fail, since it would be running in the background. However, the second user might feel it failed because the System Error.
Although the operation and the stack trace error is similar, this issue is different from bug CONFSERVER-57823 – "Purge All" space trash fails with "org.hibernate.StaleStateException" due to Confluence's handling of CustomContentEntity.
Workaround
Currently there is no known workaround for this behavior. A workaround will be added here when available
- is related to
-
CONFSERVER-57823 "Purge All" space trash fails with "org.hibernate.StaleStateException" due to Confluence's handling of CustomContentEntity
- Closed
-
CONFSERVER-65136 Introduce Transaction Rollback handling in core and provide plugin API
- Gathering Interest
- relates to
-
CONFSERVER-57823 "Purge All" space trash fails with "org.hibernate.StaleStateException" due to Confluence's handling of CustomContentEntity
- Closed