Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

第11章 Java トランザクション API (JTA)

11.1. 概要

11.1.1. Java トランザクション API (JTA) の概要

はじめに

これらのトピックは、 Java トランザクション API (JTA) の基礎的な内容について取り上げます。

11.2. トランザクションの概念

11.2.1. トランザクション

トランザクションは 2 つ以上のアクションで構成されており、アクションすべてが成功または失敗する必要があります。成功した場合はコミット、失敗した場合はロールバックが結果的に実行されます。ロールバックでは、トランザクションがコミットを試行する前に、各メンバーの状態が元の状態に戻ります。

よく設計されたトランザクションの通常の標準は Atomic, Consistent, Isolated, and Durable (ACID) です。

11.2.2. トランザクションの ACID プロパティー

ACID は 原子性 (Atomicity)、一貫性 (Consistency)、独立性 (Isolation)、永続性 (Durability) の略語です。通常、この用語はデータベースやトランザクション操作において使用されます。

原子性 (Atomicity)
トランザクションの原子性を保つには、すべてのトランザクションメンバーが同じ決定を行う必要があります。これらのメンバーはコミットまたはロールバックを行います。原子性が保たれない場合の結果はヒューリスティックな結果と呼ばれます。
一貫性
一貫性とは、データベーススキーマの観点から、データベースに書き込まれたデータが有効なデータであることを保証するという意味です。データベースあるいは他のデータソースは常に一貫した状態でなければなりません。一貫性のない状態の例には、操作が中断される前にデータの半分が書き込まれてしまったフィールドなどがあります。すべてのデータが書き込まれた場合や、書き込みが完了しなかった時に書き込みがロールバックされた場合に、一貫した状態となります。
独立性 (Isolation)
独立性とは、トランザクションのスコープ外のプロセスがデータを変更できないように、トランザクションで操作されたデータが変更前にロックされる必要があることを意味します。
永続性 (Durability)
永続性とは、トランザクションのメンバーにコミットの指示を出してから外部で問題が発生した場合、問題が解決されると全メンバーがトランザクションのコミットを継続できるという意味です。ここで言う問題とは、ハードウェア、ソフトウェア、ネットワークなどのシステムが関係する問題のことです。

11.2.3. トラザクションコーディネーターまたはトランザクションマネージャー

JBoss EAP のトランザクションでは、トランザクションコーディネーターとトランザクションマネージャー (TM) という言葉は、ほとんど同じことを意味します。トランザクションコーディネーターという言葉は通常、分散 JTS トランザクションのコンテキストで使用されます。

JTA トランザクションでは、TM は JBoss EAP 内で実行され、2 フェーズコミットのプロトコルでトランザクションの参加者と通信します。

TM はトランザクションの参加者に対して、他のトランザクションの参加者の結果に従い、データをコミットするか、ロールバックするか指示します。こうすることで、確実にトランザクションが ACID 標準に準拠するようにします。

11.2.4. トランザクションの参加者

トランザクションの参加者は、状態をコミットまたはロールバックできるトランザクション内のリソースであり、一般的にデータベースまたは JMS ブローカーを生成します。ただし、トランザクションインターフェースを実装することにより、ユーザーコードがトランザクションの参加者として動作することもできます。トランザクションの各参加者は、状態をコミットまたはロールバックできるかどうかを独自に決定します。すべての参加者がコミットできる場合のみ、トランザクション全体が成功します。コミットできない参加者がある場合は、各参加者がそれぞれの状態をロールバックし、トランザクション全体が失敗します。TM は、コミットおよびロールバック操作を調整し、トランザクションの結果を判断します。

11.2.5. Java Transactions API (JTA)

Java Transactions API (JTA) は Java Enterprise Edition 仕様の一部で、JSR-907 に定義されています。

JTA の実装は、JBoss EAP アプリケーションサーバーの Narayana プロジェクトに含まれる TM を使用して実行されます。TM により、単一のグローバルトランザクションを使用してアプリケーションがさまざまなリソース (データベースや JMS ブローカーなど) を割り当てることができるようになります。グローバルトランザクションは XA トランザクションと呼ばれます。一般的に、このようなトランザクションには XA 機能を持つリソースが含まれますが、XA 以外のリソースをグローバルトランザクションに含めることもできます。XA 以外のリソースを XA 対応リソースとして動作させるのに役に立つ複数の最適化があります。詳細については、1 フェーズコミットの LRCO 最適化 を参照してください。

本書では、JTA という用語は以下の 2 つのことを意味します。

  1. Java EE 仕様で定義された Java トランザクション API
  2. TM がトランザクションをどのように処理するかを示します。

    TM は JTA トランザクションモードで動作し、データはメモリーによって共有されます。また、トランザクションコンテキストはリモート EJB 呼び出しによって転送されます。JTS モードでは、データは CORBA (Common Object Request Broker Architecture) メッセージを送信して共有され、トランザクションコンテキストは IIOP 呼び出しによって転送されます。複数の JBoss EAP サーバー上におけるトランザクションの分散は両方のモードでサポートされます。

11.2.6. Java Transaction Service (JTS)

Java Transaction Service (JTS) は、Object Transaction Service (OTS) と Java のマッピングです。Java EE アプリケーションは JTA API を使用してトランザクションを管理します。JTA API はトランザクションマネージャーが JTS モードに切り替わったときに JTS トランザクション実装と対話します。JTS は IIOP プロトコル上で動作します。JTS を使用するトランザクションマネージャーは Object Request Broker (ORB) と呼ばれるプロセスと Common Object Request Broker Architecture (CORBA) と呼ばれる通信標準を使用してお互いに通信します。詳細については、JBoss EAP Configuration GuideORB Configuration を参照してください。

アプリケーションの観点で JTA API を使用すると、JTS トラザクションは JTA トランザクションと同じように動作します。

注記

JBoss EAP に含まれる JTS の実装は、分散トランザクションをサポートします。完全準拠の JTS トランザクションとの違いは、外部のサードパーティー ORB との相互運用性です。この機能は、JBoss EAP ではサポートされません。サポートされる設定では、複数の JBoss EAP コンテナーでのみトランザクションが分散されます。

11.2.7. XA リソースおよび XA トランザクション

XA は eXtended Architecture を表し、複数のバックエンドデータストアを使用するトランザクションを定義するために X/Open Group によって開発されました。XA 標準は、グローバル TM とローカルリソースマネージャーとの間のインターフェースを定義します。XA では、4 つの ACID プロパティーすべてを保持しながらアプリケーションサーバー、データベース、キャッシュ、メッセージキューなどの複数のリソースが同じトランザクションに参加できるようにします。4 つの ACID プロパティーの 1 つは原子性であり、これは参加者の 1 つが変更のコミットに失敗した場合に他の参加者がトランザクションを中止し、トランザクションが発生する前の状態に戻すことを意味します。XA リソースは XA グローバルトランザクションに参加できるリソースです。

XA トランザクションは、複数のリソースにまたがることができるトランザクションです。これには、コーディネートを行う TM が関係します。この TM は、すべてが 1 つのグローバル XA トランザクションに関与する 1 つ以上のデータベースまたは他のトランザクションリソースを持ちます。

11.2.8. XA リカバリー

