6 Replies Latest reply on Feb 24, 2020 11:29 AM by Laird Nelson

    Narayana transactional JDBC driver questions

    Laird Nelson Expert

      Hello; I had a few questions about the proper usage of the Narayana transactional JDBC driver.

       

      Suppose I have Narayana JTA running (as a JTA-compliant transaction manager).  Suppose further I have a JPA implementation running (EclipseLink, say) that "knows" that a JTA transaction manager is in the picture, such that its EntityManagers are under JTA control.  Now suppose that I have a non-XA DataSource I wish to use to back JPA's <jta-data-source>.  I am not, as a user, interested in distributed transactions, in other words, but I do want to take advantage of injected EntityManagers, which are required by specification to be JTA controlled.  This is a very common case.

       

      It is my understanding this is one of the cases that the transactional JDBC driver exists to solve in some fashion.  Is that correct?

       

      If I understand right, the Narayana transactional JDBC driver wraps an XADataSource in java.sql.Driver clothing.  Is that right?

       

      If I wanted to use the HikariCP connection pool, which doesn't support XA itself, but is otherwise an excellent connection pool, I could tell it to pool Narayana JDBC driver URLs.  I would also need to tell the Narayana transactional driver not to do any pooling of its own.  Is that correct?  So Hikari would pool Narayana JDBC driver URLs, which, indirectly, would effectively route to XADataSources.  Is that right?

       

      One of the properties that I would supply in such a case would be a means of acquiring the XADataSource that the Narayana JDBC driver wraps.  I would (for my purposes) set the dynamicClass property (if I remember right).  Is that right?

       

      If I'm right so far, then things look like this:

      EclipseLink--uses-->DataSource.getConnection()-->supplied by-->Hikari--pools-->Narayana driver URLs--wraps-->XADataSource-->supplies XAConnection

      Is that right?

       

      Then magic happens, and even with the default auto commit settings in play two INSERT statements would get rolled back when the JTA transaction rolls back.  And it is the Narayana transactional driver that somehow makes this happen (that's sort of the part I'm missing).

       

      Now, as it happens, I don't actually care about XA.  I'm only ever going to use a connection to a single database (let's say).  But it appears to me that the data source supplied to JPA via <jta-data-source> must be XA-capable (even if it wraps a non-XA capable data source under the covers).  Otherwise in the case of transaction rollbacks the various SQL statements that might have gone to the database could be visible after the rollback (there are many examples on the web of people being bitten in this way).  Does the Narayana driver somehow solve this problem as well?  Does it allow a non-XA DataSource to somehow "play nice" with the JTA controller, or must I always supply an XADataSource to the Narayana transactional driver?

       

      Thanks,

      Best,

      Laird

        • 1. Re: Narayana transactional JDBC driver questions
          Ondrej Chaloupka Master

          Hi ljnelson ,

           

          I will try to respond but as the question overall is quite complex I could miss some parts. Let me know in such case.

           

          First from the general perspective. Narayana implements the JTA specification. The JTA works with XAResources not with JDBC connections. For Narayana would be able to work with JDBC the JDBC has to provide the XAResource interface from its part. The JDBC specification defines the package 'javax.sql' which is that bridge between the simple JDBC ('java.sql') and JTA ('javax.transaction.xa'). It's responsibility of the JDBC to provide the XAResource for Narayana. If JDBC provides the XAResource and we know the Narayana is able to consume the resource there has to be something that integrates (glues) those two parts. It's usually some runtime (like WildFly, Spring, Quarkus...) or some library for more standalone usage (like Narayana JDBC driver or Agroal).

          What is meant by integration (aka. glueing) is way you use the Narayana and JDBC.

           

           

          You can write things like - a bit of pseudocode without closing which generates more boilerplate code:

           

          TransactionManager tm = com.arjuna.ats.jta.TransactionManager.transactionManager(); 
          
          org.h2.jdbcx.JdbcDataSource ds = new JdbcDataSource();
          XAConnection xaconn = ds.getXAConnection();
          XAResource xares = xaConn.getXAResource();
          
          tm.begin();
          tm.getTransaction().enlistResource(xares);
          Connection con = xares.getConnection()) {
          Statement stmt = con.createStatement();
          stmt.executeUpdate("INSERT INTO ...");
          tm.commit();

           

          What runtime provides is more-or-less simplification to this where you don't need to mingle with XAResources:

           

          TransactionManager tm = com.arjuna.ats.jta.TransactionManager.transactionManager(); 
          FrameworkDatasource ds = framework.getDatasource();
          
          tm.begin();
          Connection con = ds.getConnection()) {
          Statement stmt = con.createStatement();
          stmt.executeUpdate("INSERT INTO ...");
          tm.commit();

           

          On top of this "gluing" there is another point which is not solved with the "Narayana - <glue code> - JDBC driver". Which is the pooling of connections. The Narayana JDBC driver, Ironjacamar or Agoral do the both - they are able to glue Narayana with JDBC XA driver code plus they are capable to pool the connections.

           

          If you want Hikari would be for pooling and the Hikari does not provies the JDBC XA interfaces then you will need to have a wrapper that will provides the implementation of those interfaces on top. I think the Narayana JDBC driver (even the dynamic class) won't be capable to provide this out of the box. I think you will need to write some extensions here. But I think you could need only for the wrapper implementing the XADatasource (XADataSource (Java Platform SE 8 ) ) (but that should probably means implicitly to implement XAConnection, XAResource...). While in your case you don't need the "prepare" functionality and you can ignore that.

           

          Regarding of the glue code it depends how you want to work with the JTA implementation. But it's good to have that "gluing" code for easier usage. If you have the "wrapper" created then it should be fine to use the Narayana JDBC driver and the dynamic class with no pooling.

          • 2. Re: Narayana transactional JDBC driver questions
            Laird Nelson Expert

            Thank you, this is very helpful.

             

            To understand you a little better:

             

            If you want Hikari would be for pooling and the Hikari does not provies the JDBC XA interfaces then you will need to have a wrapper that will provides the implementation of those interfaces on top. I think the Narayana JDBC driver (even the dynamic class) won't be capable to provide this out of the box. I think you will need to write some extensions here. But I think you could need only for the wrapper implementing the XADatasource (XADataSource (Java Platform SE 8 ) ) (but that should probably means implicitly to implement XAConnection, XAResource...). While in your case you don't need the "prepare" functionality and you can ignore that.

             

            Suppose I were to patch HikariCP's DataSource implementation to also implement XADataSource, together with some means of acquiring a TransactionManager (Narayana or not).  Wouldn't the actual pooling mechanism have to change, since JTA transactions must be pinned to a thread?  Wouldn't that mean that I would have to make sure that the HikariCP DataSource would be physically pinned to a thread?

             

            Thanks,

            Best,

            Laird

            • 3. Re: Narayana transactional JDBC driver questions
              Ondrej Chaloupka Master

              ljnelson right. While you need to check in details by yourself if it's feasible ;-)

              If it's successful it will be a great to document it as a Narayana quickstart (or at least as an blog article).

               

              I assume you will need to enhance the HikariCP to provide the XADataSource. I think it could be just a thin layer wrapping the Datasource itself (where you throw some UnsupportedOperationException on prepare). Working with the TransactionManager will be the task for the Narayana JDBC driver.

              I assume the HikariCP will be configured with a JDBC connection data to get connection to database. HikariCP then provides the XADataSource that will be integrated with Narayana by you write implementation of the narayana/DynamicClass. The JDBC driver should always acquire a new connection without pooling.

              The HikariCP provides a connection which is taken from the pool. At time you close the connection the connection is returned to HikariCP pool and it's available for next use. The linking of the datasource to transaction (which effectively means binding it to the thread as a thread local variable) will be done in the Narayana JDBC driver code when it aquires the connection from HikariCP.

               

              But as JDBC driver is currently used mainly by us for testing and experimental purposes I would be a afraid that this setup won't be checking some error cases. E.g. when thinking about this I have no idea what happens when HikariCP has no available connection for the JDBC driver. These types of cases would need to be tested and you are more than welcome to provide the PR with such adjustments ;-)

              • 4. Re: Narayana transactional JDBC driver questions
                Thomas Jenkinson Master

                I think IronJacamar might be a way to achieve this.

                 

                We have a quickstart using IronJacamar but the xa-datasource would need to be changed to a non-xa datasource: quickstart/h2-xa-ds.xml at master · jbosstm/quickstart · GitHub - you could reach out to the IronJacamar community over here for guidance on settings to allow a datasource to be wrapped as a LRCO resource. That quickstart uses a second XA resource too which you would not want to do: quickstart/CustomerManager.java at master · jbosstm/quickstart · GitHub

                 

                A similar topic was discussed over here - there might be some further discussion that would be relevant in there.

                • 5. Re: Narayana transactional JDBC driver questions
                  Ondrej Chaloupka Master

                  On our retrospective of the answered questions on the forum we got to this discussion - thus I would like to check:

                  ljnelson  have you found a way to get working the Narayana with HikariCP or have you used the DBCP2 or stayed with the Narayana JDBC driver? Is there something more we can help with?

                  • 6. Re: Narayana transactional JDBC driver questions
                    Laird Nelson Expert

                    ochaloup  wrote:

                     

                    On our retrospective of the answered questions on the forum we got to this discussion - thus I would like to check:

                    ljnelson   have you found a way to get working the Narayana with HikariCP or have you used the DBCP2 or stayed with the Narayana JDBC driver? Is there something more we can help with?

                    I actually went a slightly different route.  I also took advantage of the CDI events that Narayana fires when the transaction scope starts.

                     

                    Basically, when a transaction starts (as detected by @Observes @Initialized(TransactionScoped.class)), I ensure that connections coming from any non-JTA DataSource implementation (HikariCP in this case) are "pinned" to the current thread.  I also deactivate their close and autocommit abilities.  Then I register a synchronization with the transaction manager such that when the transaction commits I call commit() on the connection and when the transaction rolls back I call rollback().  Then I "unpin" the connection, closing it if necessary.  Works great and does not require XA interfaces to be implemented.

                     

                    Thanks again for your explanation above which got me pointed in the right direction.

                     

                    Best,

                    Laird