12 Replies Latest reply on Sep 25, 2009 4:26 PM by alllle

    how to lock a node for update?

    alllle

      I just started using JBoss Cache and went through the doc and searched the forum but didn't find the answer for my question. I am using JBossCache 3.2.0.GA.

      I need to perform the following operation frequently in a clustered caching environment:

       Node node = cache.getChild(Fqn.fromString("app/sequence/msgId"));
       Integer currentId = (Integer)node.get("id");
       Integer nextId = new Integer(currentId.intValue()+1);
       node.put("id", nextId);
      


      Basically, a read followed by a write that increment the value. I'd like to know what's the best way to approach this.

      If I don't use any locking, the node.put() will, according to the doc, fast fail if another thread on the same JBoss node or the clustered node updates the value, in which case I need to capture exception and repeat this operation and might suffer the starvation problem.

      Alternatively, I think I can use LockManager.lock() to proactively lock the node before read / update it. The starvation problem may still occur, but if I obtain the lock, I know I won't get an exception for writing later one. But I didn't find any useful document on the use of the LockManager and therefore, I am not sure if this approach is recommended or not.

      Furthermore, I am not sure how my setting of the isolationLevel attribute (either "READ_COMMITTED" or "REPEATABLE_READ") affects the behavior in the above two scenarios.

      Can someone please share your thoughts on this?

      Thank you,

        • 1. Re: how to lock a node for update?
          alllle

          Looks like the LockManager.lock() is not directly accessible from client code, as I didn't find any public API that retrieves the LockManager instance.

          So is there anyway that allows me to obtain a write lock before reading a node?

          Thanks,

          • 2. Re: how to lock a node for update?
            alllle

            I read a post in this forum showing that with READ_COMMITTED, the following may occur:

            1. tx1 and tx2 both read node value 10
            2. tx1 set the value to (value+1), which is 11
            3. tx2 set the value to (value+1), which is also 11, while the desired behavior is 12, and there is no exception thrown since write skew is not checked

            I couldn't figure out a reliable way of incrementing a shared value without possible starvation. I would think this is a common use case, but I couldn't find a solution with the provided JBC API.

            Any help is appreciated,

            • 3. Re: how to lock a node for update?

              Have you trying using the pessimistic locking scheme?

              Try adding the following to your replSync-service.xml:

              <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
               <attribute name="SyncCommitPhase">true</attribute>
               <attribute name="SyncRollbackPhase">true</attribute>
               <attribute name="IsolationLevel">SERIALIZABLE</attribute>


              It is the only way i have found to have "select for update" semantics (blocking reads), and it is deprecated.. See my post on a comparable topic: http://www.jboss.org/index.html? module=bb&op=viewtopic&p=4254357#4254357

              I have no clue how to achieve the same thing with MVCC (or even if it is possible at all)

              • 4. Re: how to lock a node for update?

                Have you trying using the pessimistic locking scheme?

                Try adding the following to your replSync-service.xml:

                <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
                 <attribute name="SyncCommitPhase">true</attribute>
                 <attribute name="SyncRollbackPhase">true</attribute>
                 <attribute name="IsolationLevel">SERIALIZABLE</attribute>


                It is the only way i have found to have "select for update" semantics (blocking reads), and it is deprecated.. See my post on a comparable topic: http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4254357#4254357

                I have no clue how to achieve the same thing with MVCC, or even if it is at all possible.

                • 5. Re: how to lock a node for update?
                  alllle

                  Thanks for the reply.

                  I have the same concern regarding the deprecated pessimistic locking. The JBC documents keeps talking about the benefit of mvcc, but failed to address this common use case.

                  Note that the concurrent write is not really an exception in this scenario, it is expected to happen frequently, and need to be handled in a way that guarantees the continuation of the application logic (i.e., no starvation, the write will be executed in a finite time without hurting data integrity).

                  Anyone else having the same issue? Any other ideas?

                  • 6. Re: how to lock a node for update?

                     

                    "alllle" wrote:

                    I have the same concern regarding the deprecated pessimistic locking. The JBC documents keeps talking about the benefit of mvcc, but failed to address this common use case.

                    Support for it is even completely discontinued in the next version (Infinispan). This is a big issue for me as what we need is isolation semantics that work like java locks.

                    • 7. Re: how to lock a node for update?
                      manik

                      You can force write locks for a read. There is an Option for this (see the Options API). When used with MVCC, this gives you an exclusive (write) lock, when when you are reading. And when combined with a transaction, this means you hold the write lock until the tx completes.

                      • 8. Re: how to lock a node for update?
                        alllle

                         

                        "manik.surtani@jboss.com" wrote:
                        You can force write locks for a read. There is an Option for this (see the Options API). When used with MVCC, this gives you an exclusive (write) lock, when when you are reading. And when combined with a transaction, this means you hold the write lock until the tx completes.


                        Thanks for the help.

                        I assume you are referring to some thing like this (from the doc):
                         // first start a transaction
                         cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true);
                         Node n = cache.getNode(Fqn.fromString("/a/b/c"));
                         // make changes to the node
                         // commit transaction
                        


                        I wonder if you can explain it in a little more detail. I need some clarification to my confusions:
                        - when is the lock obtained? is it, like the write lock, at the time when the transaction commits or it is at the time when the read happens?
                        - What is considered as a "read"? I mean, does cache.getNode() considered a "read", or only when I do a subsequent node.get("key") is when the lock obtained?
                        - With "REPEATABLE_READ" isolation, is the "write skew" still a problem? - I think it's not but I'd like to get a confirmation from you.
                        - What is the scope of the current "InvocationContext"? Is it within the current transaction, or current thread, or something else?



                        • 9. Re: how to lock a node for update?

                          allle i might have a use case a bit like yours, look at my isolation unit test in the other thread http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4254357#4254357.

                          If i use

                          cache.getCache().getConfiguration().setNodeLockingScheme(NodeLockingScheme.PESSIMISTIC);
                          , the test passes.

                          If i use
                          cache.getCache().getInvocationContext().getOptionOverrides().setForceWriteLock(true);
                          instead, the test fails.

                          If i use
                          cache.getCache().getInvocationContext().getOptionOverrides().setForceWriteLock(true);
                          cache.getCache().getConfiguration().setWriteSkewCheck(true);
                          , the test fails and JBC detects writeSkews. But what i want is not the read thread to fail, but to block until the write thread is over.

                          • 10. Re: how to lock a node for update?
                            manik

                             

                            "alllle" wrote:

                            I assume you are referring to some thing like this (from the doc):
                            // SNIP

                            Yes.

                            "alllle" wrote:

                            I wonder if you can explain it in a little more detail. I need some clarification to my confusions:
                            - when is the lock obtained? is it, like the write lock, at the time when the transaction commits or it is at the time when the read happens?

                            When the read happens.

                            "alllle" wrote:

                            - What is considered as a "read"? I mean, does cache.getNode() considered a "read", or only when I do a subsequent node.get("key") is when the lock obtained?

                            cache.getNode() is a read.

                            "alllle" wrote:

                            - With "REPEATABLE_READ" isolation, is the "write skew" still a problem? - I think it's not but I'd like to get a confirmation from you.

                            Write skews are *only* a problem with R_R. See the discussion in the user guide for details.

                            "alllle" wrote:

                            - What is the scope of the current "InvocationContext"? Is it within the current transaction, or current thread, or something else?

                            Invocation contexts last for, well, an invocation! :-)



                            • 11. Re: how to lock a node for update?
                              alllle

                              Yes, I read that thread and yes, the use case is similar. We want a easy way to block for write operation rather capturing exceptions. The thing is that I don't want to use pessimistic locking from your test code there as it is deprecated now.

                              • 12. Re: how to lock a node for update?
                                alllle

                                Thanks for the reply, I just realized that you answered the questions before my last post :)

                                I am still not clear with two of the answers and wonder if you can help:

                                "manik.surtani@jboss.com" wrote:

                                "alllle" wrote:

                                - With "REPEATABLE_READ" isolation, is the "write skew" still a problem? - I think it's not but I'd like to get a confirmation from you.

                                Write skews are *only* a problem with R_R. See the discussion in the user guide for details.

                                So if I have isolation set to R_R in the config file, when I call the
                                cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true);
                                
                                Is the isolation for the current invocation context still the R_R, or is it changed to some thing else? In other words, do I still need to worry about write skew?

                                "manik.surtani@jboss.com" wrote:
                                "alllle" wrote:

                                - What is the scope of the current "InvocationContext"? Is it within the current transaction, or current thread, or something else?

                                Invocation contexts last for, well, an invocation! :-)

                                Could you explain more on what an "invocation" is then? This answer, I have to say, doesn't really help :)
                                What I'd like to know is the impact of the call. Should I remove the option override after the transaction? Does other thread who is accessing the same cache affected by this call? etc.


                                Thanks,