TM は X/Open XA 仕様を実装し、複数の XA リソースで XA トランザクションをサポートします。

XA リカバリーは、トランザクションの参加者であるリソースのいずれかがクラッシュしたり使用できなくなったりしても、トランザクションの影響を受けたすべてのリソースが確実に更新またはロールバックされるようにするプロセスのことです。JBoss EAP の範囲内では、XA データソース、JMS メッセージキュー、JCA リソースアダプターなどの XA リソースまたはサブシステムに対して、transactions サブシステムが XA リカバリーのメカニズムを提供します。

XA リカバリーはユーザーの介入がなくても実行されます。XA リカバリーに失敗すると、エラーがログ出力に記録されます。サポートが必要な場合は、Red Hat グローバルサポートサービスまでご連絡ください。XA リカバリープロセスは、デフォルトで 2 分ごとに開始される定期リカバリースレッドにより開始されます。定期リカバリースレッドにより、未完了のすべてのトランザクションが処理されます。

注記

不明なトランザクションのリカバリーを完了するには 4〜8 分かかることがあります。これはリカバリープロセスを複数回実行する必要がある場合があるためです。

11.2.9. XA リカバリープロセスの制限

XA リカバリーには以下の制限があります。

トランザクションログが正常にコミットされたトランザクションから消去されないことがある

XAResource のコミットメソッドが正常に完了し、トランザクションをコミットしてからコーディネーターがログをアップデートするまでに JBoss EAP サーバーがクラッシュすると、サーバーの再起動時に以下の警告メッセージが表示されることがあります。

ARJUNA016037: Could not find new XAResource to use for recovering non-serializable XAResource XAResourceRecord

これは、リカバリー時に JBoss トランザクションマネージャー (TM) はログのトランザクション参加者を確認し、コミットを再試行しようとするからです。最終的に、JBoss TM はリソースがコミットされたと見なし、コミットを再試行しなくなります。このような場合、トランザクションはコミットされデータの損失はないため、警告を無視しても問題ありません。

警告が表示されないようにするには、com.arjuna.ats.jta.xaAssumeRecoveryComplete プロパティーの値を true に設定します。このプロパティーは、登録された XAResourceRecovery インスタンスから新しい XAResource インスタンスが見つからないとチェックされます。true に設定すると、リカバリーで、以前のコミットの試行が成功したと見なされ、これ以上リカバリーを試行しなくてもインスタンスをログから削除できます。このプロパティーはグローバルであり、適切に使用しないと XAResource インスタンスがコミットされていない状態のままになるため、注意して使用する必要があります。

注記

JBoss EAP 7.0 には、トランザクションが正常にコミットされた後にトランザクションログを消去する拡張機能が実装されているため、上記の状況は頻繁に発生しません。

XAResource.prepare() の最後にサーバーがクラッシュすると、JTS トランザクションに対するロールバックは呼び出されません。
XAResource prepare() メソッド呼び出しの完了後に JBoss EAP サーバーがクラッシュすると、参加している XAResources はすべて準備済みの状態でロックされ、サーバーの再起動時にその状態のままになります。トランザクションがタイムアウトするか、DBA で手動でリソースをロールバックしてトランザクションログを消去するまで、トランザクションはロールバックされずリソースはロックされたままになります。詳細については、https://issues.jboss.org/browse/JBTM-2124 を参照してください。
周期リカバリーはコミットされたトランザクションで発生する可能性があります。

サーバーに過剰な負荷がかかっている場合、サーバーログには以下の警告メッセージとそれに続くスタックトレースが含まれる場合があります。

ARJUNA016027: Local XARecoveryModule.xaRecovery got XA exception XAException.XAER_NOTA: javax.transaction.xa.XAException

負荷が大きいと、トランザクションの処理時間と周期リカバリープロセスの活動が重なることがあります。周期リカバリープロセスは進行中のトランザクションを検出してロールバックを実行しようとしますが、トランザクションは完了するまで続行されます。周期リカバリーがロールバックの試行に失敗すると、ロールバックの失敗をサーバーログに記録します。この問題の原因は今後のリリースで修正される予定ですが、問題の回避策を適用できます。

com.arjuna.ats.jta.orphanSafetyInterval プロパティーの値をデフォルト値の 10000 ミリ秒よりも大きくし、リカバリープロセスの 2 つのフェーズの間隔を増やします。40000 ミリ秒の値が推奨されます。この設定では問題は解決されず、問題が発生して警告メッセージがログに記録される可能性が減少することに注意してください。詳細については、https://developer.jboss.org/thread/266729 を参照してください。

11.2.10. 2 フェーズコミットプロトコル

2 フェーズコミット (2PC) プロトコルは、トランザクションの結果を決定するアルゴリズムを参照します。2PC は XA トランザクションを完了するプロセスとしてトランザクションマネージャー (TM) によって開始されます。

フェーズ 1: 準備

最初のフェーズでは、トランザクションをコミットできるか、あるいはロールバックする必要があるかをトランザクションの参加者がトランザクションコーディネーターに通知します。

フェーズ 2: コミット

2 番目のフェーズでは、トランザクションコーディネーターがトランザクション全体をコミットするか、またはロールバックするかを決定します。いずれの参加者もコミットできない場合、トランザクションはロールバックしなければなりません。それ以外の場合、トランザクションはコミットできます。コーディネーターは何を行うかをリソースに指示し、リソースはその完了時にコーディネーターに通知します。この時点で、トランザクションは完了します。

11.2.11. トランザクションタイムアウト

原子性を確保し、トランザクションを ACID 標準に準拠させるために、トランザクションの一部が長期間実行される場合があります。トランザクションの参加者は、コミット時にデータベーステーブルまたはキュー内のメッセージの一部である XA リソースをロックする必要があります。また、TM は各トランザクション参加者からの応答を待ってからすべての参加者にコミットあるいはロールバックの指示を出す必要があります。ハードウェアあるいはネットワークの障害のため、リソースが永久にロックされることがあります。

トランザクションのタイムアウトをトランザクションと関連付け、ライフサイクルを制御することができます。タイムアウトのしきい値がトランザクションのコミットあるいはロールバック前に渡された場合、タイムアウトにより、自動的にトランザクションがロールバックされます。

トランザクションサブシステム全体に対しデフォルトのタイムアウト値を設定できます。または、デフォルトのタイムアウト値を無効にし、トランザクションごとにタイムアウトを指定できます。

11.2.12. 分散トランザクション

分散トランザクションは、複数の JBoss EAP サーバー上に参加者が存在するトランザクションです。Java Transaction Service (JTS) 仕様では、異なるベンダーのアプリケーションサーバー間で JTS トランザクションを分散可能にすることが規定されています。Java Transaction API (JTA) はこれを定義していませんが、JBoss EAP は JBoss EAP サーバー間での分散 JTA トランザクションをサポートしています。

注記

異なるベンダーのサーバー間でのトランザクション分散はサポートされません。

注記

他のベンダーのアプリケーションサーバーのドキュメントでは、分散トランザクションという用語が XA トランザクションを意味することがあります。JBoss EAP のドキュメントでは、複数の JBoss EAP アプリケーションサーバー間で分散されるトランザクションを分散トランザクションと呼びます。また、本書では、異なるリソースで構成されるトランザクション (データベースリソースや jms リソースなど) を XA トランザクションと呼びます。詳細については、Java Transaction Service (JTS) および XA データソースおよび XA トランザクション を参照してください。

