4 Replies Latest reply on Jun 9, 2015 4:32 PM by christian.beikov

    Datasource with JTA integration

    christian.beikov

      Hello!

       

      I have two datasources, one for master only communication and another one for read only communication.

      My use case is, that I want the read only datasource not to participate in a running transaction. Since I am using container managed transactions, I thought I could use @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) but apparently this didn't make any difference for the transaction system since it enlisted the read only datasource. When trying to enlist the master only datasource it fails with a cryptic error.

       

      Caused by: org.hibernate.exception.GenericJDBCException: Could not open connection

        at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54)

        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)

        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)

        at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:235)

        at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:171)

        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:63)

        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:162)

        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186)

        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:160)

        at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1885)

        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1862)

        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1839)

        at org.hibernate.loader.Loader.doQuery(Loader.java:910)

        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:355)

        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:325)

        at org.hibernate.loader.Loader.loadEntity(Loader.java:2149)

        at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:78)

        at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:68)

        at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4126)

        at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:503)

        at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:468)

        at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:213)

        at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:275)

        at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:151)

        at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1106)

        at org.hibernate.internal.SessionImpl.access$2000(SessionImpl.java:176)

        at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2587)

        at org.hibernate.internal.SessionImpl.get(SessionImpl.java:996)

        at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:306)

        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:186)

        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)

        at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876)

        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858)

        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863)

        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196)

        ... 186 more

      Caused by: java.sql.SQLException: javax.resource.ResourceException: IJ000457: Unchecked throwable in managedConnectionReconnected() cl=org.jboss.jca.core.connectionmanager.listener.TxConnectionListener@6e700878[state=NORMAL managed connection=org.jboss.jca.adapters.jdbc.local.LocalManagedConnection@1b815806 connection handles=0 lastReturned=1433450163370 lastValidated=1433450009358 lastCheckedOut=1433450017295 trackByTx=false pool=org.jboss.jca.core.connectionmanager.pool.strategy.OnePool@18e02a39 mcp=SemaphoreArrayListManagedConnectionPool@baa4336[pool=SweazerTestMasterOnly] xaResource=LocalXAResourceImpl@2a5935ff[connectionListener=6e700878 connectionManager=6913e920 warned=false currentXid=null productName=PostgreSQL productVersion=9.4.1 jndiName=java:jboss/datasources/SweazerTestMasterOnly] txSync=null]

        at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:146)

        at org.jboss.as.connector.subsystems.datasources.WildFlyDataSource.getConnection(WildFlyDataSource.java:67)

        at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:139)

        at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:380)

        at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:228)

        ... 217 more

      Caused by: javax.resource.ResourceException: IJ000457: Unchecked throwable in managedConnectionReconnected() cl=org.jboss.jca.core.connectionmanager.listener.TxConnectionListener@6e700878[state=NORMAL managed connection=org.jboss.jca.adapters.jdbc.local.LocalManagedConnection@1b815806 connection handles=0 lastReturned=1433450163370 lastValidated=1433450009358 lastCheckedOut=1433450017295 trackByTx=false pool=org.jboss.jca.core.connectionmanager.pool.strategy.OnePool@18e02a39 mcp=SemaphoreArrayListManagedConnectionPool@baa4336[pool=SweazerTestMasterOnly] xaResource=LocalXAResourceImpl@2a5935ff[connectionListener=6e700878 connectionManager=6913e920 warned=false currentXid=null productName=PostgreSQL productVersion=9.4.1 jndiName=java:jboss/datasources/SweazerTestMasterOnly] txSync=null]

        at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.reconnectManagedConnection(AbstractConnectionManager.java:927)

        at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:740)

        at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:138)

        ... 221 more

      Caused by: javax.resource.ResourceException: IJ000461: Could not enlist in transaction on entering meta-aware object

        at org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.managedConnectionReconnected(TxConnectionManagerImpl.java:561)

        at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.reconnectManagedConnection(AbstractConnectionManager.java:922)

        ... 223 more

      Caused by: javax.transaction.SystemException: IJ000356: Failed to enlist: java.lang.Throwable: Unabled to enlist resource, see the previous warnings. tx=TransactionImple < ac, BasicAction: 0:ffff0a000063:40f3ed17:5570b5a0:45 status: ActionStatus.ABORT_ONLY >

        at org.jboss.jca.core.connectionmanager.listener.TxConnectionListener$TransactionSynchronization.checkEnlisted(TxConnectionListener.java:883)

        at org.jboss.jca.core.connectionmanager.listener.TxConnectionListener.enlist(TxConnectionListener.java:390)

        at org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.managedConnectionReconnected(TxConnectionManagerImpl.java:554)

        ... 224 more

       

      After I enabled prepared transactions on my PostgreSQL it suddenly worked so I guess that it tried to start a 2PC.

      When setting the attribute "jta" to false on the read only datasource, it does not seem to do a 2PC anymore.

       

      Here some example code:

      @Stateless
      class Ejb1 {
        @Inject
        Ejb2 b2;
        @PersistenceContext(unitName="masterOnly")
        EntityManager em;
      
      
        public void business1() {
        List list = b2.getList();
        Object someEntity = list.get(0);
        // Do something with someEntity
        em.merge(someEntity);
        }
      }
      
      
      @Stateless
      @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
      class Ejb2 {
      
      
        @PersistenceContext(unitName="readOnly")
        EntityManager em;
      
      
        public List getList() {
        return em.createQuery("...").getResultList();
        }
      }
      

       

      Could you tell me what the expected behavior is, or what I might have misconfigured?

      As far as I understand, the jta attribute enables JTA integration which should somehow also propagate the TransactionAttributeType and finally prevent the read only datasource from being enlisted to the transaction.

      I guess setting the jta attribute to false is good enough for me for now, but I am wondering what the implications are and if that is a bug.

       

      I am using IronJacamar 1.2.4.Final in Wildfly 9.0.0.CR1.

        • 1. Re: Datasource with JTA integration
          jesper.pedersen

          A "read-only" datasource, or more correctly a datasource with manual transaction handling is configured by <datasource jta="false">. Your 'SweazerTestMasterOnly' doesn't have the jta=false flag in the above stack trace.

           

          Also, the error occurs because you ar etrying to obtain a connection when the transaction is in the MARK_FOR_ROLLBACK state.

           

          With jta=false there is no 2PC - either the datasource can participate in a transaction or it won't. You can control this using the jta=false or proper transaction semantics in your application.

           

          HTH

          • 2. Re: Datasource with JTA integration
            christian.beikov

            "SweazerTestMasterOnly" is the datasource I would use for writing so I have set "jta" to true on that. I also have set "jta" of "SweazerTestReadOnly" to true with the hope that specifying the transaction type would resolve the problem.

            When I debugged the transaction I saw that "SweazerTestReadOnly" was already enlisted. Trying to add "SweazerTestMasterOnly" to the transaction then causes the fail and results in MARK_FOR_ROLLBACK. As I tried to explain, this is due to the fact that I have disabled "prepared transactions"/2PC on my database. The error doesn't show that and I only came to know that by debugging the transaction code.

             

            Would you recommend to just set "jta" on my read only datasource "SweazerTestReadOnly" to false or should it be true and I should control the participation with the transaction via the transaction type?

            The latter did not work for me for the stated reasons, that the read only datasource was enlisted although in my understanding it shouldn't because I use @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)

             

            Would you mind to elaborate what "proper transaction semantics in your application" means? My understanding is that using the EJB transaction attribute annotations is "the proper way" to control transaction semantics. Please correct me if I am wrong!

             

            Thanks in advance!

            • 3. Re: Datasource with JTA integration
              jesper.pedersen

              jta=false will never participate in a transaction.

               

              You can enlist multiple 2-phase capable resources (f.ex. <xa-datasource>) in a transaction plus one 1-phase (f.ex. <datasource jta=true).

               

              And yes, using @TransactionAttribute is a way of controlling your transaction flow.

              • 4. Re: Datasource with JTA integration
                christian.beikov

                So I guess the "right" way to make a read only datasource would be to use jta=false

                Anyway, if you say that it should be possible to control the transactions via @TransactionAttribute then there is a be a bug in the transaction handling.

                 

                Having a transaction that calls a method annotated with @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) should suspend the transaction as described.

                I use the read only datasource within this suspended context, which I rightfully expect not to be enlisted into a parent transaction. Unfortunately it gets enlisted. The problem becomes apparent when another datasource outside of the suspended context is accessed. Then a 2PC would be started which is not expected.

                 

                Should I provide a test case for that or are you already investigating this? If so, please point me to some code that I can use to get started with.