Re: XAResource implementation - Mailing list pgsql-jdbc

From Heikki Linnakangas
Subject Re: XAResource implementation
Date
Msg-id 4739D5FB.9080202@enterprisedb.com
Whole thread Raw
In response to Re: XAResource implementation  (joël Winteregg <joel.winteregg@gmail.com>)
Responses Re: XAResource implementation
Re: XAResource implementation
List pgsql-jdbc
joël Winteregg wrote:
> Many thanks for your answer and for the time you took to do some
> testing ! As you can see I put Ludovic Orban as Cc because he is the BTM
> developer and he seems to be interested to this issue too...
>
>> Hmm. I downloaded the BTM newUserDemo.zip and modified it to run with
>> Postgres, and to run two queries in same transaction. Works for me.
>>
>
> Ah Yes, you're right, your transaction just looks perfect !
>
>> Attached is the modified Test.java I used. To run:
>> 0. Download newUserDemo.zip from
>> http://docs.codehaus.org/display/BTM/NewUserGuide
>> 1. Put postgresql.jar in newUserDemo/lib
>> 2. Copy the attached Test.java to newUserDemo/src/jtatest
>> 3. Modify database/username/password in Test.java if necessary
>> 4. Run the CREATE TABLE from derby-create.sql
>>
>> Here's what I get in the Postgres log, with log_statements='all':
>>
>> LOG:  execute <unnamed>: SELECT gid FROM pg_prepared_xacts
>> LOG:  execute S_1: BEGIN
>> LOG:  execute <unnamed>: insert into messages(content) values ($1)
>> DETAIL:  parameters: $1 = 'hello, world!'
>> LOG:  execute S_2: COMMIT
>> LOG:  execute S_1: BEGIN
>> LOG:  execute <unnamed>: select content from messages
>> LOG:  execute <unnamed>: select content from messages
>> LOG:  execute S_2: COMMIT
>
> On my side, I tryed the following example:
> http://docs.codehaus.org/download/attachments/9240687/HibernateBTM.zip?version=2

Ok, I downloaded and installed that as well, and saw similar results.

It looks like there is indeed a bug in the Postgres driver. I believe it
was introduced by the recent changes to keep the connection in
autocommit mode when no XA-transaction is in progress.

It's this call sequence that borks it up:

xares.start()
conn = xares.getConnection()
// do stuff
conn.close();
conn = xares.getConnection()
// do more stuff
xares.end()
xares.commit()

The 2nd getConnection call inadvertently commits the transaction, and
opens another one.

Patch attached. I also added a test for this in the test suite.

Can you check that this fixes the issue for you, please? I can send you
a patched jar if you don't have build environment, let me know if you
need it.

--
   Heikki Linnakangas
   EnterpriseDB   http://www.enterprisedb.com
? xa-getConnection-fix.patch
Index: org/postgresql/ds/jdbc23/AbstractJdbc23PooledConnection.java
===================================================================
RCS file: /cvsroot/jdbc/pgjdbc/org/postgresql/ds/jdbc23/AbstractJdbc23PooledConnection.java,v
retrieving revision 1.2
diff -c -r1.2 AbstractJdbc23PooledConnection.java
*** org/postgresql/ds/jdbc23/AbstractJdbc23PooledConnection.java    10 Sep 2007 08:38:15 -0000    1.2
--- org/postgresql/ds/jdbc23/AbstractJdbc23PooledConnection.java    13 Nov 2007 16:41:27 -0000
***************
*** 137,143 ****
                  }
                  con.clearWarnings();
              }
!             con.setAutoCommit(autoCommit);
          }
          catch (SQLException sqlException)
          {
--- 137,149 ----
                  }
                  con.clearWarnings();
              }