11.2.13. ORB 移植性 API

Object Request Broker (ORB) とは、複数のアプリケーションサーバーで分散されるトランザクションの参加者、コーディネーター、リソース、および他のサービスにメッセージを送受信するプロセスのことです。ORB は標準的なインターフェース記述言語 (IDL) を使用してメッセージを通信し解釈します。Common Object Request Broker Architecture (CORBA) は JBoss EAP の ORB によって使用される IDL です。

ORB を使用する主なタイプのサービスは、Java トランザクションサービス (JTS) 仕様を使用する分散 Java トランザクションのシステムです。レガシーシステムなどの他のシステムは、通信にリモートエンタープライズ JavaBeans や JAX-WS または JAX-RS Web サービスなどの他のメカニズムではなく ORB を使用することがあります。

ORB 移植性 API は ORB とやりとりするメカニズムを提供します。この API は ORB への参照を取得するメソッドや、ORB からの受信接続をリッスンするモードにアプリケーションを置くメソッドを提供します。API のメソッドの一部はすべての ORB によってサポートされていません。このような場合、例外が発生します。

API は 2 つの異なるクラスによって構成されます。

  • com.arjuna.orbportability.orb
  • com.arjuna.orbportability.oa

ORB 移植性 API に含まれるメソッドとプロパティーの詳細については、Red Hat カスタマーポータルの JBoss EAP Javadocs バンドルを参照してください。

11.3. トランザクションの最適化

11.3.1. トランザクション最適化の概要

JBoss EAP のトランザクションマネージャー (TM) には、アプリケーションで利用できる複数の最適化機能が含まれています。

最適化機能は、特定のケースで 2 フェーズコミットプロトコルを拡張するために使用します。一般的に、TM によって、2 フェーズコミットを介して渡されるグローバルトランザクションが開始されます。ただし、特定のケースでこれらのトランザクションを最適化する場合は、TM によって完全な 2 フェーズコミットを行う必要がないため、処理は速くなります。

TM により使用される別の最適化機能は、以下で詳細に説明されています。

11.3.2. 1 フェーズコミット (1PC) の LRCO 最適化

1 フェーズコミット (1PC)

トランザクションでは、2 フェーズコミットプロトコル (2PC) がより一般的に使用されますが、両フェーズに対応する必要がなかったり、対応できない場合もあります。そのような場合、1 フェーズコミット (1PC) プロトコルを使用できます。1 フェーズコミットプロトコルは、XA または非 XA リソースの 1 つがグローバルトランザクションの一部である場合に使用されます。

一般的に、準備フェースでは、第 2 フェーズが処理されるまでリソースがロックされます。1 フェーズコミットは、準備フェースが省略され、コミットのみがリソースに対して処理されることを意味します。指定されない場合は、グローバルトランザクションに参加者が 1 つだけ含まれるときに 1 フェーズコミット最適化機能が自動的に使用されます。

最終リソースコミット最適化 (LRCO: Last Resource Commit Optimization)

非 XA データソースが XA トランザクションに参加する場合は、最終リソースコミット最適化 (LRCO) と呼ばれる最適化機能が使用されます。このプロトコルにより、ほとんどのトランザクションは正常に完了しますが、一部のエラーによってトランザクションの結果の一貫性が失われることがあります。そのため、この方法は最終手段として使用してください。

非 XA リソースは準備フェーズの終了時に処理され、コミットが試行されます。コミットに成功すると、トランザクションログが書き込まれ、残りのリソースがコミットフェーズに移動します。最終リソースがコミットに失敗すると、トランザクションはロールバックされます。

ローカルの TX データソースが 1 つのみトランザクションで使用されると、LRCO が自動的に適用されます。

これまでは、LRCO メソッドを使用して非 XA リソースを XA トランザクションに追加していました。ただし、この場合は LRCO によって失敗することがあります。LRCO メソッドを用いて非 XA リソースを XA トランザクションに追加するには、以下の手順に従います。

  1. XA トランザクションの準備
  2. LRCO のコミット
  3. tx ログの書き込み
  4. XA トランザクションのコミット

手順 2 と手順 3 の間でクラッシュした場合、データが不整合になり、XA トランザクションをコミットできなくなることがあります。データの不整合が発生する理由は、LRCO 非 XA リソースがコミットされるが、XA リソースの準備に関する情報が記録されなかったことです。リカバリーマネージャーはサーバーの起動後にリソースをロールバックします。CMR ではこの制限がなくなり、非 XA が XA トランザクションに安定して参加できるようになります。

注記

CMR は特別な LRCO 最適化機能であり、データソースにのみ使用できます。非 XA リソースには適していません。

11.3.2.1. Commit Markable Resource (CMR)

概要

Commit Markable Resource (CMR) インターフェースを使用してリソースマネージャーへのアクセスを設定すると、非 XA データソースを XA (2PC) トランザクションに安定的に登録できます。これは、非 XA リソースを完全にリカバリー可能にする LRCO アルゴリズムの実装です。

CMR を設定するには、以下のことを行う必要があります。

  1. データベースでテーブルを作成します。
  2. データソースを接続可能にします。
  3. 参照を transactions サブシステムに追加します。
データベースでテーブルを作成する

トランザクションには CMR リソースを 1 つだけ含めることができます。以下の SQL が機能するテーブルを作成する必要があります。

SELECT xid,actionuid FROM _tableName_ WHERE transactionManagerID IN (String[])
DELETE FROM _tableName_ WHERE xid IN (byte[[]])
INSERT INTO _tableName_ (xid, transactionManagerID, actionuid) VALUES (byte[],String,byte[])

SQL クエリーの例

Sybase:

CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28))

Oracle:

CREATE TABLE xids (xid RAW(144), transactionManagerID varchar(64), actionuid RAW(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)

IBM:

CREATE TABLE xids (xid VARCHAR(255) for bit data not null, transactionManagerID
varchar(64), actionuid VARCHAR(255) for bit data not null)
CREATE UNIQUE INDEX index_xid ON xids (xid)

SQL Server:

CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)

Postgres:

CREATE TABLE xids (xid bytea, transactionManagerID varchar(64), actionuid bytea)
CREATE UNIQUE INDEX index_xid ON xids (xid)

MariaDB:

