I've spent the last week grappling with what I have reported as http://issues.alfresco.com/browse/ALFCOM-1683
Basically, when 2 clients access the same versioned resources in the Alfresco repository (via Web service + JCR API in my case), the database deadlocks. I'm using 2.9 community sources from subversion, and have been experiencing this and related issues for over 4 months.
I was using MySQL, but this week I've also tried Postgres and HSQL. I get problems whichever one I use.
I have attached a simple test case to that bug report, which simulates concurrent access by creating a number of threads which manipulate the content. Things fail badly if several threads access at once. I say badly, because (i) RetryingTransactionHelper doesn't actually retry in this case, and (ii) you have to restart the database before you can get reproducible behaviour.
In the case of HSQL:
Could not synchronize database state with session org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction
A 2 second delay between threads avoids this.
However, when I run it under MySQL although the initial thread which creates the content makes a node versionable, to my surprise, each subsequent thread
throws
javax.jcr.UnsupportedRepositoryOperationException: Node workspace://SpacesStore/667bf743-2e29-11dd-93d2-0108c9249316 is not versionable
at org.alfresco.jcr.item.NodeImpl.getBaseVersion.
I'd be very grateful for thoughts/suggestions how to work around these issues.
kind regards
Jason
jharrop
- Posts
- 61
- Achievements
- 3
- Member for
- 6 years 9 months
Stats
http://issues.alfresco.com/secure/attachment/11410/ALFCOM1683.java is a revised test case. The last paragraph of this post explains where I had gone wrong in the previous version.
This version demonstrates the StaleObjectStateException on MySQL.
Invoke it with something like http://localhost:8080/alfresco/testcase?firstsleep=2500&sleep=500&threads=10
My experience is that any thread invoked before the preceding ones have finished will result in a StaleObjectStateException. I see http://issues.alfresco.com/browse/CHK-1507 "teaches RetryingTransactionXXX to recognize StaleStateException", but nothing is retried in this case.
The problem with the first version of ALFCOM1683.java was that it relied on Alfresco to set up an implicit transaction around the test setup (which creates a directory, adds a file to it, and makes it versionable). HSQL was fine with this. Previous experience had taught me you need to use RetryingTransactionHelper, rather than relying on an implicit transaction, but I overlooked this since everything seemed to work. But not with MySQL. In the case of MySQL, the dir and file are created, and the file versioned for the purposes of that thread, but if there is an implicit transaction around versionable, it must get silently rolled back, because the subsequent threads insist it is not versionable.
jharrop
- Posts
- 61
- Achievements
- 3
- Member for
- 6 years 9 months
Stats
The solution to this was obvious, in the end.
The RetryingTransactionHelper was not retrying, because I was catching all exceptions in my RetryingTransactionCallback :oops:
Once I made sure the callback threw exceptions, the RetryingTransactionHelper retries on StaleObjectStateException.
Because I was catching the relevant exception, my
which tells RetryingTransactionHelper not to retry.
Before I realised the error of my ways, I had added
server.transaction.mode.default=PROPAGATION_REQUIRED, +org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureExceptionto transaction.properties.
This had the desired effect - org.springframework.transaction.interceptor.RuleBasedTransactionAttribute now ruled that the transaction should not be rolled back. But doing that is not necessary, provided you don't catch the exception! And nor is it sufficient .. you still have to throw the exception in order for RetryingTransactionHelper to do its thing. So forget it.
In summary, the key thing is to make sure the callback throws exceptions, and that your exception is one of the RETRY_EXCEPTIONS defined in RetryingTransactionHelper.
Hope this experience is helpful to someone.
The debug in RetryingTransactionHelper can be improved a little. Each time the transaction is retried, a new transaction is actually created. So where it logs the txn and iteration, the iteration increments, but the txn is always different. It is easy enough to log the transaction the retry is replacing, and worth doing in order to convince yourself things are working properly.
http://issues.alfresco.com/secure/attachment/11411/ALFCOM1683.java is an updated version of the test case, with the main issue now solved.
Here is the output of http://localhost:8080/alfresco/testcase?firstsleep=2500&sleep=200&threads=6 for anyone interested (6 threads, 200 ms apart, update versioned content via JCR).
jdunkelberger
- Posts
- 1
- Achievements
- 0
- Member for
- 3 years 10 months
Stats
Thanks for this post! Google picked it up.
I think I may be having the same fundamental issue with catching exceptions too eagerly (in order to produce useful faces error messages).