第23章 パーティシパンツのクラッシュリカバリ

トランザクションサービスの主な要件は、パーティシパントを作動するホストやトランザクションコーディネーションサービスを作動するホストによりシステムクラッシュからも回復できることです。トランザクションの停止前あるいはビジネスアクティビティの完了前に発生したクラッシュは比較的簡単に対応できます。トランザクションサービスやパーティシパントは推定アボート (presumed abort) ポリシーを採用することができます。

手順23.1 推定アボートポリシー

  1. コーディネータがクラッシュすると、把握していないトランザクションが無効であると推測し、このようなトランザクションを参照するパーティシパントのリクエストを拒否します。
  2. パーティシパントがクラッシュした場合、暫定的な変更は無視され、コーディネータサービスからのトランザクション準備あるいはビジネスアクティビティ完了リクエストを拒否します。
トランザクションが操作のコミット中あるいは、ビジネスアクティビティを完了し閉じるまでの間であった場合はクラッシュリカバリは複雑になります。トランザクションサービスは、パーティシパントがトランザクションに対してできる限り矛盾のない結果に到達できるようにしなければなりません。
WS-AT トランザクション
トランザクションは、暫定的な変更をすべてコミットするか、トランザクション開始前のステートまですべてをロールバックする必要があります。
WS-Business Activityトランザクション
パーティシパントはすべてアクティビティを終了するか、取り消す必要があり、必要な補正アクションを実行します。
まれに上記が行われない場合、トランザクションサービスはトランザクションに問題があるとのログを残し、レポートします。
XTS は、コーディネータやパーティシパントのホストのいずれか、あるいは両方がクラッシュした場合のWS-ATやWS-BAトランザクションの自動リカバリに対応しています。XTS リカバリマネージャは、XTS サービスが再開されるとコーディネータやパーティシパントホストへの実行を開始します。コーディネータのホスト上で、リカバリマネージャが準備済みでコミットされていないWS-ATトランザクションおよび完了済みで閉じられていないWS-BAトランザクションを検出します。そして、パーティシパントすべてが最初のケースでは回復され、二番目では閉じられるようにします。
パーティシパントホストでは、リカバリマネージャが準備済みでトランザクションロールバックに返答していないWS-ATパーティシパントと、完了済みでアクティビティ取消リクエストに返答していないWS-BA パーティシパントを検出し、WS-ATパーティシパントがロールバックされ、WS-BAパーティシパントが補正されるようにします。このリカバリサービスは、WS-ATコーディネータが介入したホストでクラッシュが発生した場合、従属のWS-ATトランザクションとそのパーティシパントもリカバリ可能です。

23.1. WS-AT リカバリ

23.1.1. WS-ATコーディネータのクラッシュリカバリ

WS-ATコーディネーションサービスは、2相コミットを通してトランザクションが進むにつれ、トランザクションにおける各パーティシパントの状態をトラッキングします。全パーティシパントにprepareメッセージが送られ、パーティシパントすべてがpreparedメッセージを返すと、コーディネータは各パーティシパントの詳細を格納するログ記録を書き込み、トランザクションが完了可能であると示します。この地点以降でコーディネータサービスがクラッシュした場合は、再起動後にログファイルを読み取り、commitメッセージを各パーティシパントに送信することで、2相コミットプロトコルの完了は保証されています。また、全パーティシパントがcommitに対してcommittedメッセージを返答すると、コーディネータはログエントリを何の支障もなく削除することができます。
パーティシパントが返したpreparedメッセージが暫定的な変更をコミットし永続かする準備ができていると示しているため、このタイプのリカバリは安全です。さらに、コーディネータは、クラッシュ前に送信されたコミットメッセージの対応をし複数回クラッシュした場合にメッセージを再送信する必要がありません。XTS のパーティシパント実装はcommitメッセージの再配信にも強くなっています。パーティシパントが「WS-AT パーティシパントのクラッシュリカバリAPI」にあるようなリカバリ機能を実装している場合、同時に両方がクラッシュし、さらにパーティシパントサービスのホストの1つ以上がクラッシュしても、コーディネータはcommit メッセージの配信を保証することができます。
コーディネーションサービスがprepare フェーズ完了前にクラッシュした場合、推定アボートプロトコルにより、確実にパーティシパントがロールバックされます。システムの再起動後、コーディネーションサービスはログ内のエントリがあるため、再起動前にcommitフェーズに入れた可能性のある全トランザクションの情報を持っていることになります。また、再起動後に開始されたアクティブなトランザクションについても把握しています。パーティシパントがprepared メッセージ送信後にレスポンスを待っている場合、等間隔で自動的にprepared メッセージが再送されます。コーディネータがアクティブではないトランザクションを検出し、再起動後にログファイルのエントリがない場合、パーティシパントにアボートするよう指示をだし、Web サービスがトランザクションの代わりに加えられた暫定的なステートの変更をロールバックできるようにします。
Web サービスは、特定の期間レスポンスがない場合タイムアウトするように設定すると、特定のパーティシパントに関連する暫定的な変更を一方的にコミットあるいはロールバックするように決定する場合もあります。この場合、Web サービスはこのアクションを記録し、メッセージを永続ストレージに記録します。パーティシパントがコミットあるいはロールバックするようにリクエストを受け取ると、一方的な決定アクションがリクエストしたアクションと一致しない場合は例外をスローするはずです。コーディネータは例外を検出し、メッセージをログに残しヒューリスティックとして結果をマーキングします。また、管理者が調査、調整できるようにトランザクションのステートをトランザクションログに永久に保存します。