CREATE TABLE xids (xid BINARY(144), transactionManagerID varchar(64), actionuid BINARY(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)

MySQL:

CREATE TABLE xids (xid VARCHAR(255), transactionManagerID varchar(64), actionuid VARCHAR(255))
CREATE UNIQUE INDEX index_xid ON xids (xid)
データソースを接続可能にする

デフォルトでは、CMR 機能はデータソースに対して無効になっています。有効にするには、データソースの設定を作成または変更し、connectible 属性を true に設定する必要があります。以下に、サーバーの XML 設定ファイルのデータソースセクションの例を示します。

<datasource enabled="true" jndi-name="java:jboss/datasources/ConnectableDS" pool-name="ConnectableDS" jta="true" use-java-context="true" connectable="true"/>
注記

この機能は XA データソースには適用されません。

また、以下のように管理 CLI を使用してリソースマネージャーを CMR として有効にすることもできます。

/subsystem=datasources/data-source=ConnectableDS:add(enabled="true", jndi-name="java:jboss/datasources/ConnectableDS", jta="true", use-java-context="true", connectable="true", connection-url="validConnectionURL", exception-sorter="org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLExceptionSorter", driver-name="h2")
新しい CMR 機能を使用するために既存のリソースを更新

CMR 機能を使用するために既存のデータソースのみを更新する必要がある場合は、connectable 属性を変更します。

/subsystem=datasources/data-source=ConnectableDS:write-attribute(name=connectable,value=true)
参照をトランザクションサブシステムに追加する

transactions サブシステムは、以下のような transactions サブシステム設定セクションへのエントリーを用いて CMR 対応のデータソースを特定します。

<subsystem xmlns="urn:jboss:domain:transactions:3.0">
    ...
    <commit-markable-resources>
        <commit-markable-resource jndi-name="java:jboss/datasources/ConnectableDS">
            <xid-location name="xids" batch-size="100" immediate-cleanup="false"/>
        </commit-markable-resource>
        ...
    </commit-markable-resources>
</subsystem>
注記

transactions サブシステムで CMR 参照を追加したら、サーバーを再起動する必要があります。

注記

データソース設定で exception-sorter パラメーターを使用します。詳細については、JBoss EAP Configuration GuideExample Datasource Configurations を参照してください。

11.3.3. 推定中止 (presumed-abort) の最適化

トランザクションをロールバックする場合、この情報をローカルで記録し、エンリストされたすべての参加者に通知します。この通知は形式的なもので、トランザクションの結果には影響しません。すべての参加者が通知されると、このトランザクションに関する情報を削除できます。

トランザクションのステータスに対する後続の要求が行われる場合、利用可能な情報はありません。このような場合、要求側はトランザクションが中断され、ロールバックされたと見なします。推定中止 (presumed-abort) の最適化は、トランザクションがコミットの実行を決定するまで参加者に関する情報を永続化する必要がないことを意味します。これは、トランザクションがコミットの実行を決定する前に発生した障害はトランザクションの中止であると推定されるためです。

11.3.4. 読み取り専用の最適化

参加者は、準備するよう要求されると、トランザクション中に変更したデータがないことをコーディネーターに伝えることができます。参加者が最終的にどうなってもトランザクションに影響を与えることはないため、このような参加者にトランザクションの結果について通知する必要はありません。この読み取り専用の参加者はコミットプロトコルの第 2 フェーズから省略可能です。

11.4. トランザクションの結果

11.4.1. トランザクションの結果

可能なトランザクションの結果は次の 3 つになります。

コミット
トランザクションの参加者すべてがコミットできる場合、トランザクションコーディネーターはコミットの実行を指示します。詳細については、トランザクションのコミット を参照してください。
ロールバック
トランザクションの参加者のいずれかがコミットできなかったり、トランザクションコーディネーターが参加者にコミットを指示できない場合は、トランザクションがロールバックされます。詳細については、トランザクションロールバックを参照してください。
ヒューリスティックな結果
トランザクションの参加者の一部がコミットし、他の参加者がロールバックした場合をヒューリスティックな結果と呼びます。詳細については、ヒューリスティックな結果を参照してください。

11.4.2. トランザクションのコミット

トランザクションの参加者がコミットすると、新規の状態が永続化されます。新規の状態はトランザクションで作業を行った参加者により作成されます。トランザクションのメンバーがデータベースに記録を書き込む時などが最も一般的な例になります。

コミット後、トランザクションの情報はトランザクションコーディネーターから削除され、新たに書き込まれた状態が永続状態となります。

11.4.3. トランザクションロールバック

トランザクションの参加者はトランザクションの開始前に、状態を反映するため状態をリストアし、ロールバックを行います。ロールバック後の状態はトランザクション開始前の状態と同じになります。

11.4.4. ヒューリスティックな結果

ヒューリスティックな結果 (アトミックでない結果) は、トランザクションでの参加者の決定がトランザクションマネージャーのものとは異なる状況です。ヒューリスティックな結果が起こると、システムの整合性が保たれなくなることがあり、通常、解決に人的介入が必要になります。ヒューリスティックな結果に依存するようなコードは記述しないようにしてください。

通常、ヒューリスティックな結果は、2 フェーズコミット (2PC) プロトコルの 2 番目のフェーズで発生します。まれにですが、この結果は 1PC で発生することがあります。多くの場合、これは基盤のハードウェアまたは基盤のサーバーの通信サブシステムの障害によって引き起こされます。

ヒューリスティックな結果は、さまざまなサブシステムまたはリソースのタイムアウトにより可能になります (トランザクションマネージャーと完全なクラッシュリカバリーを使用)。何らかの形の分散契約が必要なシステムでは、グローバルな結果という点でシステムのいくつかの部分が分岐する状況が発生することがあります。

ヒューリスティックな結果には 4 種類あります。

ヒューリスティックロールバック

コミット操作はリソースをコミットできませんでしたが、すべての参加者はロールバックでき、アトミックな結果が実現されました。

ヒューリスティックコミット

参加者のすべてが一方的にコミットしたため、ロールバック操作に失敗します。たとえば、コーディネーターが正常にトランザクションを準備したにも関わらず、ログ更新の失敗などでコーディネーター側で障害が発生したため、ロールバックの実行を決定した場合などに発生します。暫定的に参加者がコミットの実行を決定する場合があります。

ヒューリスティック混合

一部の参加者がコミットし、その他の参加者はロールバックした状態です。

ヒューリスティックハザード

更新の一部の配置が不明な状態です。既知の更新結果はすべてコミットまたはロールバックされています。

11.4.5. JBoss Transactions エラーと例外

UserTransaction クラスのメソッドがスローする例外に関する詳細については、http://docs.oracle.com/javaee/7/api/javax/transaction/UserTransaction.htmlUserTransaction API の仕様を参照してください。

11.5. トランザクションライフサイクルの概要

11.5.1. トランザクションライフサイクル

Java Transactions API (JTA) の詳細については、Java Transactions API (JTA) を参照してください。

リソースがトランザクションへの参加を要求すると、一連のイベントが開始されます。トランザクションマネージャー (TM) は、アプリケーションサーバー内に存在するプロセスであり、トランザクションを管理します。トランザクションの参加者は、トランザクションに参加するオブジェクトです。リソースは、データソース、JMS 接続ファクトリー、または他の JCA 接続です。

  1. アプリケーションが新しいトランザクションを開始する

    トランザクションを開始するために、アプリケーションは JNDI から (EJB の場合はアノテーションから) UserTransaction クラスのインスタンスを取得します。UserTransaction インターフェースには、トップレベルのトランザクションを開始、コミット、およびロールバックするメソッドが含まれています。新規作成されたトランザクションは、そのトランザクションを呼び出すスレッドと自動的に関連付けされます。ネストされたトランザクションは JTA ではサポートされないため、すべてのトランザクションがトップレベルのトランザクションとなります。

    UserTransaction.begin() メソッドが呼び出されると、EJB がトランザクションを開始します。このトランザクションのデフォルトの動作は TransactionAttribute アノテーションまたは ejb.xml 記述子の使用によって影響を受けることがあります。この時点以降に使用されたリソースは、このトランザクションと関連付けられます。2 つ以上のリソースが登録された場合、トランザクションは XA トランザクションになり、コミット時に 2 フェーズコミットプロトコルに参加します。

    注記

    デフォルトでは、トランザクションは EJB のアプリケーションコンテナーによって駆動されます。これは Container Managed Transaction (CMT) と呼ばれます。トランザクションをユーザー駆動にするには、Transaction ManagementBean Managed Transaction (BMT) に変更する必要があります。BMT では、UserTransaction オブジェクトはユーザーがトランザクションを管理するために使用できます。

  2. アプリケーションが状態を変更する

    次の手順では、アプリケーションが作業を実行して状態を変更します (登録されたリソースに対してのみ)。

  3. アプリケーションがコミットまたはロールバックすることを決定する

    アプリケーションの状態の変更が完了すると、アプリケーションはコミットするか、またはロールバックするかを決定し、適切なメソッド (UserTransaction.commit() または UserTransaction.rollback()) を呼び出します。CMT の場合、このプロセスは自動的に駆動されますが、BMT の場合は、UserTransaction のメソッドコミットまたはロールバックを明示的に呼び出す必要があります。

  4. TM がレコードからトランザクションを削除する

    コミットあるいはロールバックが完了すると、TM はレコードをクリーンアップし、トランザクションログからトランザクションに関する情報を削除します。

障害リカバリー

リソース、トランザクションの参加者、またはアプリケーションサーバーがクラッシュするか、使用できなくなった場合は、障害が解決され、リソースが再度使用できるようになったときに Transaction Manager がリカバリーを実行します。このプロセスは自動的に実行されます。詳細については、XA リカバリーを参照してください。

11.6. トランザクションサブシステムの設定

11.6.1. トランザクション設定の概要

はじめに

transactions サブシステムでは、統計、タイムアウト値、トランザクションロギングなどのトランザクションマネージャー (TM) のオプションを設定できます。

詳細については、JBoss EAP Configuration GuideTransactions Subsystem Configuration を参照してください。

11.6.2. トランザクションマネージャーの設定

トランザクションマネージャーは、Web ベースの管理コンソールまたはコマンドライン管理 CLI を使用して設定できます。

管理コンソールを使用したトランザクションマネージャーの設定

以下の手順は、Web ベースの管理コンソールを使用してトランザクションマネージャーを設定する方法を示しています。

  1. 画面上部の Configuration タブを選択します。
  2. JBoss EAP を管理対象ドメインとして実行している場合は、変更する任意のプロファイルを選択します。
  3. Subsystem リストから、Transactions を選択し、View をクリックします。
  4. 編集する設定に応じたタブ (リカバリーオプションの場合の Recovery など) で Edit をクリックします。
  5. 必要な変更を行い、Save をクリックして変更を保存します。
  6. Need Help? をクリックしてヘルプテキストを表示します。
管理 CLI を使用したトランザクションマネージャーの設定

管理 CLI で一連のコマンドを使用してトランザクションマネージャーを設定できます。これらのコマンドはすべて /subsystem=transactions (スタンドアロンサーバー向け) または /profile=default/subsystem=transactions/ (管理対象ドメインの default プロファイル向け) で始まります。

トランザクションマネージャーのすべての設定オプションの詳細なリストについては、トランザクションマネージャーの設定オプションを参照してください。

11.6.3. トランザクションロギング

11.6.3.1. トランザクションログメッセージ

トランザクションロガーに DEBUG ログレベルを使用することにより、ログファイルを読み取り可能な状態に保ちつつトランザクションを追跡できます。詳細なデバッグの場合は、TRACE ログレベルを使用します。トランザクションロガーの設定に関する詳細については、トランザクションサブシステムのロギング設定を参照してください。

TRACE ログレベルでログを記録するよう設定すると、トランザクションマネージャー (TM) は多くのロギング情報を生成できます。最も一般的なメッセージの一部は次のとおりです。このリストは包括的ではなく、他のメッセージが表示されることもあります。

表11.1 トランザクション状態の変更

トランザクションの開始

トランザクションが開始されたら、クラス com.arjuna.ats.arjuna.coordinator.BasicAction のメソッド Begin が実行され、メッセージ BasicAction::Begin() for action-id <transaction uid> でログに示されます。

トランザクションのコミット

トランザクションがコミットされたら、クラス com.arjuna.ats.arjuna.coordinator.BasicAction のメソッド Commit が実行され、メッセージ BasicAction::Commit() for action-id <transaction uid> でログに示されます。

トランザクションのロールバック

トランザクションがロールバックされたら、クラス com.arjuna.ats.arjuna.coordinator.BasicAction のメソッド Rollback が実行され、メッセージ BasicAction::Rollback() for action-id <transaction uid> でログに示されます。

トランザクションのタイムアウト

トランザクションがタイムアウトすると、com.arjuna.ats.arjuna.coordinator.TransactionReaper のメソッド doCancellations が実行され、Reaper Worker <thread id> attempting to cancel <transaction uid> とログに示されます。この結果、上記のように同じスレッドがトランザクションをロールバックすることが示されます。

11.6.3.2. トランザクションサブシステムのロギング設定

JBoss EAP の他のログ設定に依存せずにログに記録されたトランザクションに関する情報の量を制御できます。ログ設定は、管理コンソールまたは管理 CLI を使用して設定できます。

管理コンソールを使用したトランザクションロガーの設定
  1. ロギングサブシステム設定に移動します。

    1. 管理コンソールで、Configuration タブをクリックします。管理対象ドメインを使用する場合は、最初に適切なサーバープロファイルを選択する必要があります。
    2. Logging サブシステムを選択し、View をクリックします。
  2. com.arjuna 属性を編集します。

    Log Categories タブを選択します。com.arjuna エントリーがすでに存在します。com.arjuna を選択し、Attributes セクションの Edit をクリックします。ログレベルを変更し、親ハンドラーを使用するかどうかを選択できます。

    • ログレベル:

      トランザクションにより大量のロギング出力が生成されることがあるため、サーバーのログがトランザクション出力で満たされないようデフォルトのロギングレベルは WARN に設定されます。トランザクション処理の詳細を確認する必要がある場合は、トランザクション ID が表示されるよう TRACE ログレベルを使用します。

    • 親ハンドラーの使用:

      親ハンドラーはロガーが出力を親ロガーに送信するかどうかを指定します。デフォルトの動作は true です。

  3. Save をクリックして変更を保存します。
管理 CLI を使用したトランザクションロガーの設定

以下のコマンドを使用して管理 CLI からログレベルを設定します。スタンドアロンサーバーの場合は、コマンドから /profile=default を削除します。

/profile=default/subsystem=logging/logger=com.arjuna:write-attribute(name=level,value=VALUE)

11.6.4. トランザクションの参照と管理

管理 CLI では、トランザクションレコードを参照および操作する機能がサポートされます。この機能は、TM と JBoss EAP の管理 API 間の対話によって提供されます。

TM は、待機中の各トランザクションとトランザクションに関連する参加者に関する情報を、オブジェクトストアと呼ばれる永続ストレージに格納します。管理 API は、オブジェクトストアを log-store と呼ばれるリソースとして公開します。probe と呼ばれる API 操作はトランザクションログを読み取り、各レコードに対してノードパスを作成します。probe コマンドは、log-store を更新する必要があるときに、いつでも手動で呼び出すことができます。トランザクションログが即座に表示され、非表示になるのは、正常な挙動です。

ログストアの更新

このコマンドは、管理対象ドメインでプロファイル default を使用するサーバーグループに対してログストアを更新します。スタンドアローンサーバーの場合は、コマンドから profile=default を削除します。

/profile=default/subsystem=transactions/log-store=log-store/:probe
準備済みトランザクションすべての表示

準備されたすべてのトランザクションを表示するには、最初にログストアを更新し (ログストアの更新 を参照)、ファイルシステムの ls コマンドに類似した機能を持つ次のコマンドを実行します。

ls /profile=default/subsystem=transactions/log-store=log-store/transactions

または、

/host=master/server=server-one/subsystem=transactions/log-store=log-store:read-children-names(child-type=transactions)

各トランザクションが一意の ID とともに表示されます。個々の操作は、各トランザクションに対して実行できます (トランザクションの管理 を参照)。

11.6.4.1. トランザクションの管理

トランザクションの属性の表示

JNDI 名、EIS 製品名およびバージョン、ステータスなどのトランザクションに関する情報を表示するには、:read-resource 管理 CLIコマンドを使用します。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:read-resource
トランザクションの参加者の表示

各トランザクションログには、participants (参加者) と呼ばれる子要素が含まれます。トランザクションの参加者を確認するには、この要素に対して read-resource 管理 CLI コマンドを使用します。参加者は JNDI 名によって識別されます。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=java\:\/JmsXA:read-resource

結果は以下のようになります。

{
   "outcome" => "success",
   "result" => {
       "eis-product-name" => "ActiveMQ",
       "eis-product-version" => "2.0",
       "jndi-name" => "java:/JmsXA",
       "status" => "HEURISTIC",
       "type" => "/StateManager/AbstractRecord/XAResourceRecord"
   }
}

ここで示された結果ステータスは HEURISTIC であり、リカバリーが可能です。詳細については、トランザクションのリカバリーを参照してください。

特別な場合では、ログに対応するトランザクションレコードがないオーファンレコード (XAResourceRecords) をオブジェクトストアに作成できます。たとえば、準備済みに XA リソースが TM が記録する前にクラッシュし、ドメイン管理 API ではアクセスできません。このようなレコードにアクセスするには、管理オプション expose-all-logstrue に設定する必要があります。このオプションは管理モデルには保存されず、サーバーが再起動されると false にリストアされます。

/profile=default/subsystem=transactions/log-store=log-store:write-attribute(name=expose-all-logs, value=true)

代わりに以下のコマンドを実行すると、トラザクション参加者 ID が集約された形式で表示されます。

/host=master/server=server-one/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:read-children-names(child-type=participants)
トランザクションの削除

各トランザクションログは、トランザクションを表すトランザクションログを削除する :delete 操作をサポートします。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:delete
トランザクションのリカバリー

トランザクションの各参加者は、:recover 管理 CLI コマンドを使用したリカバリーをサポートします。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=2:recover
  • トランザクションの状態が HEURISTIC である場合は、リカバリー操作によって状態が PREPARE に変わり、リカバリーがトリガーされます。
  • トランザクションの参加者の 1 つがヒューリスティックな場合、リカバリー操作は commit 操作を再実行しようとします。成功した場合、トランザクションログから参加者が削除されます。これを確認するには、log-store 上で :probe 操作を再実行し、参加者がリストされていないことを確認します。これが最後の参加者の場合は、トランザクションも削除されます。
リカバリーが必要なトランザクションの状態を更新します。

トランザクションをリカバリーする必要がある場合は、リカバリーを試行する前に :refresh 管理 CLI コマンドを使用して、トランザクションのリカバリーが必要であるかを確認できます。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=2:refresh

11.6.5. トランザクション統計情報の表示

トランザクションマネージャーの統計が有効になっていると、トランザクションマネージャーにより処理されたトラザクションの統計を表示できます。トランザクションマネージャーの統計を有効にする方法については、トランザクションマネージャーの設定を参照してください。

管理コンソールまたは管理 CLI を使用して統計を表示できます。管理コンソールでは、トラザクションの統計は Runtime タブから Transactions サブシステムに移動することにより利用できます。管理コンソールではすべての統計を利用できるわけではありません。

以下の表は、利用できる統計とその説明を示しています。

表11.2 トランザクションサブシステムの統計

統計説明

number-of-transactions

このサーバー上でトランザクションマネージャーにより処理されるトランザクションの合計数。

number-of-committed-transactions

このサーバー上でトランザクションマネージャーにより処理されるコミット済みトランザクションの数。

number-of-aborted-transactions

このサーバー上でトランザクションマネージャーにより処理されるアボートされたトランザクションの数。

number-of-timed-out-transactions

このサーバー上でトランザクションマネージャーにより処理されるタイムアウトのトランザクションの数。タイムアウトしたトランザクションの数はアボートされたトランザクションの数まで計算されます。

number-of-heuristics

ヒューリスティック状態のトランザクションの数。

number-of-inflight-transactions

開始した未完了のトランザクションの数。

number-of-application-rollbacks

障害の原因がアプリケーションであった失敗トランザクションの数。

number-of-resource-rollbacks

障害の原因がリソースであった失敗トランザクションの数。

11.7. 実際のトランザクションの使用

11.7.1. トランザクション使用の概要

次の手順は、アプリケーションでトランザクションを使用する必要がある場合に役に立ちます。

11.7.2. トランザクションの制御

はじめに

この手順のリストでは、JTS API を使用するアプリケーションでトランザクションを制御するさまざまな方法を概説します。

11.7.3. トランザクションの開始

この手順では、新しいトランザクションの開始方法を示します。実行するトランザクションマネージャー (TM) が JTA または JTS のいずれかで設定されていれば API は同じになります。

  1. UserTransaction のインスタンスを取得する

    @TransactionManagement(TransactionManagementType.BEAN) アノテーションを用いると、JNDI、インジェクション、または EJB のコンテキストを使用してインスタンスを取得できます (EJB が Bean 管理のトランザクションを使用する場合)。

    1. JNDI

      new InitialContext().lookup("java:comp/UserTransaction")
    2. インジェクション

      @Resource UserTransaction userTransaction;
    3. コンテキスト

      • ステートレス/ステートフル Bean の場合

        @Resource SessionContext ctx;
        ctx.getUserTransaction();
      • メッセージ駆動型 Bean の場合

        @Resource MessageDrivenContext ctx;
        ctx.getUserTransaction()
  2. データソースへの接続後に UserTransaction.begin() を呼び出す

    try {
        System.out.println("\nCreating connection to database: "+url);
        stmt = conn.createStatement();  // non-tx statement
        try {
            System.out.println("Starting top-level transaction.");
            userTransaction.begin();
            stmtx = conn.createStatement(); // will be a tx-statement
            ...
        }
    }
    JTS 仕様を使用して既存のトランザクションに参加する
    EJB (CMT または BMT のいずれかと使用) の利点の 1 つは、コンテナーがトランザクション処理の内部をすべて管理することです。そのため、ユーザーは JBoss EAP コンテナー間の XA トランザクションまたはトランザクションディストリビューションの一部であるトランザクションを処理する必要がありません。

結果

トランザクションが開始します。トランザクションをコミットまたはロールバックするまで、データソースのすべての使用でトランザクションに対応します。

完全な例については、JTA トランザクションの例 を参照してください。

11.7.4. ネストされたトランザクション

ネストされたトランザクションを用いると、アプリケーションは既存のトランザクションに埋め込まれるトランザクションを作成できます。このモデルでは、再帰的に複数のサブトランザクションをトランザクションに埋め込むことができます。親トランザクションをコミットまたはロールバックせずにサブトランザクションをコミットまたはロールバックできます。しかし、コミット操作の結果は、先祖のトランザクションがすべてコミットしたかどうかによって決まります。

実装固有の情報については、Narayana プロジェクトドキュメンテーションを参照してください。

ネストされたトランザクションは、JTS 仕様と使用した場合のみ利用できます。ネストされたトランザクションは JBoss EAP アプリケーションサーバーではサポートされない機能です。また、多くのデータベースベンダーがネストされたトランザクションをサポートしないため、ネストされたトランザクションをアプリケーションに追加する前にデータベースベンダーにお問い合わせください。

11.7.5. トランザクションのコミット

この手順では、Java Transaction API (JTA) を使用してトランザクションをコミットする方法を説明します。

前提条件

トランザクションは、コミットする前に開始する必要があります。トランザクションの開始方法については、トランザクションの開始を参照してください。

  1. UserTransaction の commit() メソッドを呼び出す

    UserTransaction の commit() メソッドを呼び出す場合、TM はトランザクションをコミットしようとします。

    @Inject
    private UserTransaction userTransaction;
    
    public void updateTable(String key, String value) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            userTransaction.begin();
            <!-- Perform some data manipulation using entityManager -->
            ...
            // Commit the transaction
            userTransaction.commit();
        } catch (Exception ex) {
            <!-- Log message or notify Web page -->
            ...
            try {
                userTransaction.rollback();
            } catch (SystemException se) {
                throw new RuntimeException(se);
            }
            throw new RuntimeException(ex);
        } finally {
            entityManager.close();
        }
    }
  2. Container Managed Transactions (CMT) を使用する場合は、手動でコミットする必要がない

    Bean がコンテナー管理トランザクションを使用するよう設定すると、コンテナーはコードで設定したアノテーションに基づいてトランザクションライフサイクルを管理します。

    @PersistenceContext
    private EntityManager em;
    
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void updateTable(String key, String value)
      <!-- Perform some data manipulation using entityManager -->
      ...
    }

