-->
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.  [ 11 posts ] 
Author Message
 Post subject: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Fri Feb 07, 2014 10:50 am 
Newbie

Joined: Fri Feb 07, 2014 9:59 am
Posts: 7
We have a situation where we need to increment the version of an entity when the state of its related entities changes. To do this, we are calling EntityManager#refresh(entity, LockModeType.PESSIMISTIC_FORCE_INCREMENT). We chose to use a pessimistic lock because we need to guarantee that the version is incremented and we want to avoid the transaction rollback that would occur if we used an optimistic lock and it failed. In our testing with MySQL, this approach worked as expected, but when we switched to Oracle, we starting seeing java.sql.SQLException: ORA-00054: resource busy and acquire with NOWAIT specified. Tracing into the hibernate Dialect class, we see the following:

Code:
   private String getForUpdateString(LockMode lockMode, int timeout){
       switch ( lockMode ) {
            case UPGRADE:
                return getForUpdateString();
            case PESSIMISTIC_READ:
                return getReadLockString( timeout );
            case PESSIMISTIC_WRITE:
                return getWriteLockString( timeout );
            case UPGRADE_NOWAIT:
            case FORCE:
            case PESSIMISTIC_FORCE_INCREMENT:
                return getForUpdateNowaitString();
            default:
                return "";
        }
    }

This explains the exception - MySQL does not support NOWAIT, so the locking works as we expect but Oracle supports NOWAIT and fails immediately if the lock is already held. The question is, why does the Dialect base class specifically choose to use NOWAIT functionality for PESSIMISTIC_FORCE_INCREMENT? It seems like there must have been a reason when the code was written…

Taking a step back and reevaluating our approach, we're also interested to know if there's a better way to achieve the behavior we need - modifications to a relationship or its members increments the version of the parent. OPTIMISTIC_FORCE_INCREMENT would be perfect - we just want to ensure the version is incremented, even if another thread does it - except for the fact that it throws OptimisticLockException which rolls back our whole transaction!

Right now, we're thinking of extending the Oracle dialect and disabling NOWAIT since we don't want that behavior anyway. It would be nice if the behavior of the getForUpdateString() method in the Dialect base class was configurable. I suppose we could also use a native query to increment the version without locking.

Any suggestions are greatly appreciated!


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Sat Feb 15, 2014 11:41 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
First, have you tried lock(entity, LockModeType.PESSIMISTIC_FORCE_INCREMENT) rather than refresh(..)? lock() w/ PESSIMISTIC_FORCE_INCREMENT is handled quite differently.

As for the code in question, the general thinking is that if you are requesting a "force/increment" lock you are (1) trying to make sure the entity is not currently in the process of being updated by another transaction and (2) that future transactions will not be able to update it until your transaction is done. Using the force-increment option adds the additional requirement that we (3) update the optimistic lock attribute ensure that other transactions also fail optimistically-speaking.

Failing to acquire a write lock (the general term for an UPGRADE lock) generally indicates that the row is already locked. The nature of this pre-existing lock which would cause our lock attempt to block/fail is quite database and isolation dependent. So as a baseline, we make an assumption... that a held read lock should not block a attempt to acquire a write lock. Given that assumption, the fact that our lock attempt does block/fail we assume that means the row is already locked for write, which means we have already failed the 1st check above.

Now some databases do implement "consistent reads" style isolation-levels by having readers block writers (the idea being that the data cannot change if we do not allow the writer to proceed). I thought Oracle uses MVCC style locking, which should mean that our assumption is correct there; an upgraded lock attempt that blocks should indicate that the row is already locked for write. But I have not been a day-to-day user of Oracle since 8i/9i days so maybe that is not the case.


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Tue Feb 18, 2014 11:23 am 
Newbie

