Ok, I was able to create a multithreaded JUnit test to fail (with just 4 threads).
A race condition seems to arise when multiple threads are trying to modify state of the
same cache node(s). Here's the per-user test scenario:
public void run() {
| try {
| for (int i = 0; i < ITERATION_COUNT; i++) {
| login(userName);
| think();
|
| //get users
| getFriends2(userName);
| //readUserSession(userName);
| think();
|
| logout(userName);
| think();
|
| ++completedIterations;
| }
|
| } catch (Throwable t) {
| this.causeOfFailure = t;
| }
| }
If I omit getFriends2() -> test passes. Else, test fails.
If users participating in the test are NOT friends of each other (do not try to read each
other's data) then test passes. Else, test fails.
getFriends() includes friends' login status. In other words User A will call
User.getSessions() on user B if the 2 are friends.
Let's say user.userSessions#B is not in cache. Then A asks for B's info:
| A gets B's data:
| a1: userB.getSessions(); //will read from DB and put into cache
|
Simultaneously, B logs in:
B.login():
| b1: self.getSessions(); //will read from DB and put into cache
| b2: self.addSession(new UserSession()); //will invalidate user.userSessions#B
Seems like what's happening is that after both TXs are done, it is state from step
'a1' that ends up in cache.
Something like this order:
-a1 starts (cache miss, A is going to DB)
-b1
-b2
-a1 finished (A comes back from DB, inserts empty collection *after* B invalidated node in
step b2)
Still not clear what the root cause is here. Some possibilities:
- my code
- hibernate core
- hibernate-jbc integration code
- jbc core
I guess the problem lies either with versioning (either Hibernate versioning or MVCC
versioning) or (insufficient?) locking.
Side question on Synchronization ordering:
I looked at BTM source; seems like their Synchronization register is ordered:
| private List keys = new ArrayList();
| private Map objects = new TreeMap();
|
|
| public Scheduler() {
| }
|
| public void add(Object obj, int position) {
| Integer key = new Integer(position);
| List synchronizationsList = (List) objects.get(key);
| if (synchronizationsList == null) {
| if (!keys.contains(key)) {
| keys.add(key);
| Collections.sort(keys);
| }
| synchronizationsList = new ArrayList();
| objects.put(key, synchronizationsList);
| }
| synchronizationsList.add(obj);
| }
You said previously that assumptions around synchronization ordering were fixed for
JBossTS and JBC specifically. Was the fix to order synchronizations in TX manager impl?
The reason I ask is that Spring seems to use a Synchronization to close Hibernate
Sessions. Is there an expectation that *that* syncrhonization fire *before* JBC's
synchronization?
thanks
-nikita
View the original post :
http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4215452#...
Reply to the post :
http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&a...