結果

データソースがコミットされ、トランザクションが終了します。そうでない場合は、例外が発生します。

注記

完全な例については、JTA トランザクションの例 を参照してください。

11.7.6. トランザクションのロールバック

この手順では、Java Transaction API (JTA) を使用してトランザクションをロールバックする方法を説明します。

前提条件

トランザクションは、ロールバックする前に開始する必要があります。トランザクションの開始方法については、トランザクションの開始を参照してください。

  1. UserTransaction の rollback() メソッドを呼び出す

    UserTransactionrollback() メソッドを呼び出す場合、TM はトランザクションをロールバックし、データを前の状態に戻そうとします。

    @Inject
    private UserTransaction userTransaction;
    
    public void updateTable(String key, String value)
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            userTransaction.begin():
            <!-- Perform some data manipulation using entityManager -->
              ...
              // Commit the transaction
            userTransaction.commit();
        } catch (Exception ex) {
            <!-- Log message or notify Web page -->
            ...
            try {
                userTransaction.rollback();
            } catch (SystemException se) {
                throw new RuntimeException(se);
            }
            throw new RuntimeException(e);
        } finally {
            entityManager.close();
        }
    }
  2. コンテナー管理トランザクション (CMT) を使用する場合は、手動でトランザクションをロールバックする必要がない

    Bean がコンテナー管理トランザクションを使用するよう設定すると、コンテナーはコードで設定したアノテーションに基づいてトランザクションライフサイクルを管理します。