!             /*
!              * In XA-mode, autocommit is handled in PGXAConnection,
!              * because it depends on whether an XA-transaction is open
!              * or not
!              */
!             if (!isXA)
!                 con.setAutoCommit(autoCommit);
          }
          catch (SQLException sqlException)
          {
Index: org/postgresql/test/xa/XADataSourceTest.java
===================================================================
RCS file: /cvsroot/jdbc/pgjdbc/org/postgresql/test/xa/XADataSourceTest.java,v
retrieving revision 1.9
diff -c -r1.9 XADataSourceTest.java
*** org/postgresql/test/xa/XADataSourceTest.java    6 Jul 2007 20:32:43 -0000    1.9
--- org/postgresql/test/xa/XADataSourceTest.java    13 Nov 2007 16:41:27 -0000
***************
*** 258,272 ****
--- 258,302 ----

          xaRes.start(xid, XAResource.TMNOFLAGS);

+         conn.createStatement().executeQuery("SELECT * FROM testxa1");
+
+         java.sql.Timestamp ts1 = getTransactionTimestamp(conn);
+
+         conn.close();
          conn = xaconn.getConnection();
          assertFalse(conn.getAutoCommit());

+         java.sql.Timestamp ts2 = getTransactionTimestamp(conn);
+
+         /* Check that we're still in the same transaction.
+          * close+getConnection() should not rollback the XA-transaction
+          * implicitly.
+          */
+         assertEquals(ts1, ts2);
+
          xaRes.end(xid, XAResource.TMSUCCESS);
          xaRes.prepare(xid);
          xaRes.rollback(xid);
          assertTrue(conn.getAutoCommit());
      }

+     /**
+      * Get transaction_timeout() from server.
+      *
+      * This can be used to check that transaction doesn't get committed/
+      * rolled back inadvertently, by calling this once before and after the
+      * suspected piece of code, and check that they match. It's a bit iffy,
+      * conceivably you might get the same timestamp anyway if the
+      * suspected piece of code runs fast enough, and/or the server clock
+      * is very coarse grained. But it'll do for testing purposes.
+      */
+     private static java.sql.Timestamp getTransactionTimestamp(Connection conn) throws SQLException
+     {
+         ResultSet rs = conn.createStatement().executeQuery("SELECT transaction_timestamp()");
+         rs.next();
+         return rs.getTimestamp(1);
+     }
+
      public void testEndThenJoin() throws XAException {
          Xid xid = new CustomXid(5);

Index: org/postgresql/xa/PGXAConnection.java
===================================================================
RCS file: /cvsroot/jdbc/pgjdbc/org/postgresql/xa/PGXAConnection.java,v
retrieving revision 1.12
diff -c -r1.12 PGXAConnection.java
*** org/postgresql/xa/PGXAConnection.java    27 Jul 2007 10:15:39 -0000    1.12
--- org/postgresql/xa/PGXAConnection.java    13 Nov 2007 16:41:28 -0000
***************
*** 81,98 ****

      public Connection getConnection() throws SQLException
      {
          Connection conn = super.getConnection();

          // When we're outside an XA transaction, autocommit
          // is supposed to be true, per usual JDBC convention.
          // When an XA transaction is in progress, it should be
          // false.
!
!         // super.getConnection rolls back any previous transaction, and resets
!         // autocommit to true, so we have to set it to false before handing the
!         // connection to the caller, if an XA transaction is active.
!         if(state == STATE_ACTIVE)
!             conn.setAutoCommit(false);

          return conn;
      }
--- 81,97 ----

      public Connection getConnection() throws SQLException
      {
+         if (logger.logDebug())
+             debug("PGXAConnection.getConnection called");
+
          Connection conn = super.getConnection();

          // When we're outside an XA transaction, autocommit
          // is supposed to be true, per usual JDBC convention.
          // When an XA transaction is in progress, it should be
          // false.
!         if(state == STATE_IDLE)
!             conn.setAutoCommit(true);

          return conn;
      }

pgsql-jdbc by date:

Previous
From: joël Winteregg
Date:
Subject: Re: XAResource implementation
Next
From: Kris Jurka
Date:
Subject: Re: XAResource implementation