Description
Driver version
9.2.1.0
SQL Server version
15.00.4123
Client Operating System
Mac OS X (10.15.7; amd64) (en_US)
JAVA/JVM version
java.version = 11.0.2
java.runtime = OpenJDK Runtime Environment (11.0.2+9)
Table schema
CREATE TABLE MYTABLE (ID SMALLINT NOT NULL PRIMARY KEY, STRVAL NVARCHAR(40))
Problem description
I'm working on adding support for tightly/loosely coupled transaction branches to OpenLiberty, which we have working for a couple of other JDBC drivers. Unfortunately tightly coupled is not working with the Microsoft SQL Server JDBC Driver due to a bug.
When using the SQLServerXAResource.SSTRANSTIGHTLYCPLD xa start flag for tightly coupled transaction branches, where our test case makes updates on both transaction branches, the JDBC driver's XAResource.prepare votes XA_RDONLY from the second transaction branch. Although it seems odd to indicate read only when an update was performed, this is probably okay to do if your driver implementation is internally consolidating the updates to one of the branches. In response to the XA_RDONLY on that branch, the Liberty transaction manager issues XAResource.commit with the one-phase optimization for the remaining transaction branch. The problem is that the Microsoft JDBC driver fails the one-phase optimized commit with -6 (XAER_PROTO), making the SSTRANSTIGHTLYCPLD path busted.
-
Expected behaviour:
The Microsoft JDBC driver should either return XA_OK from XAResource.prepare in above scenario
OR if you do return XA_RDONLY then Microsoft JDBC driver should honor the one-phase optimized commit on the second branch. -
Actual behaviour:
The Microsoft JDBC driver returns XA_RDONLY from XAResource.prepare for a branch upon which updates were made, and, having voted XA_RDONLY on that branch, subsequently rejects XAResource.commit(onephase) on the other branch. -
Error message/stack trace:
javax.transaction.xa.XAException: The function COMMIT: failed. The status is: -6. Error: "*** SQL_XA DTC_ERROR Context: xa_commit_entry, StatusCode=-6 (0xFFFFFFFA)"
at com.microsoft.sqlserver.jdbc.SQLServerXAResource.DTC_XA_Interface(SQLServerXAResource.java:662)
at com.microsoft.sqlserver.jdbc.SQLServerXAResource.commit(SQLServerXAResource.java:807)
...
- Any other details that can be helpful:
see comments interspersed within the following section
JDBC trace logs
OpenLiberty trace captured the exact sequence of JDBC operations leading up to the error.
==> XAConnection219c32a1.getXAResource();
<== XAResource5aadfc27
==> Connection5b41f93c.getTransactionIsolation();
<== 4
==> Connection5b41f93c.getTransactionIsolation();
<== 4
==> Connection5b41f93c.clearWarnings();
<== clearWarnings
==> XAResource5aadfc27.start({XidImpl: formatId(57415344), gtrid_length(36), bqual_length(54), data(00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e000000010000000000000000000000000001)}, 32768);
<== start
==> Connection5b41f93c.createStatement(1003, 1007);
<== Statementa0c1b556
==> Statementa0c1b556.executeUpdate("INSERT INTO MYTABLE VALUES (32, 'thirty-two')");
<== 1
==> XAConnectionef960d1a.getXAResource();
<== XAResource7b5716db
==> Connectiona36df11c.getTransactionIsolation();
<== 4
==> Connectiona36df11c.getTransactionIsolation();
<== 4
==> Connectiona36df11c.clearWarnings();
<== clearWarnings
==> XAResource7b5716db.start({XidImpl: formatId(57415344), gtrid_length(36), bqual_length(54), data(00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e000000010000000000000000000000000002)}, 32768);
<== start
==> Connectiona36df11c.createStatement(1003, 1007);
<== Statement2eb0b291
==> Statement2eb0b291.executeUpdate("UPDATE MYTABLE SET STRVAL='XXXII' WHERE ID=32");
<== 1
==> Statement2eb0b291.close();
<== close
==> Statementa0c1b556.close();
<== close
==> XAResource7b5716db.end({XidImpl: formatId(57415344), gtrid_length(36), bqual_length(54), data(00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e000000010000000000000000000000000002)}, 67108864);
<== end
==> XAResource5aadfc27.end({XidImpl: formatId(57415344), gtrid_length(36), bqual_length(54), data(00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e000000010000000000000000000000000001)}, 67108864);
<== end
==> XAResource7b5716db.prepare({XidImpl: formatId(57415344), gtrid_length(36), bqual_length(54), data(00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e000000010000000000000000000000000002)});
<== 3
The value of 3 above is XAResource.XA_RDONLY. Even though we wrote data in this transaction branch, the Microsoft JDBC Driver votes read only. Maybe the Microsoft driver has really done both of the updates under the same branch? In that case, it is probably fine, but the XA_RDONLY leads the transaction manager into believing that it should go ahead and commit the other transaction branch using the one-phase optimization -- because there is only one resource left and it hasn't prepared yet -- but the JDBC driver fails this with -6 (XAER_PROTO), which I suspect is the bug here:
==> XAResource5aadfc27.commit({XidImpl: formatId(57415344), gtrid_length(36), bqual_length(54), data(00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e00000178661629ec000000011858a7b8ff430098135991e26fa8884c901b8a930d74016e000000010000000000000000000000000001)}, true);
<== javax.transaction.xa.XAException: The function COMMIT: failed. The status is: -6. Error: "*** SQL_XA DTC_ERROR Context: xa_commit_entry, StatusCode=-6 (0xFFFFFFFA)"
at com.microsoft.sqlserver.jdbc.SQLServerXAResource.DTC_XA_Interface(SQLServerXAResource.java:662)
at com.microsoft.sqlserver.jdbc.SQLServerXAResource.commit(SQLServerXAResource.java:807)
...
Reproduction code
I have created an automated test in OpenLiberty, named testTransactionBranchesTightlyCoupled
, that reproduces the failure: OpenLiberty/open-liberty@24e0d49
In order to merge it, I temporarily disabled it by issuing a rollback rather than a commit, but you can easily switch it back on by changing,
// TODO switch to commit once Microsoft bug is fixed
tran.rollback();
to
tran.commit();
Command to run the test (after building OpenLiberty & also requires docker),
./gradlew com.ibm.ws.jdbc_fat_sqlserver:buildandrun -Dfat.test.class.name=com.ibm.ws.jdbc.fat.sqlserver.SQLServerTest -Dfat.test.method.name=testTransactionBranchesTightlyCoupled
While we could in theory write a standalone test based on the above, it would not be using a real transaction manager, so running the test within OpenLiberty seems preferable.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status