注記

CMT のロールバックは RuntimeException が発生すると実行されます。setRollbackOnly メソッドを明示的に呼び出してロールバックを発生させることもできます。または、 アプリケーション例外の @ApplicationException(rollback=true) を使用してロールバックできます。

結果

トランザクションは TM によりロールバックされます。

注記

完全な例については、JTA トランザクションの例 を参照してください。

11.7.7. トランザクションにおけるヒューリスティックな結果の処理方法

ヒューリスティックなトランザクションの結果はよく発生するものではなく、通常は例外的な原因が存在します。ヒューリスティックという言葉は「手動」を意味し、こうした結果は通常手動で処理する必要があります。トランザクションのヒューリスティックな結果については、ヒューリスティックな結果を参照してください。

この手順では、Java Transaction API (JTA) を使用してトランザクションのヒューリスティックな結果を処理する方法を説明します。

  1. 原因を調べる: トランザクションのヒューリスティックな結果の全体的な原因は、リソースマネージャーがコミットまたはロールバックの実行を約束したにも関わらず、約束を守らなかったことにあります。原因としては、サードパーティーコンポーネント、サードパーティーコンポーネントと JBoss EAP 6 間の統合レイヤー、または JBoss EAP 6 自体の問題が考えられます。

    ヒューリスティックなエラーの最も一般的な原因として圧倒的に多いのが、環境の一時的な障害とリソースマネージャーを扱うコードのコーディングエラーの 2 つです。

  2. 環境の一時的な障害を修復する: 通常、環境内で一時的な障害が発生した場合は、ヒューリスティックなエラーを発見する前に気づくはずです。原因としては、ネットワークの停止、ハードウェア障害、データベース障害、電源異常などが考えられます。

    ストレステストの実施中にテスト環境でヒューリスティックな結果が発生した場合は、使用している環境の脆弱性に関する情報が提供されます。

    警告

    JBoss EAP は、障害発生時に非ヒューリスティックな状態にあるトランザクションの自動リカバリーを行いますが、ヒューリスティックなトランザクションのリカバリーを試行しません。

  3. リソースマネージャーのベンダーに連絡する: 環境に明らかな障害がない場合や、ヒューリスティックな結果が容易に再現可能な場合は、コーディングエラーである可能性があります。サードパーティーのベンダーに連絡して、解決方法の有無を確認してください。JBoss EAP の TM 自体に問題があると思われる場合は、Red Hat グローバルサポートサービスまでご連絡ください。
  4. 管理 CLI から手動でトランザクションの回復を試行します。詳細については、トランザクションのリカバリーを参照してください。
  5. テスト環境でログを削除し、JBoss EAP を再起動する: テスト環境である場合や、データの整合性を気にしない場合は、トランザクションログを削除して JBoss EAP を再起動すると、ヒューリスティックな結果はなくなります。デフォルトでは、トランザクションログの場所はスタンドアロンサーバーでは EAP_HOME/standalone/data/tx-object-store/、管理対象ドメインでは EAP_HOME/domain/servers/SERVER_NAME/data/tx-object-store になります。管理対象ドメインの SERVER_NAME は、サーバーグループに参加している個々のサーバー名を示しています。

    注記

    トランザクションログの場所は、使用中のオブジェクトストアや oject-store-relative-to パラメーターおよび object-store-path パラメーターの値セットによっても異なります。ファイルシステムログ (標準のシャドウや Apache ActiveMQ Artemis ログなど) では、デフォルトの方向の場所が使用されますが、JDBC オブジェクトストアを使用する場合、トランザクションログはデータベースに保存されます。

  6. 手動で結果を解決する: トランザクションの結果を手動で解決するプロセスは、障害の厳密な状況によって大きく左右されます。通常は、以下の手順に従って、それぞれの状況に適用する必要があります。

    1. 関連するリソースマネージャーを特定する。
    2. TM の状態とリソースマネージャーを調べる。
    3. 関与する 1 つ以上のコンポーネント内でログのクリーンアップとデータ調整を手動で強制する。

      これらの手順を実行する方法の詳細は、本書の範囲外となります。

