-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Trying to understand locking (using JPA)
PostPosted: Sat Feb 07, 2009 12:53 am 
Beginner
Beginner

Joined: Thu Jun 21, 2007 1:47 pm
Posts: 46
Sorry if this is a dumb question - I've got an object "Subscription" which I want to edit concurrently. Because when editing subscriptions I also send emails, charge credit cards, etc.. I can't rely on the "OptimisticLockException" that hibernate will throw if the version number is off.

Originally, I just used code like:

Code:
Subscription s = em.get(subId);
em.lock(s, LockModeType.READ);


From this, hibernate issues a "select for update" which will create an exclusive lock on the DB and wait for any concurrent transaction which also wants to write the object to complete before continuing.

However, I still got optimistic lock exceptions. I realized that this must be because even though I'd waited for the other operation to finish, I'm still using the "old" object (em.lock() doesn't update the contents of the object). So, I added this:

Code:
em.refresh(s);


Unfortunately, this introduced a problem where any changes already made to the object will get dumped - for example, I have a shared method that operates on the object, but this shared code should only refresh the object if it was not already locked.

One option I have is to manually manage the "locked" state of the object and only do a lock/refresh if the object has not previously been locked in this transaction. The other option I am considering is to do an em.flush() before the lock/refresh, which would theoretially work since the refresh() would just reload the same data that was flush()ed, however, this also seems potentially quite inefficient.

I'm considering putting a transient boolean inside all my objects that I lock (then they'll have a version number and a boolean) and they can use that to decide whether they've been locked in the current transaction (not sure how best to track this).

How do other handle this locking issue? The way the locks work right now seem to involve a lot of work and there's a lack of warnings about the pitfalls I am falling into one after the other ...

All suggestions and feedback appreciated!


Last edited by dobes on Sat Feb 07, 2009 5:20 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: em.lock throws OptimisticLockException?
PostPosted: Sat Feb 07, 2009 2:40 pm 
Beginner
Beginner

Joined: Thu Jun 21, 2007 1:47 pm
Posts: 46
I'm also seeing that em.lock() can throw an optimistic lock exception ... is there any way to avoid the race condition between get() (or a query) and lock()?

Should I use a loop each time I lock an object to refresh() the object if I get an optimistic lock exception and try again ... and does this mean I don't have to refresh() the object *after* the call to lock() since if the object was stale I'd have gotten an OptimisticLockException() ?

Basically:

Code:
       for(;;) {
            try {
                // Assumptions:
                //  1. If the object was changed in another transaction between fetching the object and lock(), OptimisticLockException is thrown
                //  2. If the object was previously locked during this transaction, lock() is basically a no-op and won't throw OptimisticLockException because we have a database mutex
                //  3. The caller doesn't mind if we dump their changes to the object in order to resolve a locking issue.
                //
                em.lock(entity, mode);
                break;
            } catch(OptimisticLockException ole) {
                em.refresh(entity);
            }
        }


Somehow I have a feeling, though, that the OptimisticLockException will set my transaction in "rollback mode" and I won't be able to do anything more during that transaction... I guess I'll find out.

Any advice appreciated ... locking is hard to test so I'd rather someone provide a tried and true method to do this without any race conditions or exceptions being thrown.

Thanks!


Top
 Profile  
 
 Post subject: As I feared ...
PostPosted: Sat Feb 07, 2009 5:29 pm 
Beginner
Beginner

Joined: Thu Jun 21, 2007 1:47 pm
Posts: 46
It seems like if the call to lock() fails and throws OptimisticLockException, my transaction is dead; I get this error:

Code:
javax.ejb.TransactionRolledbackLocalException: Exception thrown from bean; nested exception is: javax.persistence.TransactionRequiredException: no transaction is in progress


Do I really have to throw this all the way back to the servlet (or somewhere outside the transaction) and try again?

If this is running from a TimerService timer, I have no idea how to restart the transaction, is it even possible?

Help!


Top
 Profile  
 
 Post subject: Using Session directly seems to generate invalid SQL
PostPosted: Sat Feb 07, 2009 8:28 pm 
Beginner
Beginner

Joined: Thu Jun 21, 2007 1:47 pm
Posts: 46
I'm trying to bypass the EntityManager and use the Session direclty but now I'm getting an error that I don't know what to do with:

Code:
org.postgresql.util.PSQLException: ERROR: SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT


I'm trying to fetch this entity using session.get(id, LockMode.UPGRADE) and the entity is mapped with TABLE_PER_CLASS. I suspect that the locking code generates unsupported SQL for TABLE_PER_CLASS mappings on PostgreSQL.

I'll try and find a workaround ...


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 07, 2009 9:06 pm 
Beginner
Beginner

Joined: Thu Jun 21, 2007 1:47 pm
Posts: 46
Fetching the object first and then locking it resolves that error:

[code]
job = session.get(Job.class, jobId);
session.refresh(job, LockMode.UPGRADE);
[code]

If I try to pass LockMode.UPGRADE to get() then I get the error, presumably because the TABLE_PER_CLASS fetch is using some fancy SQL to fetch from multiple tables concurrently and the ... FOR UPDATE feature doesn't support this.

Perhaps hibernate could split the operation automatically in this case, I'll file something in JIRA.

http://opensource.atlassian.com/project ... e/HHH-3762


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.