9 Replies Latest reply on Nov 7, 2001 6:42 PM by danch1

    Deadlock in EJB due to transactions?

    normann

      I'm working on an EJB system to be deployed under JBoss (2.4.3), but I'm running into deadlocks in the beans I'm developing. I have created an example and included it below.

      The problem is that I sometimes end up in the same method in the same entity bean twice during a large transaction. But the second time I try to update on the entity bean, JBoss locks the transaction. What am I doing wrong? I have tried to change the <trans-attribute> in the deployment descriptor but that doesn't help.

      Your help will be appreciated.

      Best regards,
      Jan Nielsen

      Here's the bean class:
      ------------------------------------------------------
      [pre]package ejb;

      import javax.rmi.*;
      import java.rmi.*;
      import javax.ejb.*;

      public class ABean implements javax.ejb.EntityBean {
      private EntityContext ctx = null;
      public Integer key = null;
      public java.lang.String a1 = null;
      public java.lang.String a2 = null;

      public ABean() { super(); }

      public Integer ejbCreate(Integer key) throws CreateException {
      this.key = key;
      this.a1 = null;
      this.a2 = null;
      return this.getKey();
      }

      public void ejbPostCreate(Integer key) {}
      public void ejbLoad() {}
      public void ejbStore() {}
      public void ejbActivate() {}
      public void ejbPassivate() {}
      public void ejbRemove() throws RemoveException {}

      public EntityContext getEntityContext() { return this.ctx; }
      public void unsetEntityContext() { this.ctx = null; }
      public void setEntityContext(EntityContext ctx) { this.ctx = ctx; }

      public Integer getKey() { return this.key; }
      public void setKey(Integer key) { this.key = key; }

      public java.lang.String getA1() { return this.a1; }
      public void setA1(java.lang.String a1) { this.a1 = a1; }

      public java.lang.String getA2() { return this.a2; }
      public void setA2(java.lang.String a2) {
      try {
      this.a2 = a2;
      A a = (A) this.getEntityContext().getEJBObject();
      // This method call will create a dead-lock, even though it is a
      // call to the same entity bean that I'm already in. Why is that?
      // The line could be replaced with this.setA1(a2) and work fine.
      a.setA1(a2);
      } catch (RemoteException e) {
      throw new EJBException(e);
      }
      }

      }[/pre]
      ------------------------------------------------------
      Here's the remote interface:
      ------------------------------------------------------
      [pre]package ejb;

      import java.rmi.*;
      import javax.ejb.*;
      import java.util.*;

      public interface A extends EJBObject {
      java.lang.String getA1() throws RemoteException;
      void setA1(java.lang.String a1) throws RemoteException;
      java.lang.String getA2() throws RemoteException;
      void setA2(java.lang.String a2) throws RemoteException;
      }[/pre]
      ------------------------------------------------------
      Here's the home interface:
      ------------------------------------------------------
      [pre]package ejb;

      import java.rmi.*;
      import javax.ejb.*;
      import java.util.*;

      public interface AHome extends EJBHome {
      A create(Integer key) throws RemoteException, CreateException;
      A findByPrimaryKey(Integer key) throws RemoteException, FinderException;
      }[/pre]
      ------------------------------------------------------
      Here's the deployment descriptor:
      ------------------------------------------------------
      [pre]<?xml version="1.0"?>

      <ejb-jar>
      <enterprise-beans>


      <ejb-name>A</ejb-name>
      ejb.AHome
      ejb.A
      <ejb-class>ejb.ABean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      False
      <primkey-field>key</primkey-field>
      <cmp-field><field-name>key</field-name></cmp-field>
      <cmp-field><field-name>a1</field-name></cmp-field>
      <cmp-field><field-name>a2</field-name></cmp-field>


      </enterprise-beans>

      <assembly-descriptor>
      <container-transaction>

      <ejb-name>A</ejb-name>
      <method-name>*</method-name>

      <trans-attribute>Required</trans-attribute>
      </container-transaction>
      </assembly-descriptor>

      </ejb-jar>[/pre]
      ------------------------------------------------------
      Here's my test client:
      ------------------------------------------------------
      [pre]package ejb;

      import java.rmi.*;
      import javax.rmi.*;
      import javax.ejb.*;
      import javax.naming.*;

      public class ATester {
      private ATester() {
      }

      public static final void main(String args[]) {
      new ATester().run();
      }

      private static AHome getAHome() {
      try {
      InitialContext ctx = new InitialContext();
      return (AHome) PortableRemoteObject.narrow(ctx.lookup("A"), AHome.class);
      } catch (NamingException e) {
      System.out.println(e.toString());
      return null;
      }
      }

      private void run() {
      try {
      A a = this.getAHome().create(new Integer(0));
      a.setA1("test"); // ok
      a.setA2("test2"); // dead-lock - see remarks in bean class.
      } catch (RemoteException e) {
      e.printStackTrace();
      } catch (CreateException e) {
      e.printStackTrace();
      }
      }
      }[/pre]

        • 1. Re: Deadlock in EJB due to transactions?
          erik777

          How did you get your code to paste so cleanly?!?

          I was getting transaction locks, but discovered it was because it did not commit the transaction. So, the second time I attempted to do the same update, it would lock. But, this problem was with BMP, not CMP.

          Nevertheless, perhaps your problem is similar. It is trying to get a lock on the same table/row that it already created a lock on.

          I'm not sure if RequiresNew works in JBoss. I could have sworn I read something about it not working correctly, or JBoss not supporting nested transactions. Yet, I'd look into this.

          Of course, there is the issue of what database you are using. I'd try to get a log of what's going on there. In MySQL I turned the log on, and have learned a lot since then.

          • 2. Re: Deadlock in EJB due to transactions?
            danch1

            > I'm not sure if RequiresNew works in JBoss.

            it does.

            > I could
            > have sworn I read something about it not working
            > correctly, or JBoss not supporting nested
            > transactions.

            No EJB container supports 'nested' transactions - the spec says not to.
            RequiresNew doesn't nest transactions, it suspends the current, creates a new transaction, invokes the method, commits the new transaction, and activates the original transaction. There are two flat transactions, not two nested transactions.

            Just to clarify.

            • 3. Re: Deadlock in EJB due to transactions?
              danch1

              If your bean is reentrant (which it is) you need to mark it reentrant in your deployment descriptor.

              • 4. Re: Deadlock in EJB due to transactions?
                normann

                > How did you get your code to paste so cleanly?!?

                Use the normal HTML tag before and after the code, but instad of using < and > I use square brackets.

                > Nevertheless, perhaps your problem is similar. It is
                > trying to get a lock on the same table/row that it
                > already created a lock on.
                >
                > I'm not sure if RequiresNew works in JBoss. I could
                > have sworn I read something about it not working
                > correctly, or JBoss not supporting nested
                > transactions. Yet, I'd look into this.

                I have tried all transaction attributes possible, but it doesn't solve the problem. I might not understand transactions completely, but I think it is pointless to lock a row that is already locked in the same transaction.

                > Of course, there is the issue of what database you
                > are using. I'd try to get a log of what's going on
                > there. In MySQL I turned the log on, and have
                > learned a lot since then.

                I just use whatever is shipped with JBoss, is that Hypersonic or something?

                Best regards,
                Jan Nielsen

                • 5. Re: Deadlock in EJB due to transactions?
                  erik777

                  [pre]
                  What do you mean by "suspends the current"? Is the second transaction using the same connection? If so, how does this show up in the database? If you begin a transaction a second time, it is inherently nested in the database, and rolling back the outer transaction should roll back the inner transaction, no? Are you saying that this isn't true with J2EE and RequiresNew. If so, I am lost as to how this is accomplished in the database.
                  [/pre]

                  • 6. Re: Deadlock in EJB due to transactions?
                    erik777

                    Wow, PRE works! Here's same message with word-wrapping:

                    What do you mean by "suspends the current"? Is the second transaction using the same connection? If so, how does this show up in the database? If you begin a transaction a second time, it is inherently nested in the database, and rolling back the outer transaction should roll back the inner transaction, no? Are you saying that this isn't true with J2EE and RequiresNew. If so, I am lost as to how this is accomplished in the database.

                    • 7. Re: Deadlock in EJB due to transactions?
                      fcrabus

                      > Wow, PRE works! Here's same message with
                      > word-wrapping:
                      >
                      > What do you mean by "suspends the current"? Is the
                      > second transaction using the same connection? If so,
                      > how does this show up in the database? If you begin
                      > a transaction a second time, it is inherently nested
                      > in the database, and rolling back the outer
                      > transaction should roll back the inner transaction,
                      > no? Are you saying that this isn't true with J2EE
                      > and RequiresNew. If so, I am lost as to how this is
                      > accomplished in the database.
                      >

                      Suppose we have three beans.
                      bean1 (tx attribute Required)
                      bean2 (tx attribute RequiresNew)
                      bean3 (tx attribute Required)

                      If bean1 calls method in bean3 and an exception occurs ->
                      both rollback (i.e. bean3 joins transaction of bean1).

                      But if bean1 calls method in bean2 and an exception occurs ->
                      then only bean2 rolls back (i.e. bean2 started a new transaction, that did not affect the previous transaction).
                      As danch said you have two separate flat transactions.

                      You can see this reaction as a feature or as a liability...you decide...(if you ask me, nested transactions are nice, but I'm more comfortable with flat transactions that work - but I'm sure nested transactions will be part of EJB x.0)

                      • 8. Re: Deadlock in EJB due to transactions?
                        erik777

                        That makes sense. I realized that I implemented nested transactions by default in COM, but could have easily overridden this, and think I may have actually done it in a few rare cases.

                        One situation where I might have implemented a "flat" transaction (RequiresNew) would be where the inner one was rolled back, but then an alternative course of action commenced successfully, or perhaps the inner transaction's success wasn't critical to the success of the outter transaction.

                        I do like the fact that I could clearly control the business rules in COM. One may be able to argue, though, that with EJBs nested transactions aren't necessary. With EJBs, all calls to methods marked with "Requires" invoked from an EJB method will be encapsulated in that transaction, whereas in COM, the nested transactions were generally due to the fact that the inner transactions were not aware they were already part of an outter transaction, so they created their own. This nesting would have been unnecessary had they been able to detect whether or not they needed to begin a transaction.

                        Given this, J2EE never support nested transactions. Our thinking and design will just have to adjust.

                        • 9. Re: Deadlock in EJB due to transactions?
                          danch1

                          > [pre]
                          > What do you mean by "suspends the current"?

                          It dissaciates it from the thread.
                          > Is the
                          > second transaction using the same connection?

                          Simple answer is no. With XA drivers that's not neccessarily technically true, but the behavior is the same.

                          > If so,
                          > how does this show up in the database? If you begin
                          > a transaction a second time, it is inherently nested
                          > in the database, and rolling back the outer
                          > transaction should roll back the inner transaction,
                          > no?

                          the transactions aren't nested. there is no inner and outer transaction.

                          > Are you saying that this isn't true with J2EE
                          > and RequiresNew.

                          RequiresNew does exactly what it says: starts a new and unrelated transaction. they're not nested. The 2 transactions have nothing to do with each other.