11.7.8. JTA トランザクションのエラー処理

11.7.8.1. トランザクションエラーの処理

トランザクションエラーは、多くの場合、タイミングに依存するため、解決するのが困難です。以下に、一部の一般的なエラーと、これらのエラーのトラブルシューティングに関するヒントを示します。

注記

これらのガイドラインはヒューリスティックエラーに適用されません。ヒューリスティックエラーが発生した場合は、トランザクションにおけるヒューリスティックな結果の処理方法 を参照し、Red Hat グローバルサポートサービスまでお問い合わせください。

トランザクションがタイムアウトになったが、ビジネスロジックスレッドが認識しませんでした。

多くの場合、このようなエラーは、Hibernate がレイジーロードのためにデータベース接続を取得できない場合に発生します。頻繁に発生する場合は、タイムアウト値を増加できます。トランザクションマネージャーの設定 を参照してください。

引き続き問題が解決されない場合は、パフォーマンスを向上させるために外部環境を調整するか、さらに効率的になるようコードを再構築できます。タイムアウトの問題が解消されない場合は、Red Hat グローバルサポートサービスにお問い合わせください。

トランザクションがすでにスレッドで実行されているか、NotSupportedException 例外が発生する

NotSupportedException 例外は、通常、JTA トランザクションをネストしようとし、ネストがサポートされていないことを示します。トランザクションをネストしようとしないときは、多くの場合、スレッドプールタスクで別のトランザクションが開始されますが、トランザクションを中断または終了せずにタスクが終了します。

