Multithreaded mode of OpenJPA kernel and parallel database operation in Slice
a) OpenJPA allows multiple threads to execute a single instance of EntityManager. By default, OpenJPA assumes that a single thread is invoking operations on a particular instance of EntityManager.
openjpa.Multithreaded configuration property can be set to true to signal multithreaded acceess. Under multithreaded mode, OpenJPA kernel classes acquire an instance-level Reentrant lock before almost any of its methods and
releases the lock on the method's finally block.
b) Slice executes most of the frequent database operations (query, flush) on individual slice in separate threads.
These two threading models conflict and give rise to a classic deadlock scenario as follows:
1. Assume EntityManager instance em, a Query instance Q created by E, openjpa.Multithreaded=true, a user thread UT and three slices S1,S2,S3
2. T calls em.x() or Q.y()
3. em/Q acquires a reentrant lock L on thread UT and invokes lower-layer method which eventually invokes Slice operations
4. Slice spawns three threads ST1, ST2, ST3 and on each of these threads invoke identical operation S.z()
5. If S.z() on ST1 invokes any operation of em/Q then ST1 can not acquire L as it is acquired by em/Q in step 3 and yet to be released. The architecture of Slice makes it typical that S.z() invokes one or more method on em or Q.
6. em.x()/Q.y() can not release L till S.z() finishes
7. S.z() can not finish because ST1 waits for L to be released by UT
a) openjpa.Multithreaded is a non-default option and single threaded operation on em is more prevalent. Note that single threaded access does not imply that em can only be invoked only on UT. It is perfectly permissible to
start a transaction of em on some thread UT, commit the transaction and then start another transaction on same em on a different thread UT2 under default mode of openjpa.Multithreaded=false.
b) Execution of common database operations on each slice S1, S2,... in parallel has definite performance benefit and should be the default choice. And this is at par with the default choice of openjpa.Multithreaded=false
1. Under openjpa.Multithreaded=true, execute database operation on all slices on the same user thread UT. Otherwise, execute database operation on each slice on separate thread
2. Modify OpenJPA kernel's threading model to make it more fine-grained, read/write sensitive
3. Detect deadlock and throw exception as ST1 waits on L
My preferred solution is (1).