23.1.2. WS-AT パーティシパントのクラッシュリカバリ

パーティシパントが準備を指示される前にWeb サービスのホストマシンがクラッシュした場合、トランザクショナルなWeb サービスに関連しているWS-AT パーティシパントはクラッシュリカバリに関わる必要はありません。コーディネータは、トランザクションを中断されたと推定し、Web サービスは準備がまだのトランザクションに関する情報を破棄することができます。
パーティシパントがprepareするように指示を受けると、Web サービスは永続ストレージにトランザクションのコミットあるいはロールバックが必要なトランザクションのステートを保存するはずです。保存する必要のある特定情報は、Web サービスのビジネスロジックや実装に依存します。しかし、prepareの呼出しからPrepared と返答する前に、パーティシパントはこの状態を保存する必要があります。パーティシパントが必要とされる状態を保存できない場合、あるいはクライアントからのリクエストに対応できない場合、Aborted と返答する必要があります。
Webサービスのホストマシンで稼働するXTS パーティシパントサービスはWeb サービス実装と連携しパーティシパントのクラッシュリカバリを容易化します。これらのパーティシパントサービスは、パーティシパントのpreparecommitrollback メソッドの呼出しを行います。XTS 実装は、登録したパーティシパントすべてのローカルのステートをトラッキングします。prepare の呼出しがPrepared を返す場合、XTS 実装があると、コーディネータにprepared メッセージが転送される前にパーティシパントの状態が確実にローカルのトランザクションログに記録されるようにします。
パーティシパントのログ記録にはパーティシパント、トランザクション、コーディネータを特定する情報が含まれています。パーティシパントが登録され、prepared のステートに駆動されているにも拘らず、この情報があれば、再起動したXTS 実装がアクティブとしてパーティシパントを復帰し、コーディネータとのやりとりを十分に継続できます。ただし、プロセスのコミットあるいはロールバックを継続するにはパーティシパントインスタンスが必要です。
完全なリカバリを行うには、パーティシパントを登録したWeb サービスが必要とする情報がログ記録に含まれている必要になります。この情報があれば、同等のパーティシパントインスタンスを再構築可能になるはずですので、別のWeb Service がprepareに失敗した場合、commit プロセスを完了するまで継続するか、ロールバックすることができます。この情報は、パーティシパントが(Prepared の答えを返す前に永続化された)データの場所を突き止めることのできるString Keyのように単純な場合もあります。しかし、元のパーティシパントインスタンスやWeb サービスが作成した他のオブジェクトを含む直列化されたオブジェクトツリーのように複雑な場合もあります。
パーティシパントインスタンスが関連インターフェースを実装する場合、XTS実装は永続ストレージに書き込む前にこのパーティシパントのリカバリステートをログ記録に添付します。クラッシュ発生時、パーティシパントのリカバリステートはログから取得され、パーティシパントを作成したWeb Service に渡されます。Web Service はこのステートを使い新規パーティシパントを作成し、XTS 実装がこれを使いトランザクションを完了させます。ログ記録はパーティシパントのcommit あるいは rollbackメソッドが呼び出されてからでなければ、削除されません。

警告

クラッシュがcommit メソッド呼出し直後/直前に発生した場合、commit あるいは rollback メソッドは2度呼び出される場合があります。

