[jboss-jira] [JBoss JIRA] (WFLY-4333) Transaction must be sticky to ensure consistency for EJB remote invocation with JPA

Scott Marlow (JIRA) issues at jboss.org
Tue Jul 14 16:28:03 EDT 2015


    [ https://issues.jboss.org/browse/WFLY-4333?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13089461#comment-13089461 ] 

Scott Marlow edited comment on WFLY-4333 at 7/14/15 4:27 PM:
-------------------------------------------------------------

>From the JPA 2.1 specification section 7.9.1 Container Responsibilities:
{quote}
For the management of a transaction-scoped persistence context, if there is no EntityManager already
associated with the JTA transaction:

* The container creates a new entity manager by calling EntityManagerFactory.createEntityManager when the first invocation of an entity manager with PersistenceContextType.TRANSACTION occurs within the scope of a business method executing in the JTA transaction.

* After the JTA transaction has completed (either by transaction commit or rollback), the container closes the entity manager by calling EntityManager.close. [86] Note that the JTA transaction may rollback in a background thread (e.g., as a result of transaction timeout), in which case the container should arrange for the entity manager to be closed but the EntityManager.close method should not be concurrently invoked while the application is in an EntityManager invocation.
.
.
.
Remaining text from 7.9.1 omitted.
{quote}

In our implementation of the JPA container/subsystem, that fulfils the above contract, on the first invocation to the SLSB instance, we create an EntityManager instance and associate it with the local transaction (via javax.transaction.TransactionSynchronizationRegistry#putResource(Object, Object)).  On subsequent calls within that same transaction, we only reuse the same EntityManager instance when we reach a node that we have called before.  On calls to new nodes with the same transaction, we will create a new EntityManager instance and associate it with the transaction on that node.

The invoked SLSB bean:
{code}
@Stateless
public class MYSLSB {
	@PersistenceContext(unitName = "myPersistenceUnit")
	EntityManager em;

        public void updateInvoice(int invoiceID, int quantity) {
             Invoice invoice = em.find(Invoice.class, invoiceID);
             invoice.setQuantity(quantity);
             em.persist(invoice);
             // updated invoice is in 1st level JPA cache associated with TSR.putResource() call mentioned above
             // the 1st level JPA cache is not clustered and is not supposed to be
        }
{code}

In invocation1, updateInvoice() is called, as it is in invocation2 + invocation3.  After invoke calls to the stateless session bean, with in the same transaction have occurred where each separate node has been called, we have three separate entity manager instances associated with the transaction.  When the transaction commits, the Synchronization.afterCompletion(int status) is called on each cluster node and the entity manager is flushed to the database from each node.  

By using a sticky load balancing policy for choosing the target node, we ensure that all remote invocations (within the same JTA transaction) are sent to the same node (as long as that node stays available).  


was (Author: smarlow):
>From the JPA 2.1 specification section 7.9.1 Container Responsibilities:
{quote}
For the management of a transaction-scoped persistence context, if there is no EntityManager already
associated with the JTA transaction:

* The container creates a new entity manager by calling EntityManagerFactory.createEntityManager when the first invocation of an entity manager with PersistenceContextType.TRANSACTION occurs within the scope of a business method executing in the JTA transaction.

* After the JTA transaction has completed (either by transaction commit or rollback), the container closes the entity manager by calling EntityManager.close. [86] Note that the JTA transaction may rollback in a background thread (e.g., as a result of transaction timeout), in which case the container should arrange for the entity manager to be closed but the EntityManager.close method should not be concurrently invoked while the application is in an EntityManager invocation.
.
.
.
Remaining text from 7.9.1 omitted.
{quote}

In our implementation of the JPA container/subsystem, that fulfils the above contract, on the first invocation to the SLSB instance, we create an EntityManager instance and associate it with the local transaction (via javax.transaction.TransactionSynchronizationRegistry#putResource(Object, Object)).  On subsequent calls within that same transaction, we only reuse the same EntityManager instance when we reach a node that we have called before.  On calls to new nodes with the same transaction, we will create a new EntityManager instance and associate it with the transaction on that node.

The invoked SLSB bean:
{code}
@Stateless
public class MYSLSB {
	@PersistenceContext(unitName = "myPersistenceUnit")
	EntityManager em;

        public void updateInvoice(int invoiceID, int quantity) {
             Invoice invoice = em.find(Invoice.class, invoiceID);
             invoice.setQuantity(quantity);
             em.persist(invoice);
             // updated invoice is in 1st level JPA cache associated with TSR.putResource() call mentioned above
             // the 1st level JPA cache is not clustered and is not supposed to be
        }
{code}

In invocation1, updateInvoice() is called, as it is in invocation2 + invocation3.

> Transaction must be sticky to ensure consistency for EJB remote invocation with JPA
> -----------------------------------------------------------------------------------
>
>                 Key: WFLY-4333
>                 URL: https://issues.jboss.org/browse/WFLY-4333
>             Project: WildFly
>          Issue Type: Bug
>          Components: EJB
>            Reporter: Wolf-Dieter Fink
>            Assignee: Tomasz Adamski
>            Priority: Critical
>              Labels: ejb, transaction
>
> If an application inside of a server uses standard JavaEE persistence JPA, the current implementation include a local 1. level cache which might end in inconsistent reads within the same (uncommited) transaction.
> There are two reasons
> - JPA must not flush changes to an entity until commit
> - The 1. level cache might not read from the persistence if the entity is already loaded in a transaction
> Therefore the server to server invocations of EJB's need to be sticky to one node during a transaction. The granularity must be the application.
> It might be worth to use already known nodes for other applications as well.
> The stickyness should be enabled automatically if a transaction is active and distributed, which is the default within the current implementation, and use the loadbalancing policy if no transaction is active.



--
This message was sent by Atlassian JIRA
(v6.3.15#6346)


More information about the jboss-jira mailing list