Reputation: 30990
Edit:
It turns out in this case since I was using "version" annotation, so I'm using optimistic locking, not pessimistic locking.
If I remove version and hence disable optimistic locking. The pessimistic locking takes over and performance degraded significantly.
So I guess I have to live with optimistic locking and occasional exceptions. Is there a better solution?
Original:
I currently have multiple tomcat instances in an apache 2.2 load balancer via ajp. The backend system is hibernate. The system serves multiple users and requests, for request it deducts one credit from the user's account.
I use hibernate's pessimistic locking for managing credit deductions.
I kept getting the following from time to time on user account object in the logs:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
Code
private boolean decUserAccountQuota(UserAccount userAccount, int creditDec) {
if(userAccount.getBalance() <1) return false;
String userName = userAccount.getUsername();
Manager manager = getManager(userName);
try{
manager.beginTransaction();
manager.refresh(userAccount, LockMode.UPGRADE); //this uses pessimistic locking, this calls sessionFactory.getCurrentSession().refresh();
userAccount.setBalance(userAccount.getBalance()-creditDec);
manager.commitTransaction(); //this causes the Exception
}catch(Exception exp){
exp.printStackTrace();
manager.rollbackTransaction();
return false;
}finally{
manager.closeSession();
}
return true;
}
Questions:
Upvotes: 1
Views: 2303
Reputation: 1685
In your code the below line is not taking a pessimistic lock, although you are specifying it to, because your database doesnot support SELECT.. FOR UPDATE .
manager.refresh(userAccount, LockMode.UPGRADE);
For this reason hibernate uses an alternate mode of locking called LockMode.READ for userAccount which is optimistic locking based on the @Version attribute in your entity.
I searched to see if there is any alternative way of forcing hibernate to take pessimistic locking for your scenario but it looks like its not possbile.
Coming back to your question of how to minimize or avoid the StaleObjectStateException here are my thoughts:-
Synchronize on userAccount object . Though this seems to affect performance but this would happen only if the same user places too many concurrent requests. Also giving up a little on performance to make sure that user need not be thrown an exception and made to retry would be ideal scenario.
If you have found any alternative solution please do share it .
Upvotes: 0
Reputation: 681
Your Hibernate entity is either using @Version
annotation or defines <version>
in your XML Hibernate mapping. This will enable the optimistic locking provided by Hibernate.
If you are explicitly using pessimistic locking as you described, removing the above should fix your problem.
More info here
Upvotes: 0