-->
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.  [ 17 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Session use after StaleObjectException
PostPosted: Mon Aug 02, 2004 1:03 pm 
Newbie

Joined: Tue Sep 02, 2003 9:47 am
Posts: 8
Hi there,

was wondering if anyone has a solution to this little problem I have.

Basically, I am performing some processing on an object (we'll call it A) as a prelude to an update. As part of the update of this object, I need to update another object (we'll call this one B) to set some information from A.

Now these objects are all optimistically locked using Hibernates version number, so what I want to happen, is if the update on object B fails with a StaleObjectException, to simply re-load it, re-set the data, and try again. I don't want to have to abandon the entire operation. Similarly, I do not want to completely rollback the transaction, kill the session, and restart all of the operations that have to be done on object A.

The problem I get however, is if B fails to update because of a StaleObjectException, despite doing an evict on the object from the session, it seems to remain in the 'updates' ArrayList within SessionImpl, so that the next time I try to do anything with the session, a flush causes Hibernate to attempt the update again, which of course fails once more with a StaleObjectException.

What I would ideally like, is a way to remove it from this updates list, so despite that update failing I can continue using the session, effectively accepting the failure and carrying on. Is this possible? Is there an alternative which I have overlooked?

Any help would be much appreciated. Thanks.

Graham.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 02, 2004 7:34 pm 
Proxool Developer
Proxool Developer

Joined: Tue Aug 26, 2003 10:42 am
Posts: 373
Location: Belgium
Tell me if I'm wrong, but it seems you always need the most up-to-date version of B before you apply your changes on it.

If so, why don't you session.refresh(B) in advance ?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 4:28 am 
Newbie

Joined: Tue Sep 02, 2003 9:47 am
Posts: 8
Absolutely, it is the latest version we need, and we do load it for the first time directly before making the change to it to ensure we have the most up to date version.

However there are still cases where if 2 objects of the type of A are being processed at exactly the same time, they will both attempt to grab B and modify it. Obviously doesn't happen every time, but there is still a small chance (and it does happen).

This is why we want one of those to fail (as it does currently) then perform a search to locate a new object to update instead of B. However, as I said, because the Session still has the failed update in its updates list, whenever we try to do anything else on the Session, it tries the failed update again and fails.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 4:38 am 
Newbie

Joined: Tue Sep 02, 2003 9:47 am
Posts: 8
So I guess the key point here, which I didn't make clear in my original post, is that: if A is being processed, and the update of B fails, the search done to retry may not bring back B again, but some other object which will be updated instead. Hence why I don't want anything pertaining to B to be updated again.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 4:45 am 
Proxool Developer
Proxool Developer

Joined: Tue Aug 26, 2003 10:42 am
Posts: 373
Location: Belgium
graz wrote:
Absolutely, it is the latest version we need, and we do load it for the first time directly before making the change to it to ensure we have the most up to date version.


session.load() will return the version currently held in the session if already loaded - this version may be dirty (ie not in-sync with the database).

graz wrote:
However there are still cases where if 2 objects of the type of A are being processed at exactly the same time, they will both attempt to grab B and modify it. Obviously doesn't happen every time, but there is still a small chance (and it does happen).


This where I don't understand your problem. If these two instances of A are in the same session, they will bot get the same instance of B. This instance will be loaded at that time unless you have loaded it prior to As in the current session.

So, if you start by making sure your object B is up-to-date (by a call to refresh) - you shouldn't have any problem.

Now, things are different if the B you are talking about comes from anoter session and is held held in your http session.... Awlays make sure to be consistent in where your objects are coming (from current or previous session - don't forget this represents a snapshot view at a particular moment in time).


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 5:00 am 
Newbie

Joined: Tue Sep 02, 2003 9:47 am
Posts: 8
Hi again,

thanks for your help so far with this, I haven't done a very good job in making myself clear, so allow me to try again and explain exactly what is going on.

t1: User1 submits object A for processing.
t2: User2 submits object C for processing.
t3: Operations start on object A in its own thread, using its own Session and transaction.
t4: Operations start on object C, in a different thread, using a different Session and transaction.
t5: Processing for A searches and retrieves object B.
t6: Processing for C searches and retrieves object B.
t7: Processing for A sets some values on B and updates.
t8: Processing for A commits, job done!
t9: Processing for C sets some values on B and updates: StaleObjectException thrown.

Now what we want to happen is this...

t10: Processing for C re-searches and retrieves object A.
t11: Processing for C sets some values on A and updates.
t12: Processing for C commits, job done!

However, what does happen is this....

t10: Processing for C re-searches, the Session flushes, the update on B is re-executed: StaleObjectException thrown.
t11: User 2 cries.

We need someway of letting Hibernate know not to re-execute that update statement for B after it has failed. Evict obviously isn't enough. As this isn't a JDBC exception, we would like to recover from it and carry on, without having to throw away all the processing we have already done.

Thanks for your time.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 03, 2004 5:08 am 
Proxool Developer
Proxool Developer

Joined: Tue Aug 26, 2003 10:42 am
Posts: 373
Location: Belgium
That's clearer...

So what you have is concurrency problem.
I believe you can solve it with proper transaction isolation level and object locking.

What about an explicit lock on B - using sesion.lock(LockMode.UPDATE) ?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 04, 2004 4:50 am 
Newbie

Joined: Tue Sep 02, 2003 9:47 am
Posts: 8
Thanks for the advice brenuart. Unfortunately, we made the decision a while back that we were not going to use any kind of pessimistic locking in any shape or form. We have also decided we don't want to change that because of this problem.

To be honest, I am thinking this could be a bug in Hibernate. At the end of the day, a StaleObjectException is not a SQLException (which certainly in Postgres tends to implicitly rollback the connection) so it should be recoverable. The idea seems sound that as part of a transaction, the StaleObjectException is caught and processing continues. And in fact, if we do a Session.clear() after the StaleObjectException is thrown, this does solve our problem, the rogue update is removed and we can continue. However this is a bit of a sledgehammer way of doing things, as for starters we need to make sure there has been a flush well beforehand so that we don't lose any other operations.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 04, 2004 6:19 am 
Proxool Developer
Proxool Developer

Joined: Tue Aug 26, 2003 10:42 am
Posts: 373
Location: Belgium
This is not a bug at all. You have two different threads, accessing and modifing the same data. To avoid potential problems, you need a way to synchronize them.

Hibernate is of course not aware of this (how could it be anyway) - so you have to find another way yourself.

Either you use the database as sycnhronisation point (using a lock) or you do it yourself in your own code...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 04, 2004 6:56 am 
Beginner
Beginner

Joined: Tue Sep 30, 2003 10:09 am
Posts: 34
Location: London, UK
brenuart wrote:
This is not a bug at all. You have two different threads, accessing and modifing the same data. To avoid potential problems, you need a way to synchronize them.

Hibernate is of course not aware of this (how could it be anyway) - so you have to find another way yourself.

Either you use the database as sycnhronisation point (using a lock) or you do it yourself in your own code...


Er, no. This is not a synchronization issue. There's one session per thread. Overwrites of data in the database are prevented using Hibernate's optimistic lock implementation.

The problem is that when one thread gets a StaleObjectException, this should be recoverable. It should not be necessary for that thread to rollback and then remake all the changes made up to that point, because there has been no SQLException - there is no problem with the connection or with the transaction. However, because the update statement that causes the StaleObjectException is not removed from the session, the next time the session is flushed, you get the StaleObjectException again. It would be nice if either Hibernate removed the offending statement when the StaleObjectException occurrs, or even for the Session.evict() to remove not only the object from the entity cache, but also from the various statement caches.

To be clear: this is not due to transaction isolation, or proper demarcation of transactions, or synchronization of Java threads.

This is a question of what Hibernate is supposed to do and how it is supposed to be used: if there is an exception, should the session be thrown away? If so, why isn't the session flagged as being unusable? Then every method on it can assert the session is valid before continuing? In any case, if it should be thrown away, why is that necessary when we know that a condition can be recovered from?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 20, 2004 8:18 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
no hibernate exception is recoverable, as per doc


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 17, 2004 4:13 pm 
Beginner
Beginner

Joined: Tue Sep 30, 2003 10:09 am
Posts: 34
Location: London, UK
Chapter 19 says:

Some exceptions are recoverable, for example the StaleObjectStateException and ObjectNotFoundException.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 17, 2004 4:16 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
That would be my mistake. I'll remove it.

_________________
JAVA PERSISTENCE WITH HIBERNATE
http://jpwh.org
Get the book, training, and consulting for your Hibernate team.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 18, 2004 12:52 pm 
Newbie

Joined: Mon Aug 23, 2004 4:33 am
Posts: 4
In one of Bertrand's answers he says:

session.load() will return the version currently held in the session if already loaded - this version may be dirty (ie not in-sync with the database).

I am using optimisitic locking and getting the stale object exception and when this happens I am trying to rerun my routine using the latest object from the database. However, I cannot get Hibernate to reload the object. I've tried evict(), session.load, session.lock but always have the object with the old version number.

I appreciate that things may be dependent on my system setup but is there a standard way to reload dirty objects.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 18, 2004 12:59 pm 
Regular
Regular

Joined: Tue Jan 27, 2004 12:22 pm
Posts: 103
Check the Api of Session. (http://www.hibernate.org/hib_docs/api/)
It names the method: refresh(Object). You would first like to evict the object from the session and then refresh it.

Code:
session.evict(myObj);
session.refresh(myObj);

_________________
Dencel
- The sun has never seen a shadow -


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 17 posts ]  Go to page 1, 2  Next

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.