【问题标题】:How to simulate database failure to test 2-phase commit in Java如何模拟数据库失败以在 Java 中测试两阶段提交
【发布时间】:2012-04-23 22:00:21
【问题描述】:

我正在实施涉及分布式资源的两阶段提交。如何模拟参与数据库的故障?拔网线不起作用,因为它会导致表死锁。我目前在我的应用程序代码中使用钩子,它们在查询执行之前和查询执行之后的不同点抛出StaleConnectionException。我对这种方法的担忧是:

  • 有没有更好的方法来模拟数据库故障?
  • 当数据库连接变坏时,连接对象会发生什么?它是保留其价值还是变为 null?
  • 当应用程序尝试重新连接到 DB 时实际发生了什么?连接对象得到什么值?它是否使用连接池中的现有值?

我还想在中间点进行测试,例如在查询执行期间、提交期间(发送 prepare 之后等)。现在我将应用程序置于调试模式并进入函数调用并在两者之间插入插件。但这种方法是手动的,不适用于规模测试。

是否有模拟器/模拟器或工具可以帮助我做到这一点?

【问题讨论】:

  • 您的目标是任何特定的数据库,还是需要为任何 JDBC 连接的数据库提供通用解决方案?
  • 我现在正在使用 DB2 和 DB2 z/OS。
  • 安迪,你选择了哪种模拟数据库故障的方法?
  • @dmiandre:这是某个时候回来的,所以 dnt 真的记得..但我认为我所做的一种方法是确保第二个数据库上的查询出现错误(不正确的表名或其他东西) ..因此,当第一个到达提交阶段时,第二个查询确实失败了。我的主要目标是使两阶段提交失败,所以这行得通!将尝试挖掘旧项目,看看是否可以找到任何其他使用的方法。
  • 我一直在互联网上搜索以找到测试此类案例的最佳实践,但似乎没有人对其进行测试。我唯一找到的是来自 JBoss 的 Byteman。它是一种无需更改代码即可注入故障的工具。

标签: java database transactions websphere 2phase-commit


【解决方案1】:

这是很多问题:) 我将尝试完成以前的答案。

Is there a better way to simulate the DB failure?

测试所有情况很复杂。测试主要案例的一种方法是创建一个 JCA 连接器(数据库驱动程序 is 是一个 JCA 连接器)。您可以从将在事务中登记的连接器(第三个参与者)获取连接。然后连接可能会引发某些错误。

三个部分协同工作:(1) 应用程序,(2) 应用程序。服务器的事务管理器,以及 (3) jca 连接器(所谓的资源适配器)。

连接通过ManagedConnection.getXAResource 将自身挂接到事务中。使用自定义 jca 连接器,您可以向应用程序(图片中的Connection)或应用程序服务器的事务管理器(通过图片中的ManagedConnection 获得的XAResource)引发异常。您可以在XAResource.prepareXAResource.commit 期间抛出异常,这对应于错误期间 2 阶段提交。

请注意,参与者的入伍顺序很难控制(请参阅this question)。所以很容易测试prepare 之一失败(即你的),但很难控制它们被调用的顺序。重现 2 阶段提交的所有可能的无效状态是复杂的,尤其是在进行优化时。

(我曾经写过一个 JCA 连接器 (http://code.google.com/p/txfs),周围还有其他的,如果你想要示例代码的话。)

What happens to the connection object when DB connection goes bad? 
Does it retain its value or does it become null?

ManagedConnection 可以通知事务管理器。其中一个通知是ConnectionEvent.CONNECTION_ERROR_OCCURRED,通知它在使用此特定连接时发生错误。

如其他答案所述,通常每个事务关联一个托管连接。托管连接抽象了物理连接,你不想用太多。应用程序只获得“句柄”(图片中的Connection)。在一个给定事务中获得的句柄都指向同一个托管连接。这是大多数应用服务器支持的优化。

如果托管连接无效,则使用它的句柄也会无效。但是手柄不能“刷新”AFAIK。事务必须回滚,托管连接被破坏。当另一个事务启动时,它将与池中的另一个有效托管连接相关联。

What actually happens when application tries to reconnect to DB?
What value does connection object get?
Does it use an existing value from the connection pool?

应用服务器管理一个托管连接池。如上一段所述,使用时可能会变坏。但是一个人也可能在没有被使用的情况下变坏。例如,池中已使用的托管连接可能会因为底层物理连接超时而变得无效。应用服务器通常具有在开始使用托管连接之前测试托管连接是否有效的功能。如果不是,它将尝试从池中创建另一个托管连接,或者创建一个新连接。

【讨论】:

  • 感谢您提供如此详细的解释。我还没有时间测试这个。一旦我测试它,我会投票,bt仍然thnx 4这样详细的解释
  • 希望对您有所帮助。你想做的事情并不容易。 (正如@nsfyn55 所写,存在“极高的成本/极低的回报”)
【解决方案2】:

您可能可以添加自己的资源来参与提交,并在第一阶段之后暂停事务。同时你可以“拔掉插头”。

【讨论】:

  • 我无法添加比现有资源更多的资源。此外,我无法控制它们的运行,因为它们是开发数据库,​​所以我无法停止和启动它们。
  • 我认为 Andrej 的意思是争取另一个(虚拟)XAResource,它会在准备和提交之间触发某种失败。正确的方法是创建一个资源适配器。您也可以尝试直接从您的应用程序中征用 XAResource,但我认为 WebSphere 不允许通过标准 JTA API 进行此操作(请注意,通常 J2EE 无论如何都不允许这样做)。您将需要使用特定于 WebSphere 的 API(因为 WebSphere 要求您在登记 XAResource 时生成所谓的“恢复令牌”)。
【解决方案3】:

Andrej 回答了问题的一部分,所以让我回答第二部分。

您在应用程序中获得的 Connection 对象只是物理连接的包装。该包装器在连接池和事务管理中发挥作用。如果数据库出现任何问题,连接包装器将无法使用,您只能回滚。这是有道理的,因为您只能在 2PC 启动之前访问连接,并且在 2PC 启动之前所做的任何事情都无法恢复。

请注意,尝试释放连接并获取新连接不会改变任何事情,因为一旦在事务中使用了来自给定数据源的连接,您将始终从该数据源获得相同的连接,只要你在同一个交易中。这意味着您的应用程序无法在不重新启动整个事务的情况下“重新连接”。

另一方面,如果在所有资源都准备好之后但在所有资源提交之前出现问题,则事务管理器有责任恢复事务。但这发生在幕后,您的应用程序无法控制。同样在这一点上,您的应用程序应该已经释放了该事务中使用的所有连接。

【讨论】:

  • 谢谢详细解释。在我的情况下发生的是:作为事务的一部分,我在两个数据库上执行一个查询,就在它即将提交时,我拔掉了插头。应用程序抛出“javax.transaction.HeuristicMixedException”,然后尝试回滚。现在,当 TM(在我的例子中为 Websphere)尝试执行回滚时,它会收到以下异常:“发生 XAException。错误代码是:XAER_NOTA (-4) ERRORCODE=-4228, SQLSTATE=null”。那么我的问题是:如果从未调用过提交,则从未发送过准备。TM 是否仍应调用回滚?
  • 另外,我的jst方法是否足以模拟数据库故障,或者我应该释放连接?
【解决方案4】:

您最好的选择可能是在内存数据库中使用。调用失败,检查前后数据源的状态,确保回滚/提交正确执行。

至于您的其他顾虑,这些似乎是极高成本/低回报的测试。阅读您的供应商文档并确保您的事务环境配置正确。完成此操作后,您可能应该将其自动化,以便放手。

除非您编写自己的 2PC 协议特定事务管理器 + 数据库实现,否则我会将这些功能的测试留给您的供应商。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-25
    • 2012-07-20
    • 2015-02-28
    • 2012-09-22
    • 1970-01-01
    • 1970-01-01
    • 2020-11-17
    相关资源
    最近更新 更多