5 Replies Latest reply on Jun 19, 2009 7:06 AM by mircea.markus

    Manual node locking?

      So I suspect this is somewhat of an odd request...

      I'm trying to implement OpenFire's clustering plugin using JBossCache. I can implement their cache interface pretty easily, save for one method - getLock.

      So OpenFire wants to manually acquire and release locks on cached objects. Beyond that, OpenFire _is_ the server- it's not a J2EE app- so there's no TransactionManager to be found.

      So is there any way I can directly access JBossCache's locking APIs? (even if it requires accessing non-public implementation classes)

      I'm trying to think of a way I could "manually" synchronize on an object via the cache - i.e. perform a "put," and if the operation does not return a "previous value" assume I have the lock. But I'm not sure that's entirely safe either. Any ideas on how I could accomplish such a thing?

      Thanks.
      -Tom

        • 1. Re: Manual node locking?
          mircea.markus

          Look at the Option API, namely at setForceWriteLock method. This will acquire a WL even if you're doing a read. Re:transaction, you can either use the one JBossCache supplies (DummyTransactionManager) or try integrating with another one, e.g. JBossTM.

          • 2. Re: Manual node locking?

            Interesting, thanks Markus.

            So in order to use this I would need to
            (1) begin a transaction
            (2) write to, say, node '/a'
            (2a) any other client that tries to read '/a' will block
            (3) commit transaction
            (3a) clients reading '/a' will be notified and continue

            Does that sound accurate?

            But the DummyTransactionManager says it synchronizes in-memory only, so cache clients on two different machines wouldn't be sync'd. Or are the node locking calls still made by the cache client in order to sync between servers?

            Thanks.

            • 3. Re: Manual node locking?
              mircea.markus

               

              Does that sound accurate?

              yes, that's the way things happen.

              But the DummyTransactionManager says it synchronizes in-memory only, so cache clients on two different machines wouldn't be sync'd.

              what that means is that locks will be acquired on the node on which the tx was initiated only. If you have n1 and n2, two nodes in an replicated cluster,
              and an transaction having a write lock (WL) on fqn_1 on n1, then same fqn_1 is not locked on n2. In other words, transactions only aquire local locks. Locks are acquired on n2 only at commit time: at this point all operations performed on n1 will be executed in same sequence on n2 (including lock acquisition).

              If you need eager remote locking (i.e. acquire locks on all nodes of a cluster at write time) I suggest you to take a look at infinispan: http://www.jboss.org/infinispan

              • 4. Re: Manual node locking?

                 

                "mircea.markus" wrote:

                But the DummyTransactionManager says it synchronizes in-memory only, so cache clients on two different machines wouldn't be sync'd.

                what that means is that locks will be acquired on the node on which the tx was initiated only. If you have n1 and n2, two nodes in an replicated cluster,
                and an transaction having a write lock (WL) on fqn_1 on n1, then same fqn_1 is not locked on n2. In other words, transactions only aquire local locks. Locks are acquired on n2 only at commit time: at this point all operations performed on n1 will be executed in same sequence on n2 (including lock acquisition).


                Unfortunately that doesn't sound like it will work for me... The locking API assumes that once you pass the lock() call, you have exclusive access to those resources... So that's very different from attempting to commit changes on implicitly shared resources.

                "mircea.markus" wrote:

                If you need eager remote locking (i.e. acquire locks on all nodes of a cluster at write time) I suggest you to take a look at infinispan: http://www.jboss.org/infinispan


                A grid computing solution? That sounds like a bit overkill... Could you point to an example of how this would work?

                I'm going to try my put() method, which would work something like this:

                To lock object O:
                1. val = cache.put( '/locks', O, 'lock' )
                2a. If val == null, we have the lock (no previous 'put')
                2b. Else (val != null ) somebody else locked it first. Use a cache listener to wait for removed nodes
                3a. Lock owner in (2a) proceeds. When finished, call cache.remove( '/locks', O )
                3b. Contenders from (2b) get notified and return to (1)

                Does that sound like it will work? Since it's a put, the operations will acquire a write lock, and subsequently (I assume) guarantees that two concurrent puts could no both return 'null' for the previous value.

                What do you think? My other option would be to fall back to ZooKeeper, since I know that works :) But I really don't want to use 3 different frameworks in order to accomplish this :)

                Thanks.

                • 5. Re: Manual node locking?
                  mircea.markus

                   

                  To lock object O:
                  1. val = cache.put( '/locks', O, 'lock' )
                  2a. If val == null, we have the lock (no previous 'put')
                  2b. Else (val != null ) somebody else locked it first. Use a cache listener to wait for removed nodes
                  3a. Lock owner in (2a) proceeds. When finished, call cache.remove( '/locks', O )
                  3b. Contenders from (2b) get notified and return to (1)

                  You have to use tx in order to keep the lock from step1 to step 3. Otherwise the lock will be released when cache.put returns. Here is a way for doing this:

                  //1 use the tx manager that comes with cache;line bellow can also be specified through configuration, in transaction element, transactionManagerLookupClass attribute
                  configuration.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
                  
                  //2. obtain the TransactionManager
                  TransactionManager tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager();
                  
                  //3. start a transaction
                   tm.begin();
                  
                  //4. make sure that the node will be locked even if you only read it. After this call you are ensured to be the only one having access to the node
                  cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true);
                  Object o = cache.get(fqn, "k");
                  
                  //5. do whatever you need, the locks are still held here
                  ....
                  
                  //locks are released here, other trying to read same fqn will be allowed to
                  6.tm.release();