11 Replies Latest reply: Apr 26, 2012 1:25 PM by Dmitry Udalov RSS

How to acquire a lock without possible transaction termination ?

Dmitry Udalov Newbie

Let’s say you have similar processes running on different cluster nodes and each process runs within a transactional context. At the end these processes check and update the same replicated cache, but it’s a small part of the entire transaction and "key" is not available in the beginning of the transaction, so the object cannot be locked when transaction starts.

 

tx.begin()

// do some long and hard analysis

 

// and finally update cache and commit

cache.getAdvancedCache().lock(key)      // @throws TimeoutException

cache.put(key, value)

tx.commit()

 

The problem here is that  may lose all your hard work if it happens that more than one process is trying to acquire the same lock. In that case you might get the exception

       org.infinispan.util.concurrent.TimeoutException: Unable to acquire lock after [10 seconds]

and you lose the entire transaction thanks to the InvocationContextInterceptor.markTxForRollbackAndRethrow that set transaction's status to STATUS_MARKED_ROLLBACK.

 

I wonder if there is a way to alter that behavior. I don’t mind to try acquiring the lock more than once. Could it be done by changing interceptor, so that it wouldn't mark the transaction for rollback only? Please advise.

 

Thanks,

Dmitry

  • 1. Re: How to acquire a lock without possible transaction termination ?
    Mathieu Lachance Newbie

    hi dmitry,

     

    in pessimistic mode, when acquiring lock, even if there's no value associated in the cache with the specified key, it will still lock the key you passed on.

    if someone else has already aquired the lock, the lock operation will wait until it has been released either by commit or rollback.

     

    hope this help.

  • 2. Re: How to acquire a lock without possible transaction termination ?
    Dmitry Udalov Newbie

    Hi Mathieu,

     

    Thank you for the prompt response. Let me see if I get it correctly. Are you saying that instead of locking directly the object in the main cache I could:

      1. start transaction on cache A

      2  ....

      3. lock an auxiliary key on another cache B

      4.      only if it succeed lock the object in question in cache A

      5.      some updates in cache A

      6.      commit the main transaction on cache A

      7. unlock auxiliary lock on cache B

     

    In this scenario inability to lock an auxiliary key (step 3) won't damage the main transaction, so the process could decide what to do. Do I get it right? But what about performance? Any significant impact on it since it takes additional lock/unlock operations?

     

    Thanks,

    Dmitry

  • 3. Re: How to acquire a lock without possible transaction termination ?
    Dmitry Udalov Newbie

    BTW, it was pessimistic mode where I saw that TimeoutException, so the outcome is not only "... the lock operation will wait until it has been released either by commit or rollback ...".

    The operation might also fail with an exception which would mark the transaction as "rollback only" as a side effect.

  • 4. Re: How to acquire a lock without possible transaction termination ?
    Dmitry Udalov Newbie

    Obviously I got it wrong. Embedded transactions are not supported.

    So I'm back to my original question: how can I safely try if an object could be locked in the cache without breaking the existing transaction.

  • 5. Re: How to acquire a lock without possible transaction termination ?
    Mathieu Lachance Newbie

    I don't think there's such feature like ReentrantLock::tryLock()

     

    see actual LockManager documentation :

    http://docs.jboss.org/infinispan/5.1/apidocs/org/infinispan/util/concurrent/locks/LockManager.html

     

    tough, when lock acquisition fail, it do not break transaction at all, just catch the TimeoutException.

     

    finally, maybe you should also have a look at how to manage user transaction :

    https://community.jboss.org/wiki/WhatIsTheCorrectPatternForUserTransactions

  • 6. Re: How to acquire a lock without possible transaction termination ?
    Dmitry Udalov Newbie

    Thanks for the links. As for re-trying, unfortunately it's not possible because transaction's state is set to rollback only. That's the point. I tried to catch the TimeoutException and re-submit lock acquisition request one more time but it failed immediately with IllegalStateException. Debugging showed that the state of the transaction was changed to to STATUS_MARKED_ROLLBACK by

          InvocationContextInterceptor.markTxForRollbackAndRethrow    called from line 144 (infinispan-5.1.2.FINAL)

     

    Here's the complete stack traces

     

    org.infinispan.util.concurrent.TimeoutException: Unable to acquire lock after [120 seconds] on key [737IMzDYBABCAAWmEkTPAZg==] for requestor [GlobalTransaction:<Sound-57047>:1:local]! Lock held by [null]

        at org.infinispan.util.concurrent.locks.LockManagerImpl.lock(LockManagerImpl.java:206)

        at org.infinispan.util.concurrent.locks.LockManagerImpl.acquireLock(LockManagerImpl.java:180)

        at org.infinispan.util.concurrent.locks.LockManagerImpl.acquireLock(LockManagerImpl.java:170)

        at org.infinispan.interceptors.locking.AbstractTxLockingInterceptor.lockKeyAndCheckOwnership(AbstractTxLockingInterceptor.java:209)

        at org.infinispan.interceptors.locking.AbstractTxLockingInterceptor.lockAndRegisterBackupLock(AbstractTxLockingInterceptor.java:136)

        at org.infinispan.interceptors.locking.PessimisticLockingInterceptor.visitLockControlCommand(PessimisticLockingInterceptor.java:228)

        at org.infinispan.commands.control.LockControlCommand.acceptVisitor(LockControlCommand.java:129)

        at org.infinispan.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:116)

        at org.infinispan.interceptors.base.CommandInterceptor.handleDefault(CommandInterceptor.java:130)

        at org.infinispan.commands.AbstractVisitor.visitLockControlCommand(AbstractVisitor.java:159)

        at org.infinispan.commands.control.LockControlCommand.acceptVisitor(LockControlCommand.java:129)

        at org.infinispan.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:116)

        at org.infinispan.interceptors.TxInterceptor.visitLockControlCommand(TxInterceptor.java:144)

        at org.infinispan.commands.control.LockControlCommand.acceptVisitor(LockControlCommand.java:129)

        at org.infinispan.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:116)

        at org.infinispan.interceptors.StateTransferLockInterceptor.handleWithRetries(StateTransferLockInterceptor.java:207)

        at org.infinispan.interceptors.StateTransferLockInterceptor.visitLockControlCommand(StateTransferLockInterceptor.java:138)

        at org.infinispan.commands.control.LockControlCommand.acceptVisitor(LockControlCommand.java:129)

        at org.infinispan.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:116)

        at org.infinispan.interceptors.base.CommandInterceptor.handleDefault(CommandInterceptor.java:130)

        at org.infinispan.commands.AbstractVisitor.visitLockControlCommand(AbstractVisitor.java:159)

        at org.infinispan.commands.control.LockControlCommand.acceptVisitor(LockControlCommand.java:129)

        at org.infinispan.interceptors.base.CommandInterceptor.invokeNextInterceptor(CommandInterceptor.java:116)

        at org.infinispan.interceptors.InvocationContextInterceptor.handleAll(InvocationContextInterceptor.java:130)       <<<<<<<<<<<<<<<<< falls back here and changes transaction status

        at org.infinispan.interceptors.InvocationContextInterceptor.visitLockControlCommand(InvocationContextInterceptor.java:94)

        at org.infinispan.commands.control.LockControlCommand.acceptVisitor(LockControlCommand.java:129)

        at org.infinispan.interceptors.InterceptorChain.invoke(InterceptorChain.java:345)

        at org.infinispan.CacheImpl.lock(CacheImpl.java:484)

        at org.infinispan.CacheImpl.lock(CacheImpl.java:468)

        .....

     

    And it's easy reproducible with pessimistic transactional replicated cache. Just have two or more Threads running the same task which execution time exceeds lockAcquisitionTimeout

  • 7. Re: How to acquire a lock without possible transaction termination ?
    Dmitry Udalov Newbie

    Any suggestions?

     

    Thanks,

    Dmitry

  • 9. Re: How to acquire a lock without possible transaction termination ?
    Erik Salter Newbie

    Hi Dimitry,

     

    I've done a TryLock-esque feature like the following:

     

     

    public boolean tryLock(K key) {
    
    
          boolean success = cache.getAdvancedCache()
                .withFlags(Flag.FAIL_SILENTLY).lock(key);
    
    
          return success;
    }
    

     

    The FAIL_SILENTY flag keeps the transaction from rolling back, and the lock API was enhanced to return a boolean value (in case of this value) back in the 4.2 timeframe.

     

    Hope this helps,

     

    Erik

  • 10. Re: How to acquire a lock without possible transaction termination ?
    Mathieu Lachance Newbie

    appears to me a nice trick erik,

    i'll definitely try it.

     

    do you think this is viable galder ?

  • 11. Re: How to acquire a lock without possible transaction termination ?
    Dmitry Udalov Newbie

    Thank you, Erik! That works like a charm.

    I tried modified unit test attached to the https://issues.jboss.org/browse/ISPN-2013 and now it showed no problems when executed with .withFlags(Flag.FAIL_SILENTLY). Woot!