Joined: Fri Feb 07, 2014 9:59 am
Posts: 7
I'm pretty sure I tried lock() first, but I ran into a different issue with the entity not being loaded yet, hence the use of refresh(). I'll revisit that option and see. Regardless, I think I understand your reasoning - you want a fail-fast behavior based on your assumption of the client's scenario. I can see how this makes sense in a normal locking scenario, but in my case all I want is to guarantee that the version is incremented. I don't care if another thread modifies the parent, but I need to make sure that a future reader knows that the version has changed. I suppose this is kind of an abuse of JPA locking since I'm not really trying to lock the parent, but I don't see a better way to do it without managing the version myself.

My real concern with Hibernate is that this code path is very database-dependent (Oracle-specific?) since any database that doesn't support 'no wait' will do what I expect. I think from a Hibernate standpoint, this should be a configurable optimization for databases that support it (e.g. - useNoWaitForPessimisticForceIncrement) and the default should be consistent with normal pessimistic lock acquisition.


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Thu Feb 20, 2014 11:34 am 
Newbie

Joined: Fri Feb 07, 2014 9:59 am
Posts: 7
To clarify the reason for using refresh() instead of lock(), section 3.4.4.2 of the JPA 2.0 spec states:
Quote:
When lock(entity, LockModeType.PESSIMISTIC_READ), lock(entity, Lock- ModeType.PESSIMISTIC_WRITE), or lock(entity, LockModeType.PESSIMISTIC_- FORCE_INCREMENT)is invoked on a versioned entity that is already in the persistence context, the provider must also perform optimistic version checks when obtaining the lock. An OptimisticLockException must be thrown if the version checks fail. Depending on the implementation strategy used by the provider, it is possible that this exception may not be thrown until flush is called or commit time, whichever occurs first.
So, if we don't use refresh(), we run the risk of a transaction rollback due to an optimistic lock failure.

Right now, I'm thinking the only way to avoid the 'no wait' issue in Oracle is to obtain a pessimistic read lock on the parent before we update the child, then call lock() with PESSIMISTIC_FORCE_INCREMENT on the parent if we determine that its version needs incrementing. This seems more expensive from a locking standpoint because we'll have to always lock the parent row even if we don't end up needing to increment its version.


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Thu Feb 20, 2014 11:37 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
emattheis wrote:
I think from a Hibernate standpoint, this should be a configurable optimization for databases that support it (e.g. - useNoWaitForPessimisticForceIncrement) and the default should be consistent with normal pessimistic lock acquisition.


Well in fact you can achieve that: plug in your own Dialect. They are an extension point for just these kinds of cases.


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Thu Feb 20, 2014 11:47 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
emattheis wrote:
I suppose this is kind of an abuse of JPA locking since I'm not really trying to lock the parent, but I don't see a better way to do it without managing the version myself.
...
Right now, I'm thinking the only way to avoid the 'no wait' issue in Oracle is to obtain a pessimistic read lock on the parent before we update the child, then call lock() with PESSIMISTIC_FORCE_INCREMENT on the parent if we determine that its version needs incrementing. This seems more expensive from a locking standpoint because we'll have to always lock the parent row even if we don't end up needing to increment its version.


Exactly! You are trying to synchronize access to the children via a shared locking mechanism on the parent. This works on an assumption where all transactions agree to these steps ("updating X relies on a lock on Y being acquired first"). I would not say it is a "misuse" of the API, but you need to understand the difference and the ramifications. The typical approach to this is exactly what you outlined:
1) acquire the lock on some agreed "root" (parent)
2) update stuff covered by the lock


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Thu Feb 20, 2014 12:15 pm 
Newbie

Joined: Fri Feb 07, 2014 9:59 am
Posts: 7
steve wrote:
You are trying to synchronize access to the children via a shared locking mechanism on the parent. This works on an assumption where all transactions agree to these steps ("updating X relies on a lock on Y being acquired first"). I would not say it is a "misuse" of the API, but you need to understand the difference and the ramifications.
This is not precisely what we're trying to do. We want to synchronize access to the children independently based on their individual locking mechanisms, and only increment the version of the parent if certain aspects of the children change to indicate to caches that a newer copy exists. Basically, the cache has a representation like this:
Code:
<parent>
  <children>
    <child name="a" />
    <child name="b" />
    <child name="c" />
    <child name="d" />
  </children>
</parent>
So, if children are added or removed, or a child's name changes, we must invalidate the cache, but if some other state of a child changes, the cache is still valid. So, it is an optimization to avoid locking the parent until we determine that we must due to the nature of the change to the child.
steve wrote:
Well in fact you can achieve that: plug in your own Dialect. They are an extension point for just these kinds of cases.
Indeed a custom dialect was where we were heading before starting this thread. I just wanted to understand why Hibernate does what it does before changing the behavior. Personally, I think Hibernate should adopt a more uniform approach to pessimistic locking since the most common behavior across database vendors is to block until the lock can be acquired. If this part of Hibernate was configurable, it would be a moot point and we could avoid deploying a custom dialect and tying our deployment artifacts to a particular RDBMS.


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Thu Feb 20, 2014 12:59 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
emattheis wrote:
Indeed a custom dialect was where we were heading before starting this thread. I just wanted to understand why Hibernate does what it does before changing the behavior. Personally, I think Hibernate should adopt a more uniform approach to pessimistic locking since the most common behavior across database vendors is to block until the lock can be acquired. If this part of Hibernate was configurable, it would be a moot point and we could avoid deploying a custom dialect and tying our deployment artifacts to a particular RDBMS.


I am not going to change this in Hibernate. I think you'd have to agree that refresh + FORCE is at best an edge case, especially considering you do not follow a consistent access order.

Anyway, "deploying a custom dialect" in no way means "tying our deployment artifacts to a particular RDBMS". You'd just hook your custom Dialect into the Dialect-resolution strategy in Hibernate.


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Thu Feb 20, 2014 4:43 pm 
Newbie

Joined: Fri Feb 07, 2014 9:59 am
Posts: 7
steve wrote:
I am not going to change this in Hibernate. I think you'd have to agree that refresh + FORCE is at best an edge case, especially considering you do not follow a consistent access order.
Fair enough.
steve wrote:
Anyway, "deploying a custom dialect" in no way means "tying our deployment artifacts to a particular RDBMS". You'd just hook your custom Dialect into the Dialect-resolution strategy in Hibernate.
I was under the impression that I would have to explicitly define the dialect class in my persistence.xml properties. If I can tell Hibernate to use my custom dialect only when it detects Oracle, then that's much better. Can you point me to the relevant documentation?


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Thu Feb 20, 2014 5:50 pm 
Newbie

Joined: Fri Feb 07, 2014 9:59 am
Posts: 7
Found the documentation here:

<http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch07.html#services-DialectResolver>

I extended the Oracle10g dialect like so:
Code:
public class Oracle10gDisabledNoWaitDialect extends Oracle10gDialect {
   public String getForUpdateNowaitString() {
      return super.getForUpdateString();
   }
   public String getForUpdateNowaitString(String aliases) {
      return super.getForUpdateString(aliases);
   }
}
and implemented a resolver like so:
Code:
public class OracleDisabledNoWaitDialectResolver extends AbstractDialectResolver {
   protected Dialect resolveDialectInternal(DatabaseMetaData metaData) throws SQLException {
      String databaseName = metaData.getDatabaseProductName();
      int databaseMajorVersion = metaData.getDatabaseMajorVersion();
      if ("Oracle".equals(databaseName)) {
         switch (databaseMajorVersion) {
            case 11:
            case 10:
               return new Oracle10gDisabledNoWaitDialect();
         }
      }
      return null;
   }
}

then I set the
Code:
hibernate.dialect_resolvers
property to the fully-qualified name of my resolver and it seems to be invoking my resolver logic. Now to go and test the dialect in Oracle...

Thanks for the information!


Top
 Profile  
 
 Post subject: Re: PESSIMISTIC_FORCE_INCREMENT does not support timeout
PostPosted: Fri Feb 21, 2014 2:10 pm 
Newbie

Joined: Fri Feb 07, 2014 9:59 am
Posts: 7
Just to followup, the custom dialect solution does indeed solve our problem in Oracle. Thanks again for the assistance.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 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.