OpenJPA
  1. OpenJPA
  2. OPENJPA-368

Multithreaded client fails randomly on EntityManager.persist() with out transaction context.

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 0.9.7, 1.0.0
    • Fix Version/s: 1.0.1, 1.1.0
    • Component/s: jdbc
    • Labels:
      None
    • Environment:
      WebLogic Server 10.0, Oracle DB

      Description

      This issue happens with the following scenario with JTA transaction.

      Suppose there is Entity A which is related to table with two columns accountId and name. accountId is PK of the table whose value is generated using sequence in DB.

      1. Thread A starts Transaction 1.
      2. Thread B starts Transaction 2.
      3. Thread A and Thread B both calls EntityManager.persist(o) where o is
      Entity object whose primary key is generated with a sequence in DB.
      4. A call is made to o.getAccountId() concurrently.
      5. Thread A suspends Transaction 1, and gets the value of accountId. It updates
      sequence value in DB with new Transaction 3. It saves Transaction 1.
      6. Thread B suspends Transaction 2, and gets the value of accountId. It updates
      sequence value in DB with new Transaction 4. It saves Transaction 2.
      7. Now, when both Thread A and Thread B resumes outer transaction
      concurrently, it updates Transaction Manager with Transaction 2.

      The problem happens when JTA transaction is saved in AbstractJDBCSeq _outerTransaction variable. The variable gets overwritten if multiple threads load the value of accountId from sequence in DB, when it resumes the JTA transaction later it resumes the transaction 2 for both threads resulting in following exception.

      javax.ejb.EJBException: nested exception is: javax.persistence.TransactionRequiredException: The method public abstract void javax.persistence.EntityManager.persist(java.lang.Object) must be called in the context of a transaction.
      javax.persistence.TransactionRequiredException: The method public abstract void javax.persistence.EntityManager.persist(java.lang.Object) must be called in the context of a transaction.
      at weblogic.deployment.BasePersistenceContextProxyImpl.validateInvocation(BasePersistenceContextProxyImpl.java:121)
      at weblogic.deployment.BasePersistenceContextProxyImpl.invoke(BasePersistenceContextProxyImpl.java:86)
      at weblogic.deployment.TransactionalEntityManagerProxyImpl.invoke(TransactionalEntityManagerProxyImpl.java:90)
      at weblogic.deployment.BasePersistenceContextProxyImpl.invoke(BasePersistenceContextProxyImpl.java:80)
      at $Proxy63.persist(Unknown Source)

      Please check README.txt of attached testcase.zip to see how to reproduce the issue.

      1. proposed-fix-v2.patch
        2 kB
        Vikram Bhatia
      2. proposed-fix.patch
        2 kB
        Vikram Bhatia
      3. testcase.zip
        1.60 MB
        Vikram Bhatia

        Activity

        Hide
        Patrick Linskey added a comment -

        Committing Vikram's patch. FTR, I don't love the fact that we're using a ThreadLocal; it feels wrong. However, given the current API design, I think it's unavoidable. Once we move to a system that provides a means to execute a Runnable or some analog in an out-of-band transaction via the ManagedRuntime interface, we'll be able to simplify this code.

        Show
        Patrick Linskey added a comment - Committing Vikram's patch. FTR, I don't love the fact that we're using a ThreadLocal; it feels wrong. However, given the current API design, I think it's unavoidable. Once we move to a system that provides a means to execute a Runnable or some analog in an out-of-band transaction via the ManagedRuntime interface, we'll be able to simplify this code.
        Hide
        Vikram Bhatia added a comment -

        Thanks Patrick. Here is Version 2 of the patch using ThreadLocal.

        Show
        Vikram Bhatia added a comment - Thanks Patrick. Here is Version 2 of the patch using ThreadLocal.
        Hide
        Patrick Linskey added a comment -

        If the solution is going to be based on a thread-keyed map, why not use a ThreadLocal instead?

        Another possible solution would be to obtain a lock on something so that only a single thread can obtain a seq value for a particular instance. Personally, I think that the proposed patch (potentially with a ThreadLocal mod) would be better, though.

        Show
        Patrick Linskey added a comment - If the solution is going to be based on a thread-keyed map, why not use a ThreadLocal instead? Another possible solution would be to obtain a lock on something so that only a single thread can obtain a seq value for a particular instance. Personally, I think that the proposed patch (potentially with a ThreadLocal mod) would be better, though.
        Hide
        Patrick Linskey added a comment -

        I believe that an easy workaround would be to specify a non-jta-data-source in persistence.xml.

        Show
        Patrick Linskey added a comment - I believe that an easy workaround would be to specify a non-jta-data-source in persistence.xml.
        Hide
        Vikram Bhatia added a comment -

        Proposed Fix.

        Show
        Vikram Bhatia added a comment - Proposed Fix.

          People

          • Assignee:
            Unassigned
            Reporter:
            Vikram Bhatia
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development