23.1.2.1. WS-AT パーティシパントのクラッシュリカバリAPI

23.1.2.1.1. パーティシパントのリカバリステートを保存
リカバリ処理を実行可能であると合図を送るために、パーティシパントはjava.lang.Serializableインターフェースを実装可能です。あるいは、例23.1「PersistableATParticipant インターフェース」を実装することもできます。

例23.1 PersistableATParticipant インターフェース

	      public interface PersistableATParticipant
	      {
	        byte[] getRecoveryState() throws Exception;
	      }
パーティシパントがSerializable インターフェースを実装している場合、XTSパーティシパントサービスの実装はシリアル化APIを使い、パーティシパントのログエントリに追記できるパーティシパントのバージョンを作成します。また、PersistableATParticipantインターフェースを実装している場合、XTS パーティシパントサービスの実装はgetRecoveryState メソッドを呼出してステートを取得し、パーティシパントのログエントリに追記します。
これらのAPIがいずれも実装されていない場合、XTS 実装は警告メッセージをログに残しリカバリのステートを保存せずに進みます。コミット中にWeb サービス用のホストマシンでクラッシュが発生した場合、トランザクションはリカバリできず、ヒューリスティックな結果になってしまう可能性があります。この結果はコーディネータサービスが稼働するホストにログが残されます。
23.1.2.1.2. 再起動時のパーティシパントのリカバリ
Web サービスは、リカバリ処理に参加するためにデプロイ時にXTS 実装に登録し、アンデプロイ時に登録解除する必要があります。org.jboss.jbossts.xts.recovery.participant.atに定義されているXTSATRecoveryManager クラスを利用し登録は行われます。

例23.2 リカバリ用に登録

public abstract class XTSATRecoveryManager {
	. . .
 	public static XTSATRecoveryManager getRecoveryManager() ;
 	public void registerRecoveryModule(XTSATRecoveryModule module);
	public abstract void unregisterRecoveryModule(XTSATRecoveryModule module)
		throws NoSuchElementException;
	. . .
}
Web サービスは、org.jboss.jbossts.xts.recovery.participant.atにあるXTSATRecoveryModuleの実装をregisterunregister 呼出し両方への引数として提供する必要があります。このインスタンスは保存したパーティシパントリカバリ記録を特定し新規のリカバリ済みパーティシパントインスタンスを再作成します。

例23.3 XTSATRecoveryModule 実装

public interface XTSATRecoveryModule
{
    public Durable2PCParticipant
	deserialize(String id, ObjectInputStream stream) 
	  throws Exception;
    public Durable2PCParticipant
	recreate(String id, byte[] recoveryState) 
	  throws Exception;
}
パーティシパントのリカバリステートをシリアル化を使い保存した場合、リカバリモジュールのdeserialize メソッドがパーティシパントを再作成するために呼び出されます。通常、提供された入力ストリームからオブジェクトを読み取りキャストし返すにはリカバリモジュールが必要になります。PersistableATParticipant インターフェースを使ってパーティシパントのリカバリ状態を保存した場合、リカバリモジュールのrecreate メソッドを呼び出し、状態の保存時に提供されたバイトアレイからパーティシパントを再作成します。
XTS 実装はどのパーティシパントがどのリカバリモジュールに所属しているか特定できません。リカバリのステートがモジュールのWeb サービスに所属する場合、モジュールはパーティシパントインスタンスを返すだけでよくなっています。パーティシパントが別のWeb サービスにより作成された場合、モジュールはnullを返すはずです。deserialize あるいは recreate に対する引数として提供されたパーティシパントの識別子は、元のパーティシパントがトランザクションに登録された際にWeb サービスが利用した識別子です。リカバリ処理に参加しているWeb Servcies は、パーティシパントの識別子がサービス毎に一意であるよう確認するはずです。モジュールがパーティシパント識別子がWeb サービスに所属していると認識しているがパーティシパントの再作成ができない場合、例外をスローするでしょう。サービスがパーティシパントとビジネスロジック固有のトランザクション情報を関連付けることができない場合にこの状況が発生する可能性があります。
モジュールがシリアル化に依存し、XTS実装が保存したパーティシパントのリカバリステートを作成する場合でも、アプリケーションに再登録する必要があります。deserialization の操作は、Web サービス特有のクラスをローディング可能なクラスローダを使用する必要があります。XTS は、deserialize 操作の責任をリカバリモジュールに委譲することで、この要件を満たします。