我有一个 Java/WebSphere 应用程序,它正在与 SQL Server 2008 实例执行 XA 事务。在某些环境中,一切都按预期运行。在我们的两个环境中,事务会间歇性失败。
有关环境的一些信息:
- 应用程序服务器是运行 WebSphere 8.5.5.3 的 Linux VM。它使用 SQL Server JDBC 驱动程序 4.0 版。数据源配置为 XA。
- 数据库服务器是运行 SQL Server 2008 的 Windows VM
- 一切是否正常运转似乎取决于数据库服务器上的数据库。在损坏的环境中,我可以配置应用程序服务器以使用损坏环境中的数据库,但一切仍将无法正常工作。反之亦然 - 如果我将损坏环境中的应用程序服务器配置为使用工作环境中的数据库服务器,则应用程序将正常运行。
在应用程序服务器上,当事务失败时,我会看到如下堆栈跟踪:
com.microsoft.sqlserver.jdbc.SQLServerException: Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:216)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1515)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:404)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:350)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:5696)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:1715)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:180)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:155)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeUpdate(SQLServerPreparedStatement.java:314)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.pmiExecuteUpdate(WSJdbcPreparedStatement.java:1187)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.executeUpdate(WSJdbcPreparedStatement.java:804)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:186)
当我查看数据库服务器上的 MSDTC 日志时,我看到以下内容:
time=12/22/2015-10:47:48.611 eventid=RM_ENLISTED_IN_TRANSACTION tx_guid=690a94a2-a060-4eb9-8966-ef25b0fa001b resource manager #1001 enlisted as transaction enlistment #1. RM guid = '280f3497-9cc1-4689-b612-7a08cce82e2b'
time=12/22/2015-10:47:57.612 eventid=ABORT_DUE_TO_TRANSACTION_TIMER_EXPIRED tx_guid=690a94a2-a060-4eb9-8966-ef25b0fa001b transaction timeout expired
time=12/22/2015-10:47:57.612 eventid=TRANSACTION_ABORTING tx_guid=690a94a2-a060-4eb9-8966-ef25b0fa001b transaction is aborting
time=12/22/2015-10:47:57.612 eventid=RM_ISSUED_ABORT tx_guid=690a94a2-a060-4eb9-8966-ef25b0fa001b abort request issued to resource manager #1001 for transaction enlistment #1
time=12/22/2015-10:47:57.612 eventid=RM_ACKNOWLEDGED_ABORT tx_guid=690a94a2-a060-4eb9-8966-ef25b0fa001b received acknowledgement of abort request from the resource manager #1001 for transaction enlistment #1
time=12/22/2015-10:47:57.612 eventid=TRANSACTION_ABORTED tx_guid=690a94a2-a060-4eb9-8966-ef25b0fa001b transaction has been aborted
我始终看到 ABORT_DUE_TO_TRANSACTION_TIMER_EXPIRED 事件在 RM_ENLISTED_IN_TRANSACTION 事件发生 9 秒后发生。问题是数据库服务器上的 MSDTC 事务超时配置为 60 秒。我尝试更改此超时,但完全不影响行为。我也没有看到 WebSphere 中任何与 9 秒间隔匹配的事务超时设置。这个超时来自哪里?我该如何更改它?
答案1
我们找到了问题所在。我们有另一个非 WebSphere 应用程序,该应用程序使用 Atomikos 进行 JTA 事务。除非您设置 com.atomikos.icatch.default_jta_timeout 属性,否则 Atomikos 默认为其事务设置 10 秒超时。Atomikos 在参与事务的所有 XAResource 实例上设置此超时。出于某种原因,这导致 10 秒超时全局应用于使用该 MSDTC 实例的所有事务。还值得注意的是,WebSphere 的事务管理器不会在参与的 XAResource 实例上调用 setTimeout,这可能是另一个应用程序影响这些事务的部分原因。重新启动 MSDTC 似乎可以清除该全局超时,直到 Atomikos 事务再次运行。
可能值得注意的是,其他 JTA 事务管理器(如 Bitronix)在使用 XA 访问 SQL Server 时可能会遇到同样的问题。此问题也可能特定于 SQL Server JDBC Driver 版本 4.0,并且可能会在更高版本中修复。但是,我无法测试这些语句。
如果您碰巧遇到同样的问题,您应该能够使用以下步骤进行验证:
- 重新启动 MSDTC
- 在 WebSphere 应用程序中运行事务。它们绝不会因超时而失败。尝试多个事务 - 足以确保一切正常。
- 使用您的 Atomikos 应用程序运行交易。
- 再次与 WebSphere 运行事务。如果事务超过 10 秒,则它现在应该会失败并被 MSDTC 中止。
- 重新启动 MSDTC
- 再次与 WebSphere 运行事务。即使超过 10 秒,也不再会出现超时错误。