10 Replies Latest reply on Mar 16, 2004 7:36 AM by steinarc

    CMP + RequiresNew = ApplicationDeadlock

    morgan

      Hi, I'm relatively new to JBoss and J2EE, and I'm currently running into a
      problem with the "RequiresNew" transaction attribute. I've looked through
      the forums, and have been going through Dain's JBossCMP documentation,
      but I've yet to stumble across what might be causing my problem ... an
      application deadlock due to the current thread already having a lock in a
      different transaction. At the end of this message is (what I believe) to be
      the relevant portion of the stack trace ... and if anyone would like more info,
      I'd be more than happy to provide it.

      What I have is an entity bean, "KeyFactory" which exposes one method and
      I'm setting the transaction attribute for that method to "RequiresNew" by using
      an XDoclet tag like so:
      [pre]
      @ejb:transaction type="RequiresNew"
      [/pre]
      which appears to generate the right thing in "ejb-jar.xml":
      [pre]
      <container-transaction >

      <ejb-name>KeyFactory</ejb-name>
      <method-intf>Local</method-intf>
      <method-name>incrementKeyBy</method-name>
      <method-params>
      <method-param>int</method-param>
      </method-params>

      <trans-attribute>RequiresNew</trans-attribute>
      </container-transaction>
      [/pre]
      No other transaction attributes are set, and (in my test case) this method,
      "incrementKeyBy", is only being accessed once via a session bean which
      is only called once. Oh, and if I remove the transaction attribute (or set it to
      "Required") it works just fine. Currently, I'm using JBoss 3.0.2 and PostgreSQL 7.2.1
      (thoughI had the same problem with Hypersonic).

      Any help, or pointers to relevant documentation would be most appreciated
      (I'm going a little crazy here).

      Thanks,

      Paul

      p.s., I initially posted this to message to "EJB/JBoss", but I wasn't sure if that
      was the right forum so I've cross-posted it here. I apologize if that's a major
      faux pas, but I'm getting increasingly desperate.
      [pre]
      10:34:21,652 ERROR [STDERR] Caused by: org.jboss.ejb.plugins.lock.ApplicationDeadlockException: Application deadlock detected: Current thread already has tx lock in different transaction.
      at org.jboss.ejb.plugins.lock.BeanLockSupport.deadlockDetection(BeanLockSupport.java:182)
      at org.jboss.ejb.plugins.lock.QueuedPessimisticEJBLock.waitForTx(QueuedPessimisticEJBLock.java:283)
      at org.jboss.ejb.plugins.lock.QueuedPessimisticEJBLock.doSchedule(QueuedPessimisticEJBLock.java:189)
      at org.jboss.ejb.plugins.lock.QueuedPessimisticEJBLock.schedule(QueuedPessimisticEJBLock.java:140)
      at org.jboss.ejb.plugins.EntityLockInterceptor.invoke(EntityLockInterceptor.java:103)
      at org.jboss.ejb.plugins.EntityCreationInterceptor.invoke(EntityCreationInterceptor.java:69)
      at org.jboss.ejb.plugins.AbstractTxInterceptor.invokeNext(AbstractTxInterceptor.java:107)
      at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:255)
      at org.jboss.ejb.plugins.TxInterceptorCMT.invoke(TxInterceptorCMT.java:60)
      at org.jboss.ejb.plugins.SecurityInterceptor.invoke(SecurityInterceptor.java:130)
      at org.jboss.ejb.plugins.LogInterceptor.invoke(LogInterceptor.java:203)
      at org.jboss.ejb.EntityContainer.invoke(EntityContainer.java:493)
      at org.jboss.ejb.plugins.local.BaseLocalContainerInvoker.invoke(BaseLocalContainerInvoker.java:301)
      at org.jboss.ejb.plugins.local.EntityProxy.invoke(EntityProxy.java:38)
      [/pre]

        • 1. Re: CMP + RequiresNew = ApplicationDeadlock
          dsundstrom

          The default transaction attribute in JBoss is Required. My guess is you have an existing transaction when you call incrementByKey which is holding on to a lock to the KeyFactory, so when incrementByKey starts a new transaction you instantly get a deadlock.

          I assume this code is an auto number key factory. I suggest limit all access to the KeyFactory to a single entry point which has the tx attribute RequiresNew. The best place for this entry point is a home method.

          • 2. Re: CMP + RequiresNew = ApplicationDeadlock
            morgan

            First, thanks for the response, I appreciate it!

            Hmm, I thought I was doing most of what you suggested, in that I do limit all
            access to the KeyFactory to a single entry point that has the tx attribute
            "RequiresNew" (but it isn't a home method, so I'll make that change). And I
            didn't think that I had any other existing transactions that would be holding
            on to a lock to the key factory, but suffering from newbieosis I'm probably
            overlooking something, so I'll take a closer look. I'll let you know what
            I find.

            Paul

            • 3. Re: CMP + RequiresNew = ApplicationDeadlock
              morgan

              I'm still perplexed, dazed and confused ...

              The code that's causing me grief is a primary key generator, and
              is loosely based on code packaged with Floyd Marinescu's book,
              "EJB Design Patterns". I figured that something must have been
              lost in the translation from his code to mine, so I decided to
              use his code as a baseline. Oops, the problem didn't go away.
              So, I thought that it must be something in the rest of my code
              that was causing the problem, so I took his code (an entity bean
              and a session bean) and plopped it down in the crime portal
              example.

              In "GangsterBean.ejbCreate" I added:
              [pre]
              SequenceSessionLocalHome home;
              try {
              home = (SequenceSessionLocalHome)((new javax.naming.InitialContext()).lookup("SequenceSessionLocal"));
              }
              catch(javax.naming.NamingException e) {
              throw new CreateException(e.getExplanation());
              }
              SequenceSessionLocal sequence = home.create();
              int number = sequence.getNextSequenceNumber("GANGSTER");
              System.err.println("Next Number: " + number);
              [/pre]
              I also made the (hopefully) appropriate entries in "ejb-jar.xml",
              "jboss.xml", and "jbosscmp-jdbc.xml" (I've included the entries I
              made at the end of this message).

              I then ran "ant" followed by "ant setup", and still with the...
              [pre]
              [junit] Caused by: org.jboss.ejb.plugins.lock.ApplicationDeadlockException: Application deadlock detected: Current thread already has tx lock in different transaction.
              [/pre]
              So, I'm at a loss. Any thoughts on what else I might check? Or,
              is this possibly a JBoss problem?

              Any and all help is appreciated, thanks,

              Paul

              Following are the snippets I added to the deployment descriptors
              [pre]
              In "ejb-jar.xml":

              <ejb-name>SequenceSession</ejb-name>

              <local-home>org.jboss.docs.cmp2.crimeportal.SequenceSessionLocalHome</local-home>
              org.jboss.docs.cmp2.crimeportal.SequenceSessionLocal
              <ejb-class>org.jboss.docs.cmp2.crimeportal.SequenceSessionBean</ejb-class>

              <session-type>Stateless</session-type>
              <transaction-type>Container</transaction-type>



              <ejb-name>Sequence</ejb-name>

              <local-home>org.jboss.docs.cmp2.crimeportal.SequenceLocalHome</local-home>
              org.jboss.docs.cmp2.crimeportal.SequenceLocal
              <ejb-class>org.jboss.docs.cmp2.crimeportal.SequenceBean</ejb-class>

              <persistence-type>Container</persistence-type>
              <prim-key-class>java.lang.String</prim-key-class>
              False
              <cmp-version>2.x</cmp-version>
              <abstract-schema-name>SequenceBean</abstract-schema-name>

              <cmp-field><field-name>index</field-name></cmp-field>
              <cmp-field><field-name>name</field-name></cmp-field>

              <primkey-field>name</primkey-field>


              <container-transaction>

              <ejb-name>Sequence</ejb-name>
              <method-name>getValueAfterIncrementingBy</method-name>

              <trans-attribute>RequiresNew</trans-attribute>
              </container-transaction>

              In "jboss.xml":


              <ejb-name>SequenceSession</ejb-name>
              <local-jndi-name>SequenceSessionLocal</local-jndi-name>



              <ejb-name>Sequence</ejb-name>
              <local-jndi-name>SequenceLocalHome</local-jndi-name>


              In "jbosscmp-jdbc.xml":


              <ejb-name>Sequence</ejb-name>
              <table-name>sequences</table-name>

              <cmp-field>
              <field-name>index</field-name>
              <column-name>index</column-name>
              </cmp-field>
              <cmp-field>
              <field-name>name</field-name>
              <column-name>name</column-name>
              </cmp-field>

              [/pre]

              • 4. Re: CMP + RequiresNew = ApplicationDeadlock
                dsundstrom

                If all you want is Floyd's auto number, then you should use the autonumber code included with JBoss. The gangster example code included with the quick start guide used this.


                As for why you are getting this exact exception, my guess is you are fault creating the Sequence entity in you session bean which locks the new entity, and then you call getNextSequenceNumber which starts a new tx and tries to lock the bean which is held by the old transaction... instant deadlock.

                • 5. Re: CMP + RequiresNew = ApplicationDeadlock
                  morgan

                  Thanks for taking the time to help me out, I do appreciate it.

                  I probably will use JBoss's autonumber code, but I'm still curious
                  what the problem is. The main reason I've spent so much time on
                  this problem is that I don't understand its cause, and being new
                  to this stuff I thought that it would be reasonable to figure it
                  out ... otherwise, I'll just make similar mistakes down the road.

                  That said, I hope I'm not being too much of a time sink if I ask
                  you what "fault creating" is. I searched for that term (on the web,
                  in your book, and in the quick start guide) and have so far come up
                  with nothing. Even a pointer to a place to look would be appreciated.

                  Once again thanks for your time,

                  Paul

                  • 6. Re: CMP + RequiresNew = ApplicationDeadlock
                    dsundstrom

                    Fault createing is my term. My guess is that when you don't find an existing sequence you create a new one. When that new sequence is created it is locked with the existing tx, and when you later call it with a new tx you get a deadlock. It is very common to get a deadlock with requires new if the old transaction has accesses the bean and there for has a lock.

                    Do you follow?

                    • 7. Re: CMP + RequiresNew = ApplicationDeadlock
                      morgan

                      Thanks, I think I do follow.

                      Below is the method from Floyd's session bean (I removed some exception handling
                      to shorten it up).
                      [pre]
                      public int getNextSequenceNumber(String name) {
                      try {
                      Entry entry = (Entry) _entries.get(name);
                      if (entry == null) {
                      entry = new Entry(); // add an entry to the sequence table
                      try {
                      entry.sequence = _sequenceHome.findByPrimaryKey(name);
                      }
                      catch (javax.ejb.FinderException e) {
                      entry.sequence = _sequenceHome.create(name);
                      [/pre]
                      So when this new sequence is created, it is locked with the transaction covering
                      the invocation of "getNextSequenceNumber"
                      [pre]
                      }
                      _entries.put(name, entry);
                      }
                      if (entry.last % _blockSize == 0) {
                      entry.last = entry.sequence.getValueAfterIncrementingBy(_blockSize);
                      [/pre]
                      and here we deadlock since "getValueAfterIncrementingBy" has a transaction
                      attribute of "RequiresNew" and sequence is already locked
                      [pre]
                      }
                      return entry.last++;
                      }
                      catch (javax.ejb.CreateException e) {
                      throw new javax.ejb.EJBException(e);
                      }
                      }
                      [/pre]

                      Thanks for all the time you've spent helping me out!

                      Paul

                      • 8. Re: CMP + RequiresNew = ApplicationDeadlock
                        klei

                        Hello !

                        I have the same problem, but my workaround was to initialize the sequence "by hand" in the database and after that it worked.
                        Is there any solution to this problem ?

                        Regards,
                        Stephan

                        • 9. Re: CMP + RequiresNew = ApplicationDeadlock
                          dsundstrom

                          Yes, mark getNextSequenceNumber in the session bean as RequiresNew and don't mark the entity as RequiresNew but Requires instead. This moves the transaction creation out to the session bean an causes the lock to happen across the create and access.

                          • 10. Re: CMP + RequiresNew = ApplicationDeadlock
                            steinarc

                            This problem can be solved by specyfing 'RequiresNew' on all the methods of the Entity bean in question. I'm using the pattern from Floyd's book, and setting the transaction attribute like the following solved the problem:

                             <container-transaction>
                             <method>
                             <ejb-name>SequenceEJB</ejb-name>
                             <method-name>*</method-name>
                             </method>
                             <trans-attribute>RequiresNew</trans-attribute>
                             </container-transaction>
                             <container-transaction>
                             <method>
                             <ejb-name>SequenceSessionEJB</ejb-name>
                             <method-name>*</method-name>
                             </method>
                             <trans-attribute>Required</trans-attribute>
                             </container-transaction>