11 Replies Latest reply on Apr 26, 2012 1:25 PM by dudalov

    How to acquire a lock without possible transaction termination ?

    dudalov

      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 ?
          matlach

          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 ?
            dudalov

            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 ?
              dudalov

              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 ?
                dudalov

                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 ?
                  matlach

                  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 ?
                    dudalov

                    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 ?
                      dudalov

                      Any suggestions?

                       

                      Thanks,

                      Dmitry

                      • 8. Re: How to acquire a lock without possible transaction termination ?
                        galder.zamarreno
                        • 9. Re: How to acquire a lock without possible transaction termination ?
                          an1310

                          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

                          1 of 1 people found this helpful
                          • 10. Re: How to acquire a lock without possible transaction termination ?
                            matlach

                            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 ?
                              dudalov

                              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!