はじめに
2フェーズ・コミット(2 phase commit, 以下、2pc)を利用する場合は、Liberty の transaction タグの acceptHeuristicHazard 属性を false に変更しておいた方が安全だというお話です。
2PC ってなに?
はじめに、2PC に関して、簡単に説明しておきます。
2PC は、複数のリソース・マネージャー(Resource Manager, 以下、RM)に対して一貫性をもって更新を行うための仕組みです。RM と書きましたが、分かりやすく言うと、データベース(DB)やメッセージ指向ミドルウェア(MOM)などのことになります。
Liberty は、2PC において、トランザクション・マネージャー(Transaction Manager, 以下、TM)の役割を担い、トランザクションの一貫性を保つ役割を果たします。
例えば、Liberty で稼働するアプリケーションが、DB にデータを insert し、MOM にメッセージを put する処理を1つのトランザクションとして実行する場合を図示すると、次のようになります。Liberty の右側にあるディスクの絵は、TM が 2PC トランザクションの状況を記録するために使用するトランザクション・ログを示しています。
アプリケーション、TM (Liberty の一部)、トランザクション・ログ、2つの RM が出てきました。2PC トランザクションのフローを分かりやすくシーケンス図的に示すと次のようになります。2PC の詳細なフローはより複雑ですが、必要な部分だけを描いてあります。
- アプリケーションがトランザクションを開始します。
- アプリケーションが DB にデータを insert します。
- アプリケーションが MOM にメッセージを put します。
- アプリケーションがトランザクションの commit を要求します。
- TM が MOM と DB に prepare を要求し、各々の prepare が完了します。
- TM がトランザクション・ログにトランザクションの prepare 完了を記録します。
- TM が DB と MOM に commit を要求し、各々の commit が完了します。
- トランザクションが完了します。
上記のシーケンス図と説明にあるように、トランザクションの commit 処理が内部的に prepare と commit の2つのフェーズに分かれているので、2フェーズ・コミット(2 phase commit) と呼ばれています。
重要な点は、「TM がトランザクション・ログにトランザクションの prepare 完了を記録」した時点でトランザクションの commit が確定し、そのあとに TM や RM で障害が発生しても、リカバリー処理でトランザクションは必ず commit される点です。逆に、 prepare 完了を記録する前に TM で障害が発生すると、リカバリー処理でトランザクションは rollback されることになります。
2PC にならないリソース定義の例
リソース・マネージャーとして、IBM Db2 と IBM MQ を使用した 2PC を例として考えてみます。(アプリケーションは、UserTransaction 等のインターフェースを利用して、2PC 用に実装されている前提とします。)
Liberty の server.xml で、IBM Db2 用のデータソースと IBM MQ 用の接続ファクトリーを定義することになりますが、以下のような定義になっていると、2PC にはなりません。2PC にはならないですが、トランザクションは問題なく完了し、あたかも、2PC が行われているように見えてしまいます。
<featureManager>
<feature>webProfile-8.0</feature>
<feature>jms-2.0</feature>
<feature>wmqJmsClient-2.0</feature>
</featureManager>
<jdbcDriver id="DB2Driver">
<library><fileset dir="/IBM/DB2Driver" includes="db2jcc4.jar"/></library>
</jdbcDriver>
<dataSource id="SampleDS"
jndiName="jdbc/sample"
jdbcDriverRef="DB2Driver">
<containerAuthData user="db2inst1" password="?????"/>
<recoveryAuthData user="db2inst1" password="?????"/>
<properties.db2.jcc databaseName="sample" serverName="db2-host" portNumber="50000"/>
</dataSource>
<variable name="wmqJmsClient.rar.location" value="/IBM/MQRA/wmq/wmq.jmsra.rar"/>
<jmsConnectionFactory id="jms_cf_qmtest"
jndiName="jms/cf_qmtest">
<containerAuthData user="admin" password="?????"/>
<recoveryAuthData user="admin" password="?????"/>
<properties.wmqJms channel="????" hostName="mq-host" queueManager="QM1"/>
</jmsConnectionFactory>
直ぐに気が付いた方も多いとは思いますが、jdbc-4.2 フィーチャー(webProfile-8.0 フィーチャーに含まれる)を使用する構成で、dataSource タグの type 属性に "javax.sql.XADataSource" が指定されておらず、XA データソース(2PC 用のデータソース)になっていない点が問題です。
このため、MQ に対しては 2PC の手順でコミットが行われますが、Db2 に対しては 1PC のコミットになってしまします。
シーケンス図的に示すと、次のようになると考えられます。
「DB に Commit」と「prepare 完了を記録」の間で TM である Liberty で障害が発生すると、トランザクションに不整合が生じることになります。つまり、Db2 のトランザクションは既にコミットされていますが、MQ のトランザクションは Liberty の回復時にロールバックされてしまいます。
残念なことに、このようなリソース定義になっていても、Liberty のログには警告メッセージなどは出力されません。
XA データソースを使用しなければならないことを認識していても、server.xml の修正時に誤って削除してしまったり、単純に指定するのを忘れていても、更には、dataSource タグの type 属性に誤って "javax.sql.ConnectionPoolDataSource" を指定しても、全く気が付かないことになります。
transaction タグの acceptHeuristicHazard 属性
transaction タグのacceptHeuristicHazard 属性のデフォルト値は true 1 で、「2PC トランザクションに1つだけ 1PC リソースを含めることを許容する」動作となります。このため、先ほど示したリソース定義でもエラーにならず、あたかも 2PC が行われているような挙動となるわけです。
acceptHeuristicHazard 属性を false に設定すると、2PC トランザクションに 1PC リソースを含めることができなくなります。
<transaction acceptHeuristicHazard="false"/>
正確に言うと、acceptHeuristicHazard 属性を false に設定すると、2PC トランザクションに 1PC リソースを参加させ、トランザクションをコミットしようとすると、Liberty のログに以下のエラー・メッセージが出力され、コミットが失敗するようになります。
E WTRN0063E: 既存の 2 フェーズ可能リソースと一緒に 1 フェーズ可能リソースをコミットしようとする不正な試みが行われました。
E WTRN0086I: トランザクション 0000019C6A90CE48000000012A37A26800010203040506070809000102030405060708090000019C6A90CE48000000012A37A268000102030405060708090001020304050607080900000001 の準備フェーズで XAException が検出されました。 ローカル・リソースがこの後に続きます。
I WTRN0089I: LocalTransactionWrapper@:67cda58f localTransaction:com.ibm.ws.rsadapter.impl.WSRdbSpiLocalTransactionImpl@e1ee7aff enlisted:trueHas Tran Rolled Back = false registeredForSynctruemcWrapper.hashcode()-563596061: 評定: none
I WTRN0089I: XATransactionWrapper@ 77aefaf3 XAResource: com.ibm.mq.connector.xa.XARWrapper@919b420 enlisted: trueHas Tran Rolled Back = false mcWrapper.hashCode()-2008945398: 評定: commit
I FFDC1015I: FFDC 発生事象が作成されました: "javax.transaction.RollbackException com.ibm.tx.jta.impl.TransactionImpl.prepareResources 1505" ロケーション: ffdc_26.02.15_16.46.41.0.log
Liberty のエラー・メッセージ:詳細(クリックで展開)
[2026/02/15 16:46:41:714 JST] 00000050 com.ibm.tx.jta.impl.RegisteredResources E WTRN0063E: 既存の 2 フェーズ可能リソースと一緒に 1 フェーズ可能リソースをコミットしようとする不正な試みが行われました。
[2026/02/15 16:46:41:715 JST] 00000050 com.ibm.tx.jta.impl.RegisteredResources E WTRN0086I: トランザクション 0000019C6A90CE48000000012A37A26800010203040506070809000102030405060708090000019C6A90CE48000000012A37A268000102030405060708090001020304050607080900000001 の準備フェーズで XAException が検出されました。 ローカル・リソースがこの後に続きます。
[2026/02/15 16:46:41:716 JST] 00000050 com.ibm.ws.Transaction.JTA.JTAResourceBase I WTRN0089I: LocalTransactionWrapper@:67cda58f localTransaction:com.ibm.ws.rsadapter.impl.WSRdbSpiLocalTransactionImpl@e1ee7aff enlisted:trueHas Tran Rolled Back = false registeredForSynctruemcWrapper.hashcode()-563596061: 評定: none
[2026/02/15 16:46:41:716 JST] 00000050 com.ibm.ws.Transaction.JTA.JTAResourceBase I WTRN0089I: XATransactionWrapper@ 77aefaf3 XAResource: com.ibm.mq.connector.xa.XARWrapper@919b420 enlisted: trueHas Tran Rolled Back = false mcWrapper.hashCode()-2008945398: 評定: commit
[2026/02/15 16:46:41:781 JST] 00000050 com.ibm.ws.logging.internal.impl.IncidentImpl I FFDC1015I: FFDC 発生事象が作成されました: "javax.transaction.RollbackException com.ibm.tx.jta.impl.TransactionImpl.prepareResources 1505" ロケーション: ffdc_26.02.15_16.46.41.0.log
このように、acceptHeuristicHazard 属性を false に設定しておくと、アプリケーションの稼働確認テストなどで 2PC 構成の誤りに容易に気付くことができるようになります。
ちなみに、WAS traditonal では、acceptHeuristicHazard 属性に該当する「ヒューリスティック障害を容認」2 のデフォルト値はアンチェック(false)です。
dataSource タグの type 属性を指定していない時
データソースの種類は、dataSource タグの type 属性で指定できますが、この属性が指定されていない場合は、jdbc のフィーチャーなどに依存し、次の順で使用可能なデータソースが利用されます。3
つまり、jdbc フィーチャーのバージョンによって、type 属性を指定していない場合に利用されるデータソースの種類が変わることになります。
- jdbc-4.3 以上、または、デフォルト・データソースの場合
- javax.sql.XADataSource
- javax.sql.ConnectionPoolDataSource
- javax.sql.DataSource
- jdbc-4.2 以下で、デフォルト・データソースでない場合
- javax.sql.ConnectionPoolDataSource
- javax.sql.DataSource
- javax.sql.XADataSource
最初に示した server.xml の例では、jdbc-4.2 フィーチャー(webProfile-8.0 フィーチャーに含まれる)を使用していて、type 属性を指定していないので、"javax.sql.ConnectionPoolDataSource" が利用され、不適切な 2PC 用の構成になっていました。
しかしながら、jdbc-4.3 フィーチャー以降では、"javax.sql.XADataSource" が利用され、最初に示した server.xml のデータソース定義でも問題ないことになります。
いずれにしろ、acceptHeuristicHazard 属性を false にしておくと、不適切な 2PC 構成が回避できる点は変わりありません。更に、dataSource タグの type 属性は必ず指定しておいた方が良いと思われます。
ちなみに、デフォルト・データソース 4 は、id 属性に "DefaultDataSource" が指定されているデータソースのことですが、あまり使用されていないと思います。
終わりに
2PC を利用する場合は、Liberty の transaction タグの acceptHeuristicHazard 属性は false に変更しておきましょう。リソースが 2PC 用に適切に構成されていなくても、直ぐに気が付けるはずです。
この情報が何かのお役に立てば幸いです。
-
transactionタグのacceptHeuristicHazard属性に関しては、下記のドキュメントを参照
・Open Liberty: Transaction Manager (transaction)
・WAS Liberty: トランザクション・マネージャー (transaction) ↩ -
WAS traditonal の「ヒューリスティック障害を容認」に関しては、下記のドキュメントを参照
・WAS traditonal: トランザクション・サービス設定 ↩ -
dataSourceタグのtype属性を指定していないときに利用されるデータソースに関しては、下記のドキュメントを参照
・Open Liberty: Relational database connections with JDBC
・WAS Liberty: Liberty でのリレーショナル・データベース接続の構成 ↩ -
デフォルト・データソースに関しては、下記のドキュメントを参照
・Open Liberty: Relational database connections with JDBC
・WAS Liberty: デフォルト・データ・ソースの構成 ↩


