3 Replies Latest reply on Oct 25, 2004 5:30 AM by belaban

    putting, removing, and rolling back in the same transaction

    visionlink

      i'm using JBCv1.1 for the hibernate secondary-cache implementation. i'm getting a catastrophic exception when a rollback occurs in certain situations. it is catastrophic because locks are left locked and orphaned and the tree is generally in an inconsistent state.

      the problem is triggered by putting an Fqn, removing the same Fqn, removing the same Fqn again, then rolling back the transaction. granted, that's sort of a goofy access pattern, however, no *reasonable* access pattern should leave the tree in an inconsistent state such as this.

      the following simple program should be sufficient to cause the problem (i hope):

      import org.jboss.cache.TreeCache;
      import org.jboss.cache.DummyTransactionManagerLookup;
      import org.jboss.cache.transaction.DummyUserTransaction;
      import org.jboss.cache.transaction.DummyTransactionManager;
      import javax.transaction.UserTransaction;
      
      /**
       *
       */
      public class foo
      {
       public static void main(String[] argv)
       throws Exception
       {
       TreeCache tc;
       UserTransaction tx;
      
       tc = new TreeCache();
       tc.setTransactionManagerLookup(new DummyTransactionManagerLookup());
       tc.start();
       tx = new DummyUserTransaction(DummyTransactionManager.getInstance());
       tx.begin();
       tc.put("/foo/1", "item", new Integer(1));
       tc.remove("/foo/1");
       tc.remove("/foo/1");
       tx.rollback();
       }
      }


      the exception that you'll (hopefully) see is:
      java.util.ConcurrentModificationException
       at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:552)
       at java.util.LinkedList$ListItr.previous(LinkedList.java:508)
       at org.jboss.cache.TreeCache.rollback(TreeCache.java:2859)
       at org.jboss.cache.interceptors.TransactionInterceptor$SynchronizationHandler.afterCompletion(TransactionInterceptor.java:111)
       at org.jboss.cache.interceptors.OrderedSynchronizationHandler.afterCompletion(OrderedSynchronizationHandler.java:79)
       at org.jboss.cache.transaction.DummyTransaction.notifyAfterCompletion(DummyTransaction.java:240)
       at org.jboss.cache.transaction.DummyTransaction.rollback(DummyTransaction.java:82)
       at org.jboss.cache.transaction.DummyTransactionManager.rollback(DummyTransactionManager.java:137)
       at org.jboss.cache.transaction.DummyUserTransaction.rollback(DummyUserTransaction.java:80)
       at foo.main(foo.java:26)


      thanks, and let me know if there are any other details that you need to be able to debug this.

        • 1. Re: putting, removing, and rolling back in the same transact
          visionlink

          this appears to occur because /foo/1 is listed in TransactionEntry.nodes twice.

          when TreeCache.rollback processes the second /foo/1 node in TransactionEntry.nodes, the call to findNode in _remove (re)creates the node for /foo/1 and adds it to TransactionEntry.nodes (so now there are three in there). since TreeCache.rollback is currently iterating the nodes list, a ConcurrentModificationException is thrown.

          if i read the comments for TransactionEntry.nodes and TreeCache.rollback correctly, then "nodes" amounts to an order set of Fqn's representing nodes created by this transaction, not a simple list. granted, this might be naive, but i solved the problem by adding a set along side of TransactionEntry.nodes to track the nodes and only add them to the *list* if they're not already in the set. i'd prefer to use LinkedHashSet for this, but i believe JBCv1.1/JBv3.2.x still requires J2SEv1.3. you could also implement this with a call to nodes.indexOf, but using the Set seems a bit more efficient.

          here is a patch:

          Index: TransactionEntry.java
          ===================================================================
          RCS file: /cvsroot/jboss/jboss-cache/src/main/org/jboss/cache/TransactionEntry.java,v
          retrieving revision 1.10.2.5
          diff -u -c -r1.10.2.5 TransactionEntry.java
          *** TransactionEntry.java 23 Sep 2004 18:42:24 -0000 1.10.2.5
          --- TransactionEntry.java 16 Oct 2004 01:01:28 -0000
          ***************
          *** 11,19 ****
          --- 11,21 ----
           import org.jgroups.blocks.MethodCall;
          
           import javax.transaction.Transaction;
          + import java.util.HashSet;
           import java.util.Iterator;
           import java.util.LinkedList;
           import java.util.List;
          + import java.util.Set;
          
          
           /**
          ***************
          *** 57,63 ****
           * List of nodes created. Elements are fully qualified names (Fqns)
           */
           protected List nodes=new LinkedList();
          !
           /**
           * List of locks acquired by the transaction ({@link IdentityLock})
           */
          --- 59,66 ----
           * List of nodes created. Elements are fully qualified names (Fqns)
           */
           protected List nodes=new LinkedList();
          ! protected Set nodeSet = new HashSet();
          !
           /**
           * List of locks acquired by the transaction ({@link IdentityLock})
           */
          ***************
          *** 86,92 ****
           }
          
           public void addNode(Fqn fqn) {
          ! if(fqn != null)
           nodes.add(fqn);
           }
          
          --- 89,95 ----
           }
          
           public void addNode(Fqn fqn) {
          ! if(fqn != null && nodeSet.add(fqn))
           nodes.add(fqn);
           }
          
          
          


          • 2. Re: putting, removing, and rolling back in the same transact

            Looks like this is a bug. Is it possible you can create a bug report in sourceforge and assign it to me for now?

            Thanks,

            -Ben

            • 3. Re: putting, removing, and rolling back in the same transact
              belaban

              I added a test case to TxUnitTestCase (see below), but could not reproduce the problem.

              However, because this *is* a bug, I fixed it by making sure that the _removeXXX() methods do *not* create parent nodes if not existent.
              Can you verify this works (I committed this to CVS head).


               public void testDoubleRemovalOfSameData() {
               try {
               tx.begin();
               cache.put("/foo/1", "item", new Integer(1));
               assertEquals(cache.get("/foo/1", "item"), new Integer(1));
               cache.remove("/foo/1");
               assertNull(cache.get("/foo/1", "item"));
               cache.remove("/foo/1");
               assertNull(cache.get("/foo/1", "item"));
               tx.rollback();
               assertFalse(cache.exists("/foo/1"));
               assertNull(cache.get("/foo/1", "item"));
               }
               catch(Throwable t) {
               t.printStackTrace();
               fail(t.toString());
               }
               }