通常、アプリケーションは、これを自動的に処理する UserTransaction を使用します。その場合は、フレームワークに問題があることがあります。

コードで TransactionManager メソッドまたは Transaction メソッドを直接使用する場合は、トランザクションをコミットまたはロールバックするときに次の動作に注意してください。コードで TransactionManager メソッドを使用してトランザクションを制御する場合は、トランザクションをコミットまたはロールバックすると、現在のスレッドからトランザクションの関連付けが解除されます。ただし、コードで Transaction メソッドを使用する場合は、トランザクションを、実行中のスレッドに関連付けることができず、スレッドプールにスレッドを返す前にスレッドからトランザクションの関連付けを手動で解除する必要があります。

2 番目のローカルリソースを登録することはできません。
このエラーは、2 番目の非 XA リソースをトランザクションに登録しようとした場合に、発生します。1 つのトランザクションで複数のリソースが必要な場合、それらのリソースは XA である必要があります。

11.8. トランザクションに関するリファレンス

11.8.1. JTA トランザクションの例

この例では、 JTA トランザクションを開始、コミット、およびロールバックする方法を示します。使用している環境に合わせて接続およびデータソースパラメーターを調整し、データベースで 2 つのテストテーブルをセットアップする必要があります。

public class JDBCExample {
    public static void main (String[] args) {
        Context ctx = new InitialContext();
        // Change these two lines to suit your environment.
        DataSource ds = (DataSource)ctx.lookup("jdbc/ExampleDS");
        Connection conn = ds.getConnection("testuser", "testpwd");
        Statement stmt = null; // Non-transactional statement
        Statement stmtx = null; // Transactional statement
        Properties dbProperties = new Properties();

        // Get a UserTransaction
        UserTransaction txn = new InitialContext().lookup("java:comp/UserTransaction");

        try {
            stmt = conn.createStatement();  // non-tx statement

            // Check the database connection.
            try {
                stmt.executeUpdate("DROP TABLE test_table");
                stmt.executeUpdate("DROP TABLE test_table2");
            }
            catch (Exception e) {
                throw new RuntimeException(e);
                // assume not in database.
            }

            try {
                stmt.executeUpdate("CREATE TABLE test_table (a INTEGER,b INTEGER)");
                stmt.executeUpdate("CREATE TABLE test_table2 (a INTEGER,b INTEGER)");
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }

            try {
                System.out.println("Starting top-level transaction.");

                txn.begin();

                stmtx = conn.createStatement(); // will be a tx-statement

                // First, we try to roll back changes

                System.out.println("\nAdding entries to table 1.");

                stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)");

                ResultSet res1 = null;

                System.out.println("\nInspecting table 1.");

                res1 = stmtx.executeQuery("SELECT * FROM test_table");

                while (res1.next()) {
                    System.out.println("Column 1: "+res1.getInt(1));
                    System.out.println("Column 2: "+res1.getInt(2));
                }
                System.out.println("\nAdding entries to table 2.");

                stmtx.executeUpdate("INSERT INTO test_table2 (a, b) VALUES (3,4)");
                res1 = stmtx.executeQuery("SELECT * FROM test_table2");

                System.out.println("\nInspecting table 2.");

                while (res1.next()) {
                    System.out.println("Column 1: "+res1.getInt(1));
                    System.out.println("Column 2: "+res1.getInt(2));
                }

                System.out.print("\nNow attempting to rollback changes.");

                txn.rollback();

                // Next, we try to commit changes
                txn.begin();
                stmtx = conn.createStatement();
                System.out.println("\nAdding entries to table 1.");
                stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)");
                ResultSet res2 = null;

                System.out.println("\nNow checking state of table 1.");

                res2 = stmtx.executeQuery("SELECT * FROM test_table");

                while (res2.next()) {
                    System.out.println("Column 1: "+res2.getInt(1));
                    System.out.println("Column 2: "+res2.getInt(2));
                }

                System.out.println("\nNow checking state of table 2.");

                stmtx = conn.createStatement();

                res2 = stmtx.executeQuery("SELECT * FROM test_table2");

                while (res2.next()) {
                    System.out.println("Column 1: "+res2.getInt(1));
                    System.out.println("Column 2: "+res2.getInt(2));
                }

                txn.commit();
            }
            catch (Exception ex) {
                throw new RuntimeException(ex);

            }
        }
        catch (Exception sysEx) {
            sysEx.printStackTrace();
            System.exit(0);
        }
    }
}

11.8.2. トランザクション API ドキュメンテーション

トランザクション JTA API ドキュメンテーションは以下の場所で javadoc として利用できます。

Red Hat JBoss Developer Studio を使用してアプリケーションを開発する場合は、 API ドキュメンテーションが Help メニューに含まれています。