[MDEV-16346] Implementing two phase commit across two maria databases Created: 2018-05-30 Updated: 2018-10-04 Resolved: 2018-10-04 |
|
| Status: | Closed |
| Project: | MariaDB Server |
| Component/s: | XA |
| Affects Version/s: | None |
| Fix Version/s: | N/A |
| Type: | Bug | Priority: | Major |
| Reporter: | Nicholas Denning | Assignee: | Unassigned |
| Resolution: | Not a Bug | Votes: | 0 |
| Labels: | None | ||
| Description |
|
I hope flagging this as a bug is appropriate. We are having a problem trying to implement two phase commit across two mariadb databases. Ultimately we want to implement 2PC across an Active MQ queue and a mariadb database but chose this approach to text it working. We are doing this from TomEE to utilise the XA manager in that platform. Connections are fine, updates are fine, but when we try to rollback the transaction the rollback seems not to work. We have changed the database connections as follows: As discussed, I have got some Java code which is using JTA transactions making updates to two JDBC data sources. These are configured as normal except that I have changed the type to “javax.sql.XADataSource”, which I believe is required for XA support. <Resource name="jdbc/ipswichdb" auth="Container" <Resource name="jdbc/sessiondb" auth="Container" Note in the above we are overriding the data source factory so we can encrypt our passwords in the context file so we have no passwords in clear in files on the server. I am calling the EJBContext method “setRollbackOnly” in one of the update methods to cause the transaction to be rolled back, yet the DB changes in both databases are not rolled back. Turning on TomEE logging shows this call being made, and that the JTA then attempts to roll back the transaction: MyBean2.updatePerson; trans status: STATUS_ACTIVE Id: [Xid:globalId=344bffffff8dffffffb16310047544d4944000000000000000000000000000000000000000000000000000,length=64,branchId=0000000000000000000000000000000000000000000000000000000000000000,length=64] Should this be supported in the DB? Is there a way to check whether the JTA command is reaching the DB, and if so what is going wrong? we are using MariaDB Java Client v2.2.2. |
| Comments |
| Comment by Andrew Hutchings (Inactive) [ 2018-05-30 ] |
|
Did you mean to file this against MariaDB ColumnStore? This appears to be a MariaDB Server / API issue. If this is misfiled let me know and I can move it for you. |
| Comment by Nicholas Denning [ 2018-05-30 ] |
|
Andrew, you are right its a standard Mariadb server / API issue only. sorry if I did something wrong in the registration process. Nick |
| Comment by Andrew Hutchings (Inactive) [ 2018-05-30 ] |
|
No problem, this has been moved to an MDEV ticket. |
| Comment by Elena Stepanova [ 2018-08-04 ] |
|
nick.denning@diegesis.co.uk, |
| Comment by Nicholas Denning [ 2018-08-09 ] |
|
We have worked out how to do this. We are using tomcat and that has the apache geronimo transaction manager included. We needed to change our data source definitions in the context entries, or example <Resource id="ipswichdb" type="javax.sql.DataSource"> alternateUsernameAllowed = true Background references are: XA transactions can be handled either by making explicit calls to JTA from the application Java code, or using J2EE container features to handle transactions implicitly. The latter approach seems preferable since J2EE provides those features, and this requires the minimum of application code. A useful reference on JTA is: https://www.progress.com/tutorials/jdbc/understanding-jta and on Container Managed Transactions (CMT): https://docs.oracle.com/javaee/6/tutorial/doc/bncij.html Declaring Classes and Methods The class containing the CMT code must be marked as an EJB class using the Stateless annotation: @Stateless The Transaction Management Type is added here for clarity, but that is actually the default. If this class is contained within another class such as a servlet, its inclusion there must be be annotated as an EJB to ensure that is is correctly initialised. @EJB private MyBean1 myBean; Methods within the class may then be annotated to describe their transactional behaviour. If no annotations are used, the transaction setting is assumed to be REQUIRED, and a transaction is started by default when a method within this class is called, and terminated when that method returns. For example: @TransactionAttribute(TransactionAttributeType.REQUIRED) public void updatePerson(PrintWriter responseWriter) { ... } REQUIRED is shown here for clarity, but is actually the default. The data source to be used within the code is accessed using a Resource annotation, referencing the project name and the resource ID used in the resource declaration as shown above. @Resource(name="IPXaTest/ipswichdb") Connection connection = dataSource.getConnection(USERNAME, PASSWORD); Statement stmt = connection.createStatement(); stmt.executeUpdate("update PERSON_DETAILS set PERSON_CMM_LEVEL = " + level + " where PERSON_LOGIN_NAME = \'developer\'"); stmt.close(); connection.close(); If calls are made to other methods which have appropriate transaction attributes, e.g. REQUIRED or SUPPORTED, then they will be enrolled in the transaction, and all updates will either be committed or rolled back together when the top level method returns. Transactions will be rolled back if a system exception is thrown, or if "setRollbackOnly" is called on the EJB context, allowing a rollback to be requested under application control. InitialContext initialContext = new InitialContext(); @Resource(name = "IPXaTest/ipconnfactory") @Resource(name = "IPXaTest/ipqueue") @TransactionAttribute(TransactionAttributeType.REQUIRED) Monitoring and Troubleshooting TransactionSynchronizationRegistry tsr = (TransactionSynchronizationRegistry) int status = tsr.getTransactionStatus(); switch (status) { case 0: result = "STATUS_ACTIVE"; break; case 1: result = "STATUS_MARKED_ROLLBACK"; break; case 2: result = "STATUS_PREPARED"; break; case 3: result = "STATUS_COMMITTED"; break; case 4: result = "STATUS_ROLLEDBACK"; break; case 5: result = "STATUS_UNKNOWN"; break; case 6: result = "STATUS_NO_TRANSACTION"; break; case 7: result = "STATUS_PREPARING"; break; case 8: result = "STATUS_COMMITTING"; break; case 9: result = "STATUS_ROLLING_BACK"; break; default: result = "Unkown"; break; }Useful logging information can be obtained if some transaction related logging levels are set in TomEE's logging.properties file, similar to the following: OpenEJB.level = FINE OpenEJB.handlers = 5tomee.org.apache.juli.FileHandler, org.apache.tomee.jul.formatter.AsyncConsoleHandler 19-Jun-2018 12:55:21.550 FINE [http-nio-8080-exec-2] org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke EjbObjectProxyHandler: invoking method updatePerson on MyBean1 with identity null 19-Jun-2018 12:55:21.597 FINE [http-nio-8080-exec-2] org.apache.openejb.core.transaction.JtaTransactionPolicy.beginTransaction TX Required: Started transaction org.apache.geronimo.transaction.manager.TransactionImpl@5a7e8d5b 19-Jun-2018 12:55:21.933 FINE [http-nio-8080-exec-2] org.apache.openejb.core.transaction.JtaTransactionPolicy.completeTransaction TX Required: Committing transaction org.apache.geronimo.transaction.manager.TransactionImpl@5a7e8d5b 19-Jun-2018 12:55:21.948 FINE [http-nio-8080-exec-2] org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke EjbObjectProxyHandler: finished invoking method updatePerson. Return value:void 19-Jun-2018 12:51:39.584 FINE [http-nio-8080-exec-2] org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke EjbObjectProxyHandler: invoking method updatePerson on MyBean1 with identity null 19-Jun-2018 12:51:39.622 FINE [http-nio-8080-exec-2] org.apache.openejb.core.transaction.JtaTransactionPolicy.beginTransaction TX Required: Started transaction org.apache.geronimo.transaction.manager.TransactionImpl@36f371bf 19-Jun-2018 12:51:40.050 FINE [http-nio-8080-exec-2] org.apache.openejb.core.transaction.JtaTransactionPolicy.setRollbackOnly TX Required: setRollbackOnly() on transaction org.apache.geronimo.transaction.manager.TransactionImpl@36f371bf 19-Jun-2018 12:51:40.051 FINE [http-nio-8080-exec-2] org.apache.openejb.core.transaction.JtaTransactionPolicy.rollbackTransaction TX Required: Rolling back transaction org.apache.geronimo.transaction.manager.TransactionImpl@36f371bf 19-Jun-2018 12:51:40.061 FINE [http-nio-8080-exec-2] org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke EjbObjectProxyHandler: finished invoking method updatePerson. Return value:void Hope this all helps. |
| Comment by Nicholas Denning [ 2018-08-09 ] |
|
All the above investigated by my colleague Dave. Happy to close. Anyone who wants more details of an example eclipse project get in touch. |
| Comment by Nicholas Denning [ 2018-08-09 ] |
|
We have tried this across ActiveMQ and Mariadb as well as just two Mariadb databases. |
| Comment by Nicholas Denning [ 2018-09-10 ] |
|
all my previous comment was there to say was that we had fixed this and given example code of what was needed. Nick |
| Comment by Elena Stepanova [ 2018-10-04 ] |
|
Thanks for writing it up. Maybe it deserves an article. |