28 Sagaを使用したアプリケーションの開発
この章では、マイクロサービスの原則を使用して構築されたアプリケーションに、マイクロサービス間の効率的なトランザクションを組み込むことを可能にするSaga APIについて説明します。Saga APIは、Eclipse MicroProfileの長時間実行アクション(LRA)仕様に密接に準拠しながら実装されます。
28.1 Oracle Databaseを使用したSagaの実装
Oracle Databaseをプラットフォームとして使用して、Sagaベースのオンクラウド・アプリケーションまたはオンプレミス・アプリケーションを構築できます。分散トランザクションを処理するためにOracle Databaseを使用してSagaを実装する場合、Oracle Databaseに組み込まれた堅牢な基礎となるインフラストラクチャを活用できます。Sagaインフラストラクチャは、マイクロサービスのグローバルなアプリケーション状態を維持しすることで、開発とスループットを向上します。
Saga実装にOracle Databaseを使用する主な利点を次に示します:
-
Saga実装はデータベースに統合されるため、Sagaのコード化、デプロイおよびメンテナンスが簡単になります。
-
EclipseのLRA標準に従って簡易化されたSaga注釈により、Oracle Databaseを使用したマイクロサービス間のトランザクションを簡単に有効化できます
-
マイクロサービス全体でデータの一貫性を実現できます。
-
Sagaのロールバック時にロールバックを処理するために、データベースにはロックフリーの予約可能列に基づいた自動補正ロジックが用意されています。
-
Advanced Queuing (AQ)およびOracle Transactional Event Queues (TxEventQ)メッセージング・プラットフォームがOracle Databaseに組み込まれているため、データベース・トランザクションの一部としてマイクロサービスでメッセージとイベントを作成および使用できます。
-
PL/SQLやJavaアプリケーションなど、各種の言語でサポートされています。
-
Sagaフレームワークでは、アプリケーション・ペイロードをJSON型で表すことをお薦めします。
-
マイクロサービス間のトランザクションが必要になるアプリケーションの可用性とスケーラビリティが向上します。そのようなトランザクションは、マイクロサービスに変換するモノリス・アプリケーション(トランザクションが複数の境界付けられたコンテキストにまたがる可能性があるもの)では一般的なものです。境界付けられたコンテキストとは、その内側にビジネス・ドメイン・モデルが存在する明示的な境界のことです。
28.2 Oracle Saga Frameworkの概要
Oracle Saga Frameworkは、Oracle Databaseを使用して構築されたマイクロサービス・アプリケーション用のSagasを実装および管理する基盤を提供します。フレームワークには、管理インタフェースとクライアント・インタフェースが用意されています。管理インタフェースを使用すると、SagaアプリケーションでSaga参加者とメッセージ・ブローカを構成して管理できます。クライアント・インタフェースを使用すると、アプリケーションでSagaの開始、参加およびファイナライズができます。Saga参加者は、メッセージ・チャネルおよびメッセージ伝播を使用して自動的に構成されます。Sagaフレームワークは補正に対応していて、Sagaがロールバックされた場合に影響を受けるデータを自動的にロールバックできます。Sagaフレームワークには、PL/SQLパッケージ、ディクショナリ表およびデータベースでのSagasを円滑化するAdvanced Queuing (AQ)統合が含まれています。
Oracle Sagaフレームワークは、Oracle Databaseの2つの重要な機能を利用します。予約可能列が更新されたときに適切なメタ情報の記録を可能にする、予約可能列がサポートされているトランザクション・レイヤーがあります。予約可能列のサポートにより、補正トランザクションが自動的に起動され、Sagaがロールバックされたときに予約可能列の更新の状態が元に戻されます。予約可能列は、トランザクション同時実行性を向上するためにOracleが提供するロックフリー予約機能の一部です。
データベースに統合されているもう1つの重要なレイヤーは、Oracle Advanced Queuing (AQ)テクノロジで構築されたイベント・キューです。AQは、多様なマイクロサービス参加者を接続する非同期メッセージング・プラットフォームを提供します。Sagaフレームワークでは、ハブアンドスポーク・トポロジをデプロイして、Sagaイニシエータ、コーディネータおよび参加者を接続します。Sagaメッセージ・ブローカは、このトポロジではハブとして機能します。
28.3 Sagaフレームワークの機能
Sagaフレームワークは、Oracle DatabaseにSagaを実装するために次の機能を備えています。
-
アクティブなSagaとSaga参加者を追加および管理するためのPL/SQL管理インタフェース
-
アプリケーションがSagaフレームワークと相互作用し、データベースSagaに参加してファイナライズ(コミットまたはロールバック)するためのPL/SQLおよびJavaインタフェース
-
Oracleのマイクロサービスに特化したAQメッセージ伝播インフラストラクチャによって円滑化される、Sagaに関与する複数の参加者間の非同期メッセージ通信
-
予約可能列の更新の記録および取り消されたSagaに対する変更をロールバックするトランザクションの自動補正を可能にするための予約可能列ベースのSagaファイナライズ(補正および完了)のサポート
-
ユーザーがSagaファイナライズを明示的に定義し、自動的に実行できるようにするためのユーザー定義のSagaファイナライズ(補正および完了)のサポート
-
確実にサービス間メッセージを1回のみ配信し、紛失や重複を防止するための強力なメッセージ・セマンティクス
-
個々のトランザクションをSaga IDにバインドするためのSaga IDのサポート
-
Sagaの参加者ごとのグローバルに一意な名前
-
システムにプロビジョニングされたSaga参加者のためのメッセージ・チャネルおよびメッセージ伝播の自動構成
-
Sagaの状態を維持するための各種ディクショナリ表
-
システム内のSagaおよびSaga参加者についてレポートするシステム定義のビュー
-
Eclipse Microprofile LRA仕様のエミュレーション
28.4 Sagaフレームワークの概念
Sagaの参加者とイニシエータ
Saga参加者は、Sagaトポロジ内のスポーク(参加者マイクロサービス)を表し、Sagaを開始してオーケストレートするか、Sagaに参加するために登録します。参加者はメッセージ・ブローカ(ローカルまたはリモート)に関連付けられ、オプションでローカルのSagaコーディネータに関連付けられます。このドキュメントでは、Sagaを開始するSaga参加者を「イニシエータ」と表現し、非イニシエータ参加者を「参加者」と表現します。SagaイニシエータはSagaコーディネータに関連付けられている必要があります。初期バージョンでは、Sagaフレームワークは、イニシエータのローカルSagaコーディネータ(同じPDB)のみをサポートしています。一方、Saga参加者はコーディネータに関連付ける必要はなく、イニシエータに対してリモートにできます。
参加者は誰でもSagaを開始できます。Sagaイニシエータは、Sagaを開始し、非同期メッセージを送信することで他のマイクロサービス参加者を登録するマイクロサービスです。
次の旅行代理店の例では、参加者マイクロサービスはフライト・サービス、ホテル・サービスおよび自動車サービスです。参加者マイクロサービスは、Sagaにかわって1つ以上の参加者トランザクションを実行します。参加者トランザクションの補正アクションはローカルPDBに保持されます。参加者は、各自のSagaのローカル状態を管理します。
トランザクション・コーディネータ
トランザクション・コーディネータは、Sagaトポロジ内でトランザクション・マネージャとして機能します。Sagaコーディネータは、SagaイニシエータにかわってSaga状態を維持します。
Sagaトポロジには、複数のSagaコーディネータを指定できます。初期リリースのSagaフレームワークでは、Saga参加者は、参加者に対してローカル(同じPDBおよびスキーマ)であるコーディネータにのみ関連付けることができます。ただし、コーディネータは、ローカルまたはリモートのメッセージ・ブローカに関連付けることができます。
メッセージ・ブローカ
メッセージ・ブローカは仲介サービスとして機能し、Sagaトポロジ内のハブを表します。ブローカは、複数のSaga参加者とそのコーディネータの間のメッセージ伝播のためのメッセージ配信サービスを提供します。各Saga参加者またはコーディネータは、ローカルまたはリモートのいずれかである1つのブローカに関連付けられます。ブローカはSagaの状態を管理しません。
管理者インタフェースとクライアント・インタフェース
Sagaフレームワークには、DBMS_SAGA_ADM
とDBMS_SAGA
の2つのPL/SQLパッケージがあります。
DBMS_SAGA_ADM
パッケージは、Sagaフレームワークの管理インタフェースを提供します。管理インタフェースを使用すると、Sagaエンティティおよび進行中のSagaを管理できます。
DBMS_SAGA
パッケージには、マイクロサービス・アプリケーションを構築するときにSagaを開始および完了するための開発者APIが用意されています。
Sagaを使用したマイクロサービスを作成しようとしているJava開発者は、このドキュメントで個別に定義されているSaga注釈の使用が必要になります。
Saga注釈
Sagasのイニシエータと参加者になるJavaアプリケーションは、開発の柔軟性と簡易性のためにSaga注釈を使用する必要があります。詳細は、このドキュメントの「Saga注釈を使用したJavaアプリケーションの開発」の項を参照してください。
アドバンスト・キューイング
Sagaインフラストラクチャでは、キューとイベント通知の間のメッセージ伝播などに、既存のOracle Advanced Queuing (AQ)テクノロジを利用します。AQは、Saga参加者およびコーディネータ用のトランザクション通信メカニズムです。AQには、Sagaに関与する複数の参加者間で非同期メッセージ通信を円滑にするために必要な配管チャネルおよびメッセージ・チャネルが用意されています。AQは、分散トランザクションなしで確実に1回かぎりのメッセージ伝播を提供します。
予約可能列
予約可能列は、自動補正機能を持つ列タイプです。予約可能列を使用すると、個々のデータベースで、参加者トランザクションの補正アクションを記録する予約ジャーナルを保持できます。補正アクションは、Sagaロールバックが発生した場合に予約可能列の値に対して自動的に実行され、ローカル・トランザクション変更を元に戻します。
参加者マイクロサービスが実行するsagaローカル・トランザクションは、データベース表の1つ以上の予約列を変更できます。Sagaでは、トランザクション変更時に予約可能列を利用して補正アクションを記録し、トランザクションの失敗時に補正アクションを自動的に起動します。予約可能列の補正アクションは、予約ジャーナルに記録されます。補正トランザクションにより、以前にロックされたリソースが解放されます。
ディクショナリ・ビュー
Sagaフレームワークには、システム内のSagaおよびSaga参加者についてレポートするシステム定義のビューが用意されています。システム定義のビューALL_MICROSERVICES
、CDB_MICROSERVICES
、DBA_MICROSERVICES
およびUSER_MICROSERVICES
では、システム内のSagaおよびSaga参加者をモニターできます。
次のビューには、システム内のすべての参加者が表示されます。
CDB_SAGA_PARTICIPANTS
DBA_SAGA_PARTICIPANTS
USER_SAGA_PARTICIPANTS
次のシステム定義のビューには、進行中のSagaの動的状態が表示されます:
CDB_SAGAS
DBA_SAGAS
USER_SAGAS
次のシステム定義のビューに、完了したSagaの状態が表示されます:
CDB_HIST_SAGAS
DBA_HIST_SAGAS
USER_HIST_SAGAS
完了したSagaに関する情報は30日間保持されます。この期間は、saga_hist_retention
データベース・パラメータを使用して構成できます。
次のシステム定義のビューには、未完了のSagaが表示されます:
CDB_INCOMPLETE_SAGAS
DBA_INCOMPLETE_SAGAS
USER_INCOMPLETE_SAGAS
次のビューには、未完了のSagaの予約可能列の詳細が示されます:
CDB_SAGA_PENDING
DBA_SAGA_PENDING
USER_SAGA_PENDING
次のビューには、各Sagaの詳細が示されます:
CDB_SAGA_DETAILS
DBA_SAGA_DETAILS
USER_SAGA_DETAILS
次のビューには、Sagasの保留中のファイナライズ・アクションが示されます:
CDB_SAGA_FINALIZATION
DBA_SAGA_FINALIZATION
USER_SAGA_FINALIZATION
関連項目:
『Oracle Databaseリファレンス』ガイド
ファイナライズ・メソッド
すべての参加者がそれぞれの参加者トランザクションをコミット(完了)またはロールバック(補正)すると、Sagaはファイナライズされます。Saga commit()
またはrollback()
操作により、Sagaは完了します。Sagaフレームワークを使用すると、暗黙的な予約可能列ベースのファイナライズに加えて、アプリケーション固有のファイナライズも実装できます。
Eclipse Microprofile LRA仕様
Sagaフレームワークは、Eclipse Microprofile LRA仕様をエミュレートし、一定の制限付きで同等の機能を提供します。Sagaフレームワークの初期バージョンには、次の制限があります。
-
ネストされたSagaはサポートされていません。
-
Sagaイニシエータとコーディネータが共存します。
28.5 Sagaフレームワークの初期化
初期化パラメータ
データベース初期化パラメータ・ファイルinit.ora
で、max_saga_duration
パラメータは、sagaが未完了とみなされるまでの時間(秒)を指定します。このパラメータのデフォルト値は86400秒です。この構成可能な期間を超えるSagaは未完了とみなされ、dbms_saga.rollback_saga()
APIを使用して終了する必要があります。Sagaイニシエータは、期間を超えたSagaを自動的にロールバックします。
init.ora
ファイルで、_use_saga_qtyp
パラメータを使用して、Sagaインフラストラクチャで使用されるメッセージ・キューイング・タイプを設定できます。デフォルトでは、_use_saga_qtyp
パラメータは0 (クラシックAQキューを使用)に設定されていますが、トランザクション・イベント・キュー(TEQ)に切り替える場合は、_use_saga_qtyp
パラメータを1に設定する必要があります。_use_saga_qtyp
パラメータの変更は、Sagaエンティティ(ブローカ、コーディネータまたは参加者)を作成する前に行う必要があります。Sagaエンティティの作成後に_use_saga_qtyp
パラメータを変更しても、Sagaで使用されるキューのタイプは変更されません。
アクセス制御のロール
Sagaフレームワークを使用すると、データベース管理者がデータベース・ユーザーに必要な権限を付与し、ユーザーによるSagaの管理と参加が可能になります。ユーザーには次のロールおよび権限を指定できます。
ユーザー・ロール | アクセス |
---|---|
|
|
|
このロールは、Saga参加者サービスに必要です。Sagaプリミティブは、 |
SAGA_CONNECT_ROLE |
このロールは、リモートのdblinkユーザーに付与されます。 |
SYS
は、データ・ディクショナリのすべてのディクショナリ表およびユーザーがアクセスできるビューを所有します。
28.6 Sagaトポロジの設定
Sagaフレームワークには、DBA (SAGA_ADM_ROLE
権限を保有)がSaga参加者、コーディネータおよびブローカの定義と管理に使用できる管理APIが用意されています。SYS.DBMS_SAGA_ADM
というSaga PL/SQLパッケージは、Saga管理インタフェースを実装します。参加者は、SYS.DBMS_SAGA_ADM
パッケージのプロシージャをスキーマおよびPDB内から起動する必要があります。
管理インタフェースには、Saga参加者およびブローカをプロビジョニングするために次のAPIが用意されています。
-
add_participant()
: 参加者とそのコーディネータを追加します -
add_broker()
: ブローカを明示的に作成します -
add_coordinator()
: コーディネータを明示的に作成します -
drop_participant()
: 参加者を削除します -
drop_coordinator()
: コーディネータを削除します -
drop_broker()
: ブローカを削除します
システムにプロビジョニングされたSaga参加者は、メッセージ・チャネルおよびメッセージ伝播を使用して自動的に構成されます。参加者を追加すると、SYS.SAGA_PARTICIPANT$
ディクショナリ表に対応するエントリが作成され、参加者の着信および送信のJavaトピックが作成されます。インバウンドおよびアウトバウンドのJavaトピックは、参加者の名前から導出されたシステム生成名でプロビジョニングされます。次の各項では、管理インタフェースの処理についてさらに詳しく説明します。
関連項目:
SYS.DBMS_SAGA_ADM
パッケージAPIの詳細な説明は、DBMS_SAGA_ADMを参照してください
28.6.1 メッセージ・ブローカの追加
Sagaフレームワークでは、メッセージ・ブローカはSaga参加者からのメッセージを受信してSaga受信者に伝播するメッセージ配信サービスとして機能します。
Sagaフレームワークでは、dbms_saga_adm.add_broker()
APIを使用してブローカをフレームワークに明示的に追加します。メッセージ・ブローカを作成すると、Saga参加者のメールボックスとして機能するJavaトピックが1つ作成されます。Javaトピックのシステム生成名の形式はSAGA$_<broker_name>_INOUT
であり、メッセージ・ブローカ名(broker_name
)はadd_broker()
コールへの入力として指定された識別子です。
SAGA_ADM_ROLE
(管理者)ロールはSagaメッセージ・ブローカの作成に必須です。
28.6.2 コーディネータの追加
Sagaコーディネータは、Sagaのトランザクション・マネージャとして機能します。Sagaコーディネータは、様々な参加者にわたってSagaの状態を管理します。
Sagaフレームワークでは、dbms_saga_adm.add_coordinator()
APIを使用してコーディネータをフレームワークに追加します。
ノート:
add_participant()
には引数としてコーディネータ名が必要であるため、add_coordinator()
APIは、add_participant()
APIを起動する前にコールする必要があります。準備ステップとプロビジョニング・ステップの説明は、「例: Sagaフレームワークの設定」を参照してください。
add_coordinator()
インタフェースは、次を実行します。
-
コーディネータのインバウンドとアウトバウンドのメッセージ・チャネル用にシステム定義のAQキューを作成します。
-
コーディネータとメッセージ・ブローカの間に双方向のメッセージ伝播チャネルを確立します。
28.6.3 参加者の追加
参加者は、データベースSagaへの参加を希望するアプリケーションまたはマイクロサービスを表す名前付きエンティティです。参加者を追加すると、参加者とメッセージ・ブローカの間に双方向のメッセージ伝播チャネルが設定されます。
Sagaフレームワークでは、dbms_saga_adm.add_participant()
APIを使用して参加者をフレームワークに追加します。
ノート:
add_participant()
を起動する前に、参加者およびブローカのデータベース(PDB)に対して前提条件ステップを完了します。準備ステップとプロビジョニング・ステップの説明は、「例: Sagaフレームワークの設定」を参照してください。
add_participant()
インタフェースは、次を実行します。
-
参加者のインバウンドおよびアウトバウンドのメッセージ・チャネル用にシステム定義のJavaトピックを作成します。
-
参加者とメッセージ・ブローカの間に双方向のメッセージ伝播チャネルを確立します。
28.6.4 参加者およびメッセージ・ブローカの管理
drop_participant()
、drop_coordinator()
およびdrop_broker()
APIを使用して、参加者、コーディネータおよびメッセージ・ブローカを削除できます。参加者、コーディネータまたはメッセージ・ブローカを削除すると、関連するJMSトピックも削除されます。
ノート:
-
参加者を削除できるのは、進行中のSagaがなく、参加者の着信キューに保留中のメッセージがない場合のみです。
-
コーディネータを削除できるのは、コーディネータに関連付けられた参加者がない場合のみです。
-
メッセージ・ブローカを削除できるのは、登録済の参加者および保留中のメッセージがない場合のみです。
関連項目:
SYS.DBMS_SAGA_ADM
パッケージの管理APIの詳細は、DBMS_SAGA_ADMを参照してください
28.6.5 メッセージ伝播
メッセージ伝播により、Sagaエンティティ(イニシエータ、参加者およびブローカ)間でメッセージが転送されます。参加者をSagaフレームワークに追加すると、メッセージ伝播が設定されます。メッセージ伝播ジョブは、参加者のアウトバウンド・トピックをブローカのINOUTトピックに接続し、ブローカのINOUTトピックを参加者のインバウンド・トピックに接続します。
-
参加者のアウトバウンド・トピックをブローカのINOUTトピックに伝播するには、メッセージ伝播ジョブで
add_participant()
インタフェースのdblink_to_broker
パラメータからのdblinkを使用します。 -
ブローカのINOUTトピックを参加者のインバウンド・トピックに伝播するには、メッセージ伝播ジョブで
add_participant()
インタフェースのdblink_to_participant
パラメータからのdblinkを使用します。特定の参加者へのメッセージの伝播は、その参加者宛のメッセージに対してのみ発生します。
28.6.6 ディクショナリ表について
グローバル一意識別子(GUID)は、すべてのSagaトランザクションを識別するために使用されます。sys.saga_finalization$
ディクショナリ表には、Sagaの補正アクションを完了するために必要な個々のステップが記録されます。
いくつかのディクショナリ表では、参加者データベースでSagaトランザクションに関連付けられた状態(メタデータ)を追跡します。このような表は、次のとおりです。
-
sys.saga_message_broker$
: この表には、Sagaブローカの情報が格納されます。行は、明示的なadd_broker()
コールを使用してsaga_message_broker$
表に挿入されます。ブローカはリモートまたはローカルにすることができ、この情報はsaga_message_broker$.remote
列を使用して取得されます。 -
sys.saga_participant$
: この表には、Sagaフレームワークの参加者およびコーディネータに関する情報が格納されます。行は、add_participant()
またはadd_coordinator()
コールを使用してsaga_participant$
表に挿入されます。参加者はリモートまたはローカルにすることができ、この情報はsaga_participant$.remote
列を使用して取得されます。 -
sys.saga$
:sys.saga$
表には、指定されたPDBで開始されるか(joinSaga()
を使用して)参加されたSagaのエントリが格納されます。 -
sys.saga_finalization$
: 参加者データベースのsaga_finalization$
表には、参加者トランザクションの一部として更新された一意の予約可能表ごとに、順序番号および予約ジャーナル情報が記録されます。saga_finalization$
表には、アプリケーション固有の補正に関する情報は保持されません。 -
sys.saga_participant_set$
: Sagaイニシエータが、Saga参加者を登録するためにAQ JMSメッセージを送信します。Saga AQ JMSメッセージでは、特別なJMSメッセージ・プロパティを使用して、saga_id
およびその他のSaga属性を参加者に示します。sys.saga_participant_set$
表のエントリは、Sagaに登録されている各参加者を追跡します。これらのエントリは、各参加者の登録およびファイナライズのステータスを追跡します。 -
sys.saga_pending$
: この表には、タイムアウトしたSagaの予約ジャーナル補正情報が記録されます。Sagaインフラストラクチャではこの情報を使用して、Sagaを強制的にコミットまたはロールバックします。 -
sys.saga_errors$
: この表には、様々なSagaに対応するエラー・メッセージが記録されます。
次の2つのデータベース・パラメータはsagaに影響します。
-
max_saga_duration
: このデータベース・パラメータは、Sagaがアクティブであるとみなされ、未完了とマークされない最大時間(秒)を定義します。max_saga_duration
はSaga期間のシステム・デフォルトであり、begin()
APIを使用してこの値をオーバーライドできます。いずれかの参加者がSaga期間内にSagaをファイナライズできない場合、Sagaは未完了とマークされます。未完了のSagaは、dbms_saga.rollback_saga()
インタフェースを使用してファイナライズできます。 -
saga_hist_retention
: このデータベース・パラメータは、完了したSagaに関する情報を保持するための最大時間(日数)を定義します。saga_hist_retention
のデフォルト値は30日です。
28.6.7 例: Sagaフレームワークの設定
次の例では、参加者マイクロサービスのセットと1つのブローカを構成します。
準備ステップ
この例では、1つのブローカ、2つの参加者(TravelAgencyとAirline)およびそれぞれのコーディネータを設定して構成します。次の準備ステップおよびプロビジョニング・ステップに従うと、2つの参加者が含まれるSagaトポロジが作成されます。
ステップ1から7はbrokerPDB
で、8から11はTravelAgency
で、12から15はAirlinePDB
で実行されます。
- ブローカをホストするために、
brokerPDB
というプラガブル・データベース(PDB)をプロビジョニングまたは指定します。 brokerPDB
で、ブローカとそのJMSトピックを所有するメールボックス・ユーザーを作成します。例:MB
。- ロール
SAGA_ADM_ROLE
をユーザーMB
に付与します。 - プロキシ・ユーザー
AirlineatMB
を作成します(ロール:SAGA_CONNECT_ROLE
)。 - プロキシ・ユーザー
TravelAgencyatMB
を作成します(ロール:SAGA_CONNECT_ROLE
)。 MBatAirline
スキーマを使用して、Airline PDBへのdblinkLinkToAirline
を作成します。MBatTravel
スキーマを使用して、Travel PDBへのdblinkLinkToTravelAgency
を作成します。- 旅行予約サービスをホストするために、
TravelAgency
というPDBをプロビジョニングまたは指定します。 TravelAgency
PDBで、ユーザーTA
を作成します。- プロキシ・ユーザー
MBatTravelAgency
を作成します(ロール:SAGA_CONNECT_ROLE
)。 TravelAgencyatMB
スキーマを使用して、brokerPDB
へのdblinkLinktoBroker
を作成します。- Airlineをホストするために、
AirlinePDB
というPDBをプロビジョニングまたは指定します。 AirlinePDB
で、ユーザーAirline
を作成します。- プロキシ・ユーザー
MBatAirline
を作成します(ロール:SAGA_CONNECT_ROLE
)。 AirlineatMB
スキーマを使用して、brokerPDB
へのdblinkLinktoBroker
を作成します。
この例のディクショナリ・エントリに関連付けられているDBAビューおよびエントリは、DBA_SAGA_PARTICIPANTS
ビューにあります。DBA_SAGA_PARTICIPANTS
ビューおよび次のDBAビューの詳細は、データベース・リファレンス・ガイドを参照してください。
-
DBA_SAGAS
-
DBA_HIST_SAGAS
-
DBA_INCOMPLETE_SAGAS
-
DBA_SAGA_DETAILS
-
DBA_PARTICIPANT_SET
-
DBA_SAGA_FINALIZATION
-
DBA_SAGA_PENDING
-
DBA_SAGA_ERRORS
プロビジョニング・ステップ
BrokerPDB
:
--Add a broker dbms_saga_adm.add_broker(name=>'TravelBroker', schema=>'MB');
TravelAgencyPDB
:
--Add the Saga coordinator(local to the initiator) dbms_saga_adm.add_coordinator( coordinator_name => 'TACoordinator', dblink_to_broker => 'LinktoBroker', mailbox_schema => 'MB', broker_name => 'TravelBroker', dblink_to_coordinator => 'LinkToTravelAgency' );
--Add the local Saga participant TravelAgency and its coordinator as below dbms_saga_adm.add_participant( participant_name=> 'TravelAgency', coordinator_name=> 'TACoordinator', dblink_to_broker=> 'LinktoBroker', mailbox_schema=> 'MB', broker_name=> 'TravelBroker', dblink_to_participant=> 'LinktoTravelAgency', callback_package => 'dbms_ta_cbk' );
AirlinePDB
:
--Add the local Saga participant Airline as below dbms_saga_adm.add_participant( participant_name=> 'Airline', dblink_to_broker=> 'LinktoBroker', mailbox_schema=> 'MB', broker_name=> 'TravelBroker', dblink_to_participant=> 'LinktoAirline' callback_package => 'dbms_airline_cbk' );
--Add a table with reservable column which maintains flight information Create table flights(id NUMBER primary key, seats NUMBER reservable constraint flights_const check (seats > 0));
28.7 PL/SQLインタフェースを使用したSagaの管理
PL/SQLを使用して、中間層を必要とせずに、データベースでパッケージ化されたマイクロサービス・アプリケーションを開発します。
PL/SQLパッケージDBMS_SAGA
を使用すると、PL/SQLを使用して、中間層でデータベースと通信する必要なく、データベースでパッケージ化されたマイクロサービス・アプリケーションを開発できます。DBMS_SAGA
パッケージには、クライアント・プログラムでデータベースSagaを開始して相互作用できるようにするPL/SQLインタフェースが用意されています。
関連項目:
-
DBMS_SAGA
パッケージAPIの詳細な説明は、DBMS_SAGAを参照してください
28.7.1 例: Saga PL/SQLプログラム
Sagaイニシエータ(TravelAgency)とSaga参加者(Airline)を表すPL/SQLサンプル・プログラムを次に示します。
例28-1 Sagaイニシエータ
declare saga_id RAW(16); request JSON; begin saga_id := dbms_saga.begin_saga('TravelAgency'); request := json('{"flight":"United"}'); dbms_saga.send_request(saga_id, 'Airline', request); end; /
例28-2 Airline
create or replace package dbms_airline_cbk as function request(saga_id in RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL) return JSON; end dbms_airline_cbk; / create or replace package body dbms_airline_cbk as function request(saga_id in RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL) return JSON as response JSON; tickets NUMBER; BEGIN BEGIN select seats into tickets from flights where id = json_value(payload, '$.flight') for update; IF tickets > 0 THEN response := json('{"result":"success"}'); update flights set seats = seats - 1 where id = json_value(payload, '$.flight'); ELSE response := json('{"result":"failure"}'); END IF; EXCEPTION WHEN OTHERS THEN response := json('{"result":"failure"}'); END; return response; end; end dbms_airline_cbk; /
例28-3 TravelAgency
create or replace package dbms_ta_cbk as procedure response(saga_id in RAW, saga_sender IN varchar2, payload IN JSON DEFAULT NULL); end dbms_ta_cbk; / create or replace package body dbms_ta_cbk as procedure response(saga_id in RAW, saga_sender IN varchar2, payload IN JSON DEFAULT NULL) as booking_result VARCHAR2(10); begin booking_result := json_value(payload, '$.result'); IF booking_result = 'success' THEN dbms_saga.commit_saga('TRAVELAGENCY', saga_id); ELSE dbms_saga.rollback_saga('TRAVELAGENCY', saga_id); END IF; end; end dbms_ta_cbk; /
28.8 Saga注釈を使用したJavaアプリケーションの開発
Sagasのイニシエータと参加者になるJavaアプリケーションは、開発の柔軟性と簡易性のためにSaga注釈を使用する必要があります。この項では、Javaアプリケーションで利用できるSaga注釈とLRA注釈の詳細について説明します。
ノート:
Sagaトポロジは、個別に作成する必要があります。
関連項目:
Sagaトポロジの作成の詳細は、「Sagaトポロジの設定」を参照してください
Saga注釈では、次の参加者タイプが使用されます。
Sagaイニシエータ
Sagaイニシエータは、Sagaのライフサイクルを担う特別なSaga参加者です。イニシエータがSagaを開始し、その他の参加者にSagaへの参加を招待し、Sagaをファイナライズします。イニシエータのみが、その他の参加者にSagaへの参加をリクエストするメッセージを送信できます。Sagaイニシエータは、Sagaコーディネータに関連付けられています。Sagaイニシエータは、MicroProfile LRA仕様で定義された標準の@LRA
注釈を使用して、JAX-RSアプリケーションがSagaを開始できるようにします。Sagaフレームワークの@LRA
、@Complete
および@Compensate
注釈のサポートは、それに対応するMicroProfile LRA仕様のLRA注釈と同様のものです。LRA IDのかわりに、Sagaフレームワークの@LRA
注釈では、Saga IDを初期化してアプリケーションに返します。イニシエータのJavaクラスは、このフレームワークに用意されているSagaInitiator
クラスを拡張する必要があります。
関連項目:
項タイトル"Sagaのインタフェースとクラス"の「SagaInitiator
クラス」
Saga参加者
Saga参加者は、SagaイニシエータのリクエストでSagaに参加します。参加者はイニシエータと直接通信できますが、その他の参加者とは直接通信できません。Saga参加者は、@Request
注釈を使用して、受信Sagaペイロードを処理するために呼び出したメソッドにマークを付けます。こうしたメソッドは、Sagaに必要なすべてのメタデータとペイロードが格納されるSagaMessageContext
オブジェクトを使用します。@Request
注釈付きのメソッドは、Sagaフレームワークがレスポンスで自動的にSagaイニシエータに送信するJSONオブジェクトを返します。参加者クラスは、SagaParticipant
を拡張する必要があります。
関連項目:
項タイトル"Sagaのインタフェースとクラス"の「SagaParticipant
クラス」と「SagaMessageContext
クラス」
28.8.1 LRA注釈とSaga注釈
Sagaフレームワークは、LRAフレームワークをエミュレートし、同等の機能を提供します。次に、Sagaフレームワークで使用される注釈のリストを示します。
表28-1 LRA注釈とSaga注釈
注釈 | 説明: |
---|---|
|
Sagaの場合、LRA注釈はSaga開始動作を制御します。 現在、Sagaは非同期モデルのみを使用して処理されます。現在、 Saga JAX-RSフィルタは、参加者(
現時点では、Sagaフレームワークは、イニシエータのJAX-RSエンドポイント(JAX-RSフィルタを使用)に対して |
@SagaConnection |
@SagaConnection 注釈が付いたメソッドを呼び出すたびに、JDBC接続の新しいインスタンスが生成されます。
ノート: 参加者に対して
|
|
Sagaフレームワークは、注釈付きメソッドへの入力引数として
SagaMessageContext オブジェクトを提供しています。詳細は、「SagaMessageContext クラス」を参照してください。@Complete 注釈付きメソッドのメソッド・シグネチャは、次のようになります。
public void airlineComplete(SagaMessageContext info)。 追加のメソッド・シグネチャについては、この項の最後にある「ノート」を参照してください。
|
|
Sagaフレームワークは、注釈付きメソッドへの入力引数として
SagaMessageContext オブジェクトを提供しています。詳細は、「SagaMessageContext クラス」を参照してください。@Compensate 注釈付きメソッドのメソッド・シグネチャは、次のようになります。
public void airlineCompensate(SagaMessageContext info) 追加のメソッド・シグネチャについては、この項の最後にある「ノート」を参照してください。
|
|
Saga参加者は、データベース内で ノート:
|
|
Sagaフレームワークは、注釈付きメソッドへの入力として |
|
Sagaフレームワークは、注釈付きメソッドへの入力として |
|
|
|
|
|
このメソッドがtrueを返す場合、参加者はSagaに参加します。それ以外の場合は、否定応答が返され、
|
|
|
ノート:
メソッドが関連付けられているすべての注釈には、3つのメソッド・シグネチャがサポートされます。たとえば、@Request
注釈に対する次のメソッド・シグネチャを考えてみます。
-
ProcessPayload (SagaMessageContext info)
-
ProcessPayload (URI sagaId)
-
ProcessPayload (URI sagaId, URI parentId)
SagaフレームワークはNESTED
のSagaをサポートしていないため、parentId
は常にnullになります。これらの代替シグネチャを使用するために、ユーティリティ・メソッドgetSagaMessageContext()
が用意されています。このメソッドにより、指定のSaga IDからSagaMessageContext
オブジェクトを取得します。
ノート:
-
すべてのデータベース操作は、
SagaMessageContext
クラスにあるデータベース接続オブジェクトを使用して実行する必要があり、明示的なトランザクションのコミットまたはロールバックはサポートされていません。 -
送信者値に複数の一致がある場合は、例外が発生します。
28.8.2 パッケージ化
Sagaフレームワークは、次の2つのライブラリで構成されています:
-
Sagaクライアント・ライブラリ
-
JAX-RSフィルタ
JAX-RSフィルタは、アプリケーションがJAX-RSアプリケーションであり、Sagasの開始に@LRA
注釈を使用する場合にのみ必要になります。
この2つのライブラリに関連するMaven座標は次のとおりです:
Sagaクライアント・ライブラリ
<dependency>
<groupId>com.oracle.database.saga</groupId>
<artifactId>saga-core</artifactId>
<version>${SAGA_VERSION}</version>
</dependency>
JAX-RSフィルタ
<dependency>
<groupId>com.oracle.database.saga</groupId>
<artifactId>saga-filter</artifactId>
<version>${SAGA_VERSION}</version>
</dependency>
28.8.3 構成
JAX-RSフィルタ構成
次のJAX-RSフィルタのパラメータは、プロパティ・ファイル(sagafilter.properties
とapplication.properties
のいずれかまたは両方)を使用して構成できます:
プロパティ名 | 説明: |
---|---|
|
|
osaga.filter.database.walletPath |
フィルタ接続に使用する必要があるウォレットへのパス |
osaga.filter.database.tnsPath |
|
osaga.filter.initiator.publisherCount |
Sagasの開始に使用されるパブリッシャ・プールでインスタンス化するパブリッシャの合計数 新しいSagaの開始が必要になるたびに、プールにある既存のパブリッシャが使用されます。Sagaが開始されると、パブリッシャは解放されてプールに戻されます。 |
osaga.filter.initiator.name |
|
ノート:
Saga JAX-RSフィルタは、環境内で唯一のJAX-RS LRAフィルタであることが必要です。複数のLRAフィルタがあると、不整合や予測できない結果が発生する可能性があります。
参加者構成
次の参加者ごとのパラメータは、プロパティ・ファイル(osaga.app.properties
とapplication.properties
のいずれかまたは両方):を使用して構成できます
プロパティ名 | 説明: |
---|---|
|
ノート:
|
osaga.<participant_name>.walletPath |
参加者接続に使用する必要があるウォレットへのパス ノート:
|
osaga.<participant_name>.tnsPath |
ノート:
|
osaga.<participant_name>.numListeners |
この参加者に割り当てられているキュー・パーティションごとのリスナーの数。 リスナーは、受信メッセージの処理に使用されます。実際には、参加者またはイニシエータに構成されているリスナーの数は、それと通信できるその他の参加者とイニシエータの数に応じて異なります。この数は、ペイロードの処理時間が長くなっているときや、同時リクエストの数が増えたときなど、その他の要因に応じて増やすことが必要になる場合があります。 |
osaga.<participant_name>.numPublishers |
パブリッシャは、参加者にリクエストを送信する必要があります。実際には、パブリッシャの数は、予想される同時Sagasの数に応じて異なります。
|
ノート:
numListeners
とnumPublishers
は独立したエンティティです。numListeners
は、着信リクエストに応答するスレッドの数を表します。numPublishers
は、イニシエータのパブリッシャ・スレッド数を表します。イニシエータごとに、信頼性のあるスループットのために、numListeners=numPublishers
を設定することが理想的です。
28.8.4 Sagaインタフェースとクラス
Sagaインタフェースとクラスでは、Sagaのサポートに必要な機能を記述します。この項では、Javaアプリケーションのインタフェースおよびクラスについて説明します。
28.8.4.1 Sagaインタフェース
Sagaインタフェースは、データベースSagaに参加、完了およびメタデータのリクエストのための手段を提供します。Sagaインタフェースは、次のメソッドを提供しています:
sendRequest(String recipient, String payload)
メソッド
* @param recipient - name of the participant to be enrolled
* @param payload - saga payload
public void sendRequest(String recipient, String payload)
説明: sendRequest(String recipient,String payload)
メソッドは、イニシエータ・レベルで呼び出され、参加者にメッセージ(ペイロード)を送信します。参加者がSagaに参加すると、イニシエータは@Response
注釈が付いたメソッドを呼び出します。それ以外の場合、イニシエータは@Reject
注釈が付いたメソッドを呼び出します。
ノート:
sendRequest(String recipient,String payload)
メソッドは、Saga注釈付きメソッドの有効範囲外で使用されると、デフォルトでトランザクションを自動的にコミットします。トランザクション境界のファイングレイン制御は、sendRequest(java.sql.Connection connection, String recipient, String payload)
メソッドを使用することで実現できます。
sendRequest(AQjmsSession session, TopicPublisher publisher, String recipient, String payload)
メソッド
ノート:
このメソッドは非推奨です。かわりに、sendRequest(java.sql.Connection connection, String recipient, String payload)
メソッドを使用します。
* @param session - user supplied saga session
* @param publisher - user supplied topic publisher
* @param recipient - name of the participant to be enrolled
* @param payload - saga payload
public void sendRequest(AQjmsSession session, TopicPublisher publisher, String recipient, String payload)
説明: このメソッドは、sendRequest(String recipient,String payload)
と同じですが、パラメータAQjmsSession
とTopicPublisher
を使用しています。
AQjmsSession
セッションとTopicPublisher
パブリッシャのパラメータ値が同じ場合は、単一のデータベース・トランザクションで複数のsendRequest()
コールを実行できます。その他のデータベース操作(DMLなど)も同じトランザクションに含めることができますが、その場合は、AQjmsSession
クラスのgetDBConnection()
メソッドを使用して取得したAQjmsSession
セッションに埋め込まれているデータベース接続を使用します。トランザクションは、AQjmsSession
のメソッドcommit()
またはrollback()
を使用すると、コミットまたはロールバックできます。
ノート:
TopicPublisher
(パブリッシャ)インスタンスは、SagaInitiator
クラスで宣言されたgetSagaOutTopicPublisher
(AQjmsSession
セッション)メソッドを使用して取得されます。
sendRequest(java.sql.Connection connection, String publisher, String recipient, String payload)
メソッド
* @param connection - user supplied JDBC connection
* @param publisher - user supplied topic publisher
* @param recipient - name of the participant to be enrolled
* @param payload - saga payload
public void sendRequest(java.sql.Connection connection, String publisher, String recipient, String payload)
説明: このメソッドは、sendRequest(String recipient,String payload)
と同じですが、パラメータjava.sql.Connection connection
とpublisher
を使用します。
指定されたjava.sql.Connection
接続を使用する場合は、単一のデータベース・トランザクションで複数のsendRequest()
コールを実行できます。その他のデータベース操作(DMLなど)も、指定されたデータベース接続を使用する場合、同じトランザクションに含めることができます。トランザクションは、Connection
のメソッドcommit()
またはrollback()
を使用すると、コミットまたはロールバックできます。
getSagaId()
メソッド
public String getSagaId()
説明: getSagaId()
メソッドは、SagaInitiator
クラスのメソッドbeginSaga()
またはgetSaga(String sagaId)
を使用してインスタンス化されたSagaオブジェクトに関連付けられたSaga識別子を返します。
commitSaga()
メソッド
public void commitSaga()
説明: commitSaga()
メソッドは、Sagaをコミットするためにイニシエータによって呼び出されます。その実行の一環として、@BeforeComplete
と@Complete
の注釈が付いたメソッドは、イニシエータ・レベルと参加者レベルの両方で呼び出されます。予約可能列の操作があれば、その操作は、@BeforeComplete
コールと@Complete
コールの間でファイナライズされます。
ノート:
commitSaga()
メソッドは、Saga注釈付きメソッドの有効範囲外で使用される場合、デフォルトでトランザクションを自動コミットします。トランザクション境界のファイングレイン制御は、commitSaga(java.sql.Connection connection)
メソッドを使用することで実現できます。
commitSaga(AQjmsSession session)
メソッド
ノート:
このメソッドは非推奨です。かわりに、commitSaga(java.sql.Connection connection)
メソッドを使用します。
* @param session - user supplied saga session
public void commitSaga(AQjmsSession session)
説明: このメソッドは、commitSaga()
メソッドと同じですが、AQjmsSession
セッションを使用します。
その他のデータベース操作(DMLなど)も同じトランザクションに含めることができますが、その場合は、AQjmsSession
クラスのgetDBConnection()
メソッドを使用して取得したAQjmsSession
セッションに埋め込まれているデータベース接続を使用します。トランザクションは、AQjmsSession
のメソッドcommit()
またはrollback()
を使用すると、コミットまたはロールバックできます。
commitSaga(java.sql.Connection connection)
メソッド
* @param connection - user supplied JDBC connection
public void commitSaga(java.sql.Connection connection)
説明: このメソッドは、commitSaga()
メソッドと同じですが、java.sql.Connection
接続を使用します。
その他のデータベース操作(DMLなど)も、指定されたデータベース接続を使用する場合、同じトランザクションに含めることができます。トランザクションは、Connection
のメソッドcommit()
またはrollback()
を使用すると、コミットまたはロールバックできます。
commitSaga(boolean force)
メソッド
* @param force - force commit flag
public void commitSaga(boolean force)
説明: このメソッドは、commitSaga()
メソッドと同じですが、force
フラグを使用します。
force
フラグは、Saga参加者が特別な状況で使用し、Sagaをローカルにコミットし、Sagaイニシエータからのファイナライズを待機することなくSagaコーディネータに通知します。
commitSaga(AQjmsSession session, boolean force)
メソッド
ノート:
このメソッドは非推奨です。かわりに、commitSaga(java.sql.Connection connection, boolean force)
メソッドを使用します。
* @param session - user supplied saga session
* @param force - force commit flag
public void commitSaga(AQjmsSession session, boolean force)
説明: このメソッドは、commitSaga()
メソッドと同じですが、AQjmsSession
セッションとforce
フラグを使用します。これは、commitSaga(AQjmsSession session)
メソッドとcommitSaga(boolean force)
メソッドの機能を組み合せます。
ノート:
メソッドcommitSaga(AQjmsSession session)
、commitSaga(boolean force)
およびcommitSaga(AQjmsSession session, boolean force)
は、Saga注釈付きメソッドの有効範囲外でのみ使用できます。Saga注釈付きメソッドは、デフォルトで新しいトランザクションを開始します。このトランザクションは、メソッドの実行後にコミットまたはロールバックされます。
commitSaga(java.sql.Connection connection, boolean force)
メソッド
* @param connection - user supplied JDBC connection
* @param force - force commit flag
public void commitSaga(java.sql.Connection connection, boolean force)
説明: このメソッドは、commitSaga()
メソッドと同じですが、java.sql.Connection
接続とforce
フラグを使用します。これは、commitSaga(java.sql.Connection connection)
メソッドとcommitSaga(boolean force)
メソッドの機能を組み合せます。
ノート:
メソッドcommitSaga(java.sql.Connection connection)
、commitSaga(boolean force)
およびcommitSaga(java.sql.Connection connection, boolean force)
は、Saga注釈付きメソッドの有効範囲外でのみ使用できます。Saga注釈付きメソッドは、デフォルトで新しいトランザクションを開始します。このトランザクションは、メソッドの実行後にコミットまたはロールバックされます。
rollbackSaga()
メソッド
public void rollbackSaga()
説明: rollbackSaga()
メソッドはSagaをロールバックし、@BeforeCompensate
と@Compensate
の注釈が付いたメソッドを呼び出します。予約可能列の操作があれば、その操作は、@BeforeCompensate
コールと@Compensate
コールの間でファイナライズされます。
ノート:
rollbackSaga()
メソッドは、Saga注釈付きメソッドの有効範囲外で使用される場合、デフォルトでトランザクションを自動コミットします。トランザクション境界のファイングレイン制御は、rollbackSaga(java.sql.Connection connection)
メソッドを使用することで実現できます。
rollbackSaga(AQjmsSession session)
メソッド
ノート:
このメソッドは非推奨です。かわりに、rollbackSaga(java.sql.Connection connection)
メソッドを使用します。
* @param session - user supplied saga session
public void rollbackSaga(AQjmsSession session)
説明: このメソッドは、rollbackSaga()
メソッドと同じですが、AQjmsSession
セッションを使用します。
その他のデータベース操作(DMLなど)も同じトランザクションに含めることができますが、その場合は、AQjmsSession
クラスのgetDBConnection()
メソッドを使用して取得したAQjmsSession
セッションに埋め込まれているデータベース接続を使用します。トランザクションは、AQjmsSession
のメソッドcommit()
またはrollback()
を使用すると、コミットまたはロールバックできます。
ノート:
rollbackSaga(AQjmsSession session)
メソッドは、Saga注釈付きメソッドの有効範囲外でのみ使用できます。Saga注釈付きメソッドは、デフォルトで新しいトランザクションを開始します。このトランザクションは、メソッドの実行後にコミットまたはロールバックされます。
rollbackSaga(java.sql.Connection connection)
メソッド
* @param connection - user supplied JDBC connection
public void rollbackSaga(java.sql.Connection connection)
説明: このメソッドは、rollbackSaga()
メソッドと同じですが、java.sql.Connection
接続を使用します。
その他のデータベース操作(DMLなど)も、指定されたデータベース接続を使用する場合、同じトランザクションに含めることができます。トランザクションは、Connection
のメソッドcommit()
またはrollback()
を使用すると、コミットまたはロールバックできます。
ノート:
rollbackSaga(java.sql.Connection connection)
メソッドは、Saga注釈付きメソッドの有効範囲外でのみ使用できます。Saga注釈付きメソッドは、デフォルトで新しいトランザクションを開始します。このトランザクションは、メソッドの実行後にコミットまたはロールバックされます。
rollbackSaga(boolean force)
メソッド
* @param force - force rollback flag
public void rollbackSaga(boolean force)
説明: このメソッドは、rollbackSaga()
メソッドと同じですが、force
フラグを使用します。
force
フラグは、Saga参加者が特別な状況で使用し、Sagaをローカルにロールバックし、Sagaイニシエータからのファイナライズを待機することなくSagaコーディネータに通知します。
rollbackSaga(AQjmsSession session, boolean force)
メソッド
ノート:
このメソッドは非推奨です。かわりに、rollbackSaga(java.sql.Connection connection, boolean force)
メソッドを使用します。
* @param session - user supplied saga session
* @param force - force commit flag
public void rollbackSaga(AQjmsSession session, boolean force)
説明: このメソッドは、rollbackSaga()
メソッドと同じですが、AQjmsSession
セッションとforce
フラグを使用します。これは、rollbackSaga(AQjmsSession session)
メソッドとrollbackSaga(boolean force)
メソッドの機能を組み合せます。
ノート:
メソッドrollbackSaga(AQjmsSession session)
、rollbackSaga(boolean force)
およびrollbackSaga(AQjmsSession session, boolean force)
は、Saga注釈付きメソッドの有効範囲外でのみ使用できます。Saga注釈付きメソッドは、デフォルトで新しいトランザクションを開始します。このトランザクションは、メソッドの実行後にコミットまたはロールバックされます。
rollbackSaga(java.sql.Connection connection, boolean force)
メソッド
* @param connection - user supplied JDBC connection
* @param force - force commit flag
public void rollbackSaga(java.sql.Connection connection, boolean force)
説明: このメソッドは、rollbackSaga()
メソッドと同じですが、java.sql.Connection
接続とforce
フラグを使用します。これは、rollbackSaga(java.sql.Connection connection)
メソッドとrollbackSaga(boolean force)
メソッドの機能を組み合せます。
ノート:
rollbackSaga(java.sql.Connection connection)
、rollbackSaga(boolean force)
およびrollbackSaga(java.sql.Connection connection, boolean force)
は、Saga注釈付きメソッドの有効範囲外でのみ使用できます。Saga注釈付きメソッドは、デフォルトで新しいトランザクションを開始します。このトランザクションは、メソッドの実行後にコミットまたはロールバックされます。
isSagaFinalized()
メソッド
public boolean isSagaFinalized()
説明: isSagaFinalized()
メソッドは、イニシエータ・レベルまたは参加者レベルで呼び出すことで、Sagaがいずれかのファイナライズ状態に達したかどうかをチェックできます。Sagaの状態がJOINING
、JOINED
またはTIMEDOUT
の場合はfalseを返し、それ以外の場合はtrueを返します。
beginSagaTransaction(AQjmsSession session, TopicPublisher publisher)
メソッド
ノート:
このメソッドは非推奨です。かわりに、beginSagaTransaction(java.sql.Connection connection)
メソッドを使用します。
* @param session - user supplied saga session
* @param publisher - user supplied topic publisher
public void beginSagaTransaction(AQjmsSession session, TopicPublisher publisher)
説明: beginSagaTransaction(AQjmsSession session, TopicPublisher publisher)
メソッドは、新しいSagaトランザクションを開始するために、Saga注釈付きメソッド(@LRA annotated
メソッドなど)の有効範囲外で使用されます。イニシエータ・レベルでのみ呼出しできます。Sagaトランザクション内で実行されるすべての操作は、AQjmsSession
のメソッドcommit()
またはrollback()
を明示的にコールすることで、コミットまたはロールバックされます。beginSagaTransaction(AQjmsSession session, TopicPublisher publisher)
コールには、Sagaトランザクションを完了するための対応するendSagaTransaction()
コールが必要です。
ノート:
beginSagaTransaction(AQjmsSession session, TopicPublisher publisher)
メソッドは、その他のSaga関連メソッドのcommitSaga()
やrollbackSaga()
などを呼び出すことでシリアライズされます。
beginSagaTransaction(java.sql.Connection connection, String publisher)
メソッド
* @param connection - user supplied JDBC connection
* @param publisher - user supplied topic publisher
public void beginSagaTransaction(java.sql.Connection connection, String publisher)
説明: beginSagaTransaction(java.sql.Connection connection)
メソッドは、新しいSagaトランザクションを開始するために、Saga注釈付きメソッドの有効範囲外(@LRA
注釈付きメソッドの下など)で使用します。イニシエータ・レベルでのみ呼出しできます。Sagaトランザクション内で実行されるすべての操作は、Connection
のメソッドcommit()
またはrollback()
を明示的にコールすることで、コミットまたはロールバックされます。beginSagaTransaction(java.sql.Connection connection)
コールには、Sagaトランザクションを完了するための対応するendSagaTransaction()
コールが必要です。
ノート:
beginSagaTransaction(java.sql.Connection connection)
メソッドは、その他のSaga関連メソッドのcommitSaga()
やrollbackSaga()
などを呼び出すことでシリアライズされます。
endSagaTransaction()
メソッド
public void endSagaTransaction()
説明: endSagaTransaction()
メソッドは、beginSagaTransaction()
コールで開始したSagaトランザクションを終了します。endSagaTransaction()
メソッドは、Sagaトランザクションがロック・フラグをtrueに設定して開始されていた場合、Sagaのロックを解除します。
28.8.4.2 SagaMessageContextクラス
SagaMessageContext
オブジェクトは、Saga関連イベントのSaga注釈が付いたメソッドに引数として渡されます。SagaMessageContext
オブジェクトは、Saga注釈付きメソッドをトリガーしたSagaに関連付けられたメタデータをフェッチするために使用できます。
SagaMessageContext
クラスには、次のメソッドがあります。
getSagaId()
メソッド
構文: public String getSagaId()
説明: getSagaId()
メソッドは、Sagaの識別子を返します。
getSender()
メソッド
構文: public String getSender()
説明: getSender()
メソッドは、メッセージ送信者の名前を返します。
getPayload()
メソッド
構文: public String getPayload()
説明: getPayload()
メソッドは、Sagaメッセージに関連付けられたペイロードを返します。
getConnection()
メソッド
構文: public java.sql.Connection getConnection()
説明: getConnection()
メソッドは、Saga注釈付きメソッドで、Sagaメッセージ・リスナー・スレッドのデータベース接続オブジェクトを返すために使用できます。イニシエータまたは参加者レベルで使用できます。この接続は、アプリケーションでデータベース操作を実行するために使用できます。
Saga注釈付きメソッドでは、明示的なcommit()
またはrollback()
はサポートされていません。
getSaga()
メソッド
構文: public Saga getSaga()
説明: getSaga()
メソッドは、Saga注釈付きメソッドをトリガーしたSagaに関連付けられたSagaオブジェクトを返します。このSagaオブジェクトは、そのSagaのファイナライズや、そのSagaのメタデータのリクエストに使用できます。
28.8.4.3 SagaParticipantクラス
SagaParticipant
クラスは、Saga参加者インスタンスの作成と管理のための手段を提供します。Saga参加者は、DBMS_SAGA_ADM
パッケージで提供されるadd_participant()
PL/SQL APIを使用して作成されます。
関連項目:
SYS.DBMS_SAGA_ADM
パッケージAPIの詳細な説明は、DBMS_SAGA_ADMを参照してください。
SagaParticipant
クラスは、次のメソッドを公開しています。
addSagaMessageListener()
メソッド
public void addSagaMessageListener()
説明: addSagaMessageListener()
メソッドによって、Saga参加者はメッセージ・リスナー・スレッドを追加できます。Sagaメッセージ・リスナー・スレッドを追加することで、Sagaメッセージの処理時の同時実行性が向上します。
ノート:
addSagaMessageListener()
では、メッセージ・リスナー・スレッドが1つのみ追加されます。複数のリスナーを一度に追加する場合は、addSagaMessageListener(int numListeners)
メソッドを使用する必要があります。
addSagaMessageListener(int numListeners)
メソッド
* @param numListeners - number of listeners to add
public void addSagaMessageListener(int numListeners)
説明: addSagaMessageListener(int numListeners)
メソッドを使用すると、Saga参加者は、さらにnumListeners
と同数のメッセージ・リスナー・スレッドを追加できます。Sagaメッセージ・リスナー・スレッドを追加するほど、多くのSagaメッセージを同時に処理できるようになります。
removeSagaMessageListener()
メソッド
public void removeSagaMessageListener()
説明: removeSagaMessageListener()
メソッドによって、Saga参加者はメッセージ・リスナー・スレッドを削除できます。負荷が低い状況下では、Sagaメッセージ・リスナー・スレッドを減らすことでシステム・リソースを解放できます。
ノート:
removeSagaMessageListener()
メソッドでは、メッセージ・リスナー・スレッドが1つのみ削除されます。複数のリスナーを一度に削除する場合は、removeSagaMessageListener (int numListeners)
メソッドを使用する必要があります。
removeSagaMessageListener(int numListeners)
メソッド
* @param numListeners - number of listeners to remove
public void removeSagaMessageListener(int numListeners)
説明: removeSagaMessageListener(int numListeners)
メソッドによって、Saga参加者はnumListeners
個のメッセージ・リスナー・スレッドを削除できます。負荷が低い状況下では、Sagaメッセージ・リスナー・スレッドを減らすことでシステム・リソースを解放できます。
removeAllSagaMessageListeners()
メソッド
public void removeAllSagaMessageListeners()
説明: Saga参加者は、removeAllSagaMessageListeners()
メソッドですべてのメッセージ・リスナー・スレッドを削除できます。このメソッドを呼び出すと、Sagaメッセージの受信時にSaga固有の注釈が付いたメソッドが実行されなくなります。このメソッドは、Saga注釈が付いたメソッドのビジネス・ロジックの変更が必要な場合に使用できます。未処理のSagaメッセージは、Sagaメッセージ・キューに保持され、メッセージ・リスナーの再起動後に処理できます。
getSagaMessageListenerCount()
メソッド
public int getSagaMessageListenerCount()
説明: getSagaMessageListenerCount()
メソッドは、Saga参加者に関連付けられたSagaメッセージ・リスナー・スレッドの合計数を返します。
ノート:
AQキュー設定の場合、getSagaMessageListenerCount()
は、Saga参加者の単一パーティションに関連付けられたメッセージ・リスナー・スレッド数を返します。合計メッセージ・リスナー・スレッド数は、getSagaMessageListenerCount() * numPartitions
によって取得されます。これは、add_participant
コール時にされています。参加者に構成されたパーティションの数は、DBA_SAGA_PARTITIPANTS
ディクショナリ・ビューを使用することでも問合せできます。
getSaga(String sagaId)
メソッド
* @param sagaId - Identifier for the saga to be retrieved
public Saga getSaga(String sagaId)
説明: getSaga(String sagaId)
メソッドは、指定したSaga識別子のSagaに関連付けられているSagaオブジェクトを返します。
close()
メソッド
public void close()
説明: close()
メソッドは、Saga参加者に関連付けられたすべてのメッセージ・リスナー・スレッドと接続を削除します。Saga参加者がクローズされると、それ以降、Saga注釈が付いたメソッドは呼び出されません。Saga参加者は、データベースSagaに対して参加、完了およびメタデータのリクエストはできません。その参加者の保留中のSagaメッセージは保持され、その後で使用できます。
28.8.4.4 SagaInitiatorクラス
SagaInitiator
クラスは、Sagaイニシエータ・インスタンスの作成と管理のための手段を提供します。Sagaイニシエータは、dbms_saga_adm
パッケージで提供されるadd_participant()
PL/SQL APIを使用して作成されます。SagaInitiator
クラスは、SagaParticipant
クラスで指定されたすべてのメソッドを継承します。さらに、SagaInititor
クラスは、次のメソッドを公開しています。
beginSaga()
メソッド
public Saga beginSaga()
説明: beginSaga()
メソッドは、Sagaを起動してsagaId
を返します。sagaId
を使用すると、その他の参加者を登録できます。
ノート:
beginSaga()
メソッドは、デフォルトの84600秒のタイムアウトを使用してSagaを起動します。タイムアウトのファイングレイン制御は、beginSaga(int timeout)
を使用することで実現できます。
beginSaga(int timeout)
メソッド
* @param timeout - saga timeout
public Saga beginSaga(int timeout)
説明: beginSaga(int timeout)
メソッドは、ユーザーが指定したタイムアウトでSagaを起動してSaga識別子を返します。Saga識別子を使用すると、その他の参加者を登録できます。ノート: 指定したタイムアウトの前にSagaがファイナライズされない場合、Sagaはデータベース初期化パラメータ_saga_timeout_operation_type(default=rollback)
に応じて自動的にファイナライズします。
addSagaMessagePublishers(int numPublishers)
メソッド
* @param numPublisher - number of publishers to add
public void addSagaMessagePublishers(int numPublishers)
説明: addSagaMessagePublishers(int numPublishers)
メソッドを使用すると、Sagaイニシエータは、さらにnumPublisher
と同数のメッセージ・パブリッシャ・スレッドを追加できます。Sagaメッセージ・パブリッシャ・スレッドを追加するほど、イニシエータは多くのSagasを同時に作成および管理できます。
removeSagaMessagePublishers(int numPublishers)
メソッド
* @param numPublisher - number of publishers to remove
public void removeSagaMessagePublishers(int numPublishers)
説明: removeSagaMessagePublishers(int numPublishers)
メソッドを使用すると、SagaイニシエータはnumPublishers
個のメッセージ・パブリッシャ・スレッドを削除できます。負荷が低い状況下では、Sagaメッセージ・パブリッシャ・スレッドを減らすことでシステム・リソースを解放できます。
removeAllSagaMessagePublishers()
メソッド
public void removeAllSagaMessagePublishers()
説明: Sagaイニシエータは、removeAllSagaMessagePublishers()
メソッドですべてのメッセージ・パブリッシャ・スレッドを削除できます。今後イニシエータがSagaを開始または管理しない場合は、すべてのメッセージパブリッシャ・スレッドを削除できます。Sagaメッセージ・パブリッシャを削除しても、それまでに公開された保留中のメッセージに影響はありません。
getSagaMessagePublisherCount()
メソッド
public int getSagaMessagePublisherCount()
説明: getSagaMessagePublisherCount()
メソッドは、Sagaイニシエータに関連付けられたSagaメッセージ・パブリッシャ・スレッドの合計数を返します。
ノート:
AQキュー設定の場合、getSagaMessagePublisherCount()
メソッドは、Saga参加者の単一パーティションに関連付けられたメッセージ・パブリッシャ・スレッド数を返します。合計メッセージ・パブリッシャ・スレッド数は、getSagaMessagePublisherCount() * numPartitions
によって取得されます。これは、add_participant()
コール時に定義されています。参加者に構成されたパーティションの数は、DBA_SAGA_PARTITIPANTS
ディクショナリ・ビューを使用することでも問合せできます。
getSagaOutTopicPublisher(AQjmsSession session, int partition)
メソッド
* @param session - user supplied saga session
* @param partition - partition for which the publisher is needed
public TopicPublisher getSagaOutTopicPublisher(AQjmsSession session, int partition)
説明: getSagaOutTopicPublisher(AQjmsSession session, int partition)
メソッドは、Sagaトピック・パブリッシャ・インスタンスを返します。TopicPublisher
インスタンスは、手動でトランザクション境界を管理するためのユーザー定義のAQjmsSession
を受け入る各種メソッドにパラメータとして指定できます。
ノート:
AQキュー設定の場合、パーティションの値はadd_participant
コールで指定された[1-numPartitions]
にすることもできます。TxEventQキュー設定の場合、パーティションの値は常に1にする必要があります。
28.8.5 サンプル・プログラム
次の例では、単純なTravel Agency Sagaアプリケーションを通じて、重要なSaga注釈のサブセットの使用について説明します。Travel Agencyは、実際の旅行代理店をエミュレートして、そのエンド・ユーザーのためにAirline (航空便)の予約を実行します。Travel Agencyは、JAX-RSアプリケーション(イニシエータ)であり、1つの参加者Airlineを使用します。Airlineは、標準Javaアプリケーションとして実装されています(JAX-RSではありません)。
Travel AgencyとAirlineは、それらのタスクの実行にとって適切なSaga注釈を活用します。この例では、TravelAgency
がSagaイニシエータで、エンド・ユーザーのために航空便チケットを購入するSagaを開始します。Airline
は、Saga参加者です。この例は、参加者がSagaに1つのみの予約を入れる単純な解説です。実際には、単一のSagaが複数の参加者に対応することもあります。
ノート:
アプリケーション開発者に求められることは、この例に示すように、注釈付きメソッドを実装することのみです。Sagaフレームワークは、必要な通信を提供して、分散トポロジ全体でSagaの状態を維持します。
ノート:
注釈の@LRA
、@Compensate
、@Complete
はLRA注釈です。また、注釈の@participant
、@Request
および@Response
はSaga注釈です。
例28-4 TravelAgency (Sagaイニシエータ)
@Participant(name = "TravelAgency")
/* @Participant declares the participant’s name to the saga framework */
public class TravelAgencyController extends SagaInitiator {
/* TravelAgencyController extends the SagaInitiator class */
@LRA(end = false)
/* @LRA annotates the method that begins a saga and invites participants */
@POST("booking")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.APPLICATION_JSON)
public jakarta.ws.rs.core.Response booking(
@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId,
String bookingPayload) {
Saga saga = this.getSaga(lraId.toString());
/* The application can access the sagaId via the HTTP header
and instantiate the Saga object using it */
try {
/* The TravelAgency sends a request to the Airline sending
a JSON payload using the Saga.sendRequest() method */
saga.sendRequest ("Airline", bookingPayload);
response = Response.status(Response.Status.ACCEPTED).build();
} catch (SagaException e) {
response=Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
@Response(sender = "Airline.*")
/* @Response annotates the method to receive responses from a specific
Saga participant */
public void responseFromAirline(SagaMessageContext info) {
if (info.getPayload().equals("success")) {
saga.commitSaga ();
/* The TravelAgency commits the saga if a successful response is received */
} else {
/* Otherwise, the TravelAgency performs a Saga rollback */
saga.rollbackSaga ();
}
}
}
例28-5 Airline (Saga参加者)
@Participant(name = "Airline")
/* @Participant declares the participant’s name to the saga framework */
public class Airline extends SagaParticipant {
/* Airline extends the SagaParticipant class */
@Request(sender = "TravelAgency")
/* The @Request annotates the method that handles incoming request from a given
sender, in this example the TravelAgency */
public String handleTravelAgencyRequest(SagaMessageContext
info) {
/* Perform all DML with this connection to ensure
everything is in a single transaction */
FlightService fs = new
FlightService(info.getConnection());
fs.bookFlight(info.getPayload(), info.getSagaId());
return response;
/* Local commit is automatically performed by the saga framework.
The response is returned to the initiator */
}
@Compensate
/* @Compensate annotates the method automatically called to roll back a saga */
public void compensate(SagaMessageContext info) {
fs.deleteBooking(info.getPayload(),
info.getSagaId());
}
@Complete
/* @Complete annotates the method automatically called to commit a saga */
public void complete(SagaMessageContext info) {
fs.sendConfirmation(info.getSagaId());
}
}
ノート:
このサンプル・プログラムには、importディレクティブは含まれていません。
その他のマイクロサービス(Hotel
やCarRental
など)は、Airlineマイクロサービスと同様の構造を持ちます。
Travel Agencyは、別の参加者に対応するように次の方法で変更できます。レンタカー・サービスの追加について考えてみます:
-
TravelAgencyControllerのbookingメソッドでは、"Car"参加者にペイロードを送信するために別の
sendRequest()
が必要になります。Saga.sendRequest ("Car", bookingPayload.getCar().toString());
-
Carサービスからのレスポンスを処理するために、新しい注釈付きメソッドが必要です。
@Response(sender = "Car”)
-
@Response
メソッドには、参加者からのメッセージの受信後に予約の状態を把握するための追加のロジックが必要になります。@Response(sender = "Car") public void responseFromCar(SagaMessageContext info) { if (!info.getPayload().equals("success")) { /* On error, rollback right away */ Saga.rollbackSaga (); } else { /* Store the car's response */ cache.putCarResponse(info.getSagaId(), info.getPayload()); /* Check to see if Airline has responded If a response is found, commit */ if (cache.containsAirlineResponse(info.getSagaId())) { Saga.commitSaga (); } } }
Airlineのレスポンスを処理する方法は同様ですが、 cache.containsAirlineResponse(info.getSagaId())
をcache.containsCarResponse(info.getSagaId())
に、cache.putAirlineResponse()
をcache.putCarResponse()
に置換します。
次の非同期フロー図は、前述のコード例で示したSagaアプリケーションのプロセス・フローを示しています。フロー図の番号は、コード・スニペットに示した番号に対応しています。
関連項目:
Sagaライフサイクルの詳細は、「付録: Sagaフレームワークのトラブルシューティング」を参照してください
図28-1 非同期フロー

Sagaプロパティ・ファイル
次に、サンプル・プログラムで使用したSagaプロパティ・ファイルを示します。
# JAX-RS filter properties osaga.filter.database.tnsAlias=ta osaga.filter.database.walletPath=/Path/to/wallet osaga.filter.database.tnsPath=/Path/to/tns osaga.filter.initiator.publisherCount=2 osaga.filter.initiator.name=TravelAgency osaga.travelagency.tnsAlias=ta # Property values for the Initiator Travelagency osaga.travelagency.walletPath=/Path/to/wallet osaga.travelagency.tnsPath=/Path/to/tns osaga.travelagency.numListeners=2 osaga.travelagency.numPublishers=2 # Properties pertaining to the Airline participant osaga.airline.tnsAlias=airline1 osaga.airline.walletPath=/Path/to/wallet osaga.airline.tnsPath=/Path/to/tns osaga.airline.numListeners=2 osaga.airline.numPublishers=2
28.9 明示的なSagaのファイナライズ
自動予約可能列ベースのファイナライズに加えて、Sagaフレームワークでは、アプリケーション固有の明示的な(ユーザー定義の)ファイナライズ(完了または補正)をサポートしています。フレームワークは、Sagaファイナライズ時にアプリケーション固有のファイナライズ・ルーチンを自動的に起動します。
アプリケーション固有のファイナライズは、PL/SQLクライアントにPL/SQLコールバック・ルーチンを使用することで指定できます。
ノート:
PL/SQLコールバック・パッケージを使用して実装されているユーザー定義のコールバックから、明示的なCOMMIT
コマンドが発行されていないことを確認してください。Sagaフレームワークは、アプリケーションのかわりに変更をコミットします。
Javaについては、「Saga注釈を使用したJavaアプリケーションの開発」で、アプリケーション固有の補正の実装にJavaプログラムで使用できる注釈の@BeforeComplete
、@BeforeCompensate
および@Complete
、@Compensate
ついて説明しています。その場合、ロックフリー予約は、@BeforeCompensate
と@Compensate
注釈の付いたメソッド間で自動的に補正されます。
関連項目:
28.9.1 PL/SQLクライアント用のPL/SQLコールバック
PL/SQLベースのイニシエータまたは参加者は、Sagaに参加するためにコールバック・パッケージを実装する必要があります。コールバック・パッケージは、各参加者の通知コールバックから内部的にコールされます。コールバック・パッケージを使用すると、Sagaインフラストラクチャの内部詳細を無視してビジネス・ロジックに集中できます。
アプリケーションでは、コールバック・パッケージの実装によってデータベース・トランザクション制御操作(コミット/ロールバック)が起動されないようにする必要があります。
コールバック・パッケージは、次の機能を提供します:
request
ファンクション
構文: function request(saga_id IN RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL) return JSON;
説明: requestファンクションは、オペコードREQUEST
が指定されたSagaメッセージを受信したときに呼び出されます。アプリケーションはこのメソッドを実装する必要があります。イニシエータにレスポンスとして送り返されるJSONペイロードが戻されます。
response
プロシージャ
構文: procedure response(saga_id IN RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL)
説明: responseプロシージャは、オペコードRESPONSE
が指定されたSagaメッセージを受信したときに起動されます。アプリケーションはこのメソッドを実装する必要があります。
before_commit
プロシージャ
構文: procedure before_commit(saga_id IN RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL)
説明: before_commit
プロシージャは、同じトランザクションのオペコードCOMMIT
が指定されたSagaメッセージを受信したときに、現在の参加者に対してSagaコミットを実行する前に起動されます。before_commit()
プロシージャは、ロックフリー予約のコミット前に呼び出されます。アプリケーションは、このメソッドを実装しているものと想定されていて、ロックフリー予約を使用するアプリケーションは、before_commit()
プロシージャとafter_commit()
プロシージャのどちらを実装するかを選択できます。
after_commit
プロシージャ
構文: procedure after_commit(saga_id IN RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL)
説明: after_commit
プロシージャは、同じトランザクションのオペコードCOMMIT
が指定されたSagaメッセージを受信したときに、現在の参加者に対してSagaコミットを実行した後に起動されます。after_commit
プロシージャは、ロックフリー予約のコミット後に呼び出されます。アプリケーションは、このメソッドを実装しているものと想定されていて、ロックフリー予約を使用するアプリケーションは、before_commit()
プロシージャとafter_commit()
プロシージャのどちらを実装するかを選択できます。
before_rollback
プロシージャ
構文: procedure before_rollback(saga_id IN RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL)
説明: before_rollback
プロシージャは、同じトランザクションのオペコードROLLBACK
が指定されたSagaメッセージを受信したときに、現在の参加者に対してSagaロールバックを実行する前に起動されます。before_rollback
プロシージャは、ロックフリー予約のロールバック前に呼び出されます。アプリケーションは、このプロシージャを実装しているものと想定されていて、ロックフリー予約を使用するアプリケーションは、before_rollback
プロシージャとafter_rollback
プロシージャのどちらを実装するかを選択できます。
after_rollback
プロシージャ
構文: procedure after_rollback(saga_id IN RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL)
説明: after_rollback
プロシージャは、同じトランザクションのオペコードROLLBACK
が指定されたSagaメッセージを受信したときに、現在の参加者に対してSagaロールバックを実行した後に起動されます。after_rollback
プロシージャは、ロックフリー予約のロールバック後に呼び出されます。アプリケーションは、このプロシージャを実装しているものと想定されていて、ロックフリー予約を使用するアプリケーションは、before_rollback
プロシージャとafter_rollback
プロシージャのどちらを実装するかを選択できます。
forget
プロシージャ
構文: procedure forget(saga_id IN RAW, saga_sender IN VARCHAR2, payload IN JSON DEFAULT NULL)
説明: Sagaコーディネータは、forget
プロシージャを起動して参加者にSagaを忘れたとマークするようリクエストします。忘れたとマークされたSagaはファイナライズできず、管理者の介入が必要です。forget
プロシージャは、コーディネータから否定確認を受信するとトリガーされます。コーディネータは、否定確認で応答してリクエストを結合できます。たとえば、Sagaにタイムアウトが設定されており、タイムアウト後に参加者の結合リクエストを受信した場合、否定確認が参加者に送信されます。否定確認のもう1つの例は、Sagaがイニシエータによってすでにファイナライズされた後に行われた参加要求で否定確認が送信された場合です。
is_join
プロシージャ
構文: procedure is_join (saga_id IN saga_id_t, saga_sender IN VARCHAR2);
説明: Saga参加者は、is_join
プロシージャを起動して進行中のSagaとの関連付けを解除します。このプロシージャにより、参加者はこのSagaで先に進むか、REJECT
をイニシエータに送り返すかを選択できます。
reject
プロシージャ
構文: procedure reject (saga_id IN saga_id_t, saga_sender IN VARCHAR2);
説明:イニシエータがreject
プロシージャを起動し、参加者がSagaへの参加の招待を拒否した場合に通知を受け取ります。参加者は、is_join
メソッドでFALSE
を返すことができ、これにより、REJECT
がトリガーされイニシエータに返されます。
after_saga
プロシージャ
構文: procedure after_saga(saga_id in RAW, status in varchar2)
説明: PL/SQLベースのイニシエータの場合、after_saga
プロシージャはコールバック・パッケージに定義されていればコールされます。PL/SQLベースの参加者の場合、コールバック・パッケージで定義され、イベント10855がレベル512に設定されていると、after_saga
メソッドがコールされます。
引数status
には、enum(org.eclipse.microprofile.lra.annotation.LRAStatus)
の文字列化された値があります。
ノート:
payload
とともに、実装されたメソッドは引数としてsaga_Id
およびsaga_sender
(このメッセージの送信者)を提示します。クライアントは、saga_Id
、saga_sender
およびpayload
パラメータを記帳、内部状態の維持あるいはその両方に使用できます。
28.9.2 ロックフリー予約との統合
ロックフリー予約は、予約可能列への変更の自動補正(ロールバック)を可能にします。予約ジャーナル表には、データベースの予約可能列に対する更新ごとにエントリが記録されます。これらのエントリは、予約可能列の変更を補正するために必要な情報を提供します。予約可能列に対するSaga関連の変更に関連付けられた予約ジャーナル・エントリは、Sagaがファイナライズされるまで保持されます。
Sagaフレームワークでは、saga_finalization$
ディクショナリ表を使用して、Sagaの影響を受ける一連の予約可能な表と対応するジャーナルを追跡します。予約ジャーナル・エントリの作成は、SQLの更新時に実行されます。たとえば、一連の更新とそれぞれのsaga_finalization$
エントリは次のとおりです。
Update hotel_rooms set rooms = rooms – 2 where date=to_date(‘20201010’); Update hotel_rooms set rooms = rooms - 1 where date=to_date(‘20201011’); Update dinner_tables set available_tables= available_tables - 3 where date=to_date(‘20201010’);
表28-2 saga_finalization$
表のエントリ
saga_id | 参加者 | Txn_Id | ユーザー# | reservable_table | reservation_journal | ステータス |
---|---|---|---|---|---|---|
ABC123 |
Hotel |
1 |
124 |
hotel_rooms |
|
アクティブ |
ABC123 |
Hotel |
2 |
124 |
dinner_tables |
|
アクティブ |
Sagaフレームワークは、Sagaロールバック中に補正アクションを実行します。予約ジャーナル・エントリは、Sagaトランザクションの補正アクションを実行するために必要なデータを提供します。Sagaフレームワークは、Sagaブランチのsaga_finalization$
表を順番に処理し、予約ジャーナルを使用して補正アクションを実行します。Saga_finalization$
エントリは、Saga補正が成功すると"補正済"とマークされます。Saga_finalization$
エントリは、Sagaコミットが成功すると"コミット済"とマークされます。saga_finalization$
エントリが"補正済"または"コミット済"とマークされた後は、それ以降のアクションは実行できません。
Sagaがファイナライズ(コミットまたは補正の成功)されると、Sagaに関連付けられたすべての予約ジャーナルに対応する予約ジャーナル・エントリは削除されます。指定されたSagaのsaga_finalization$
エントリも削除されます。
関連項目:
28.10 AfterSagaコールバック
AfterSaga (またはAfterLRA)コールバック・メソッドは、LRA (Sagaの場合と同様の長時間実行アクション)の完了後にコールされます。AfterLRAメソッドを使用して、Sagaが完了したことをSagaの参加者に通知します。
JavaベースまたはPL/SQLアプリケーションのSagaの場合、ユーザーはAfterLRAコールバック・メソッドを定義できます。コールバック・メソッドは、Sagaのすべての参加者が最終的な状態になった後、つまりSagaのすべての参加者がSagaを正常にコミットまたはロールバックした後に呼び出されます。Sagaコーディネータは、イニシエータおよびすべての参加者に対してAfterLRAメソッドを呼び出します。これにより、Javaベース・アプリケーションの@afterLRA
注釈付きメソッドおよびPL/SQLベース・アプリケーションのafterLRA()メソッドがトリガーされます。
ノート:
イニシエータのAfterSagaコールバックは、デフォルトで起動されます。参加者のAfterSagaコールバックは、イベント10855がレベル512に設定されている場合にのみ呼び出されます。
@afterLRA
メソッド
Javaベースのイニシエータおよび参加者の場合、@afterLRA
で装飾されたメソッドがトリガーされます。@afterLRA
注釈で装飾されたメソッドには、次のシグネチャが必要です:
@AfterLRA
public void testAfterLRA(SagaMessageContext ctx, LRAStatus status) {
}
SagaMessageContext
はSagaメッセージ・コンテキスト・オブジェクトであり、LRAStatus
はLRAStatus (Eclipse Microprofileで定義)によって定義されます。
after_saga
メソッド
PL/SQLベースのイニシエータおよび参加者の場合は、コールバック・パッケージで定義されている場合、次のメソッドが呼び出されます:
procedure after_saga(saga_id in RAW, status in varchar2)
status
には、enum(org.eclipse.microprofile.lra.annotation.LRAStatus)
の文字列化された値があります。
Sagaコーディネータは、完了したすべてのSagaに対して5分(300秒)の間隔でAfterLRAメソッドを呼び出そうとします。この間隔は、アンダースコア・パラメータ_saga_afterlra_interval
を使用して変更できます。
ディクショナリ・ビュー
ユーザー定義AfterLRAメソッドをコールすると、USER_SAGAS
ディクショナリ・ビューでイニシエータのステータスが次のように変更されます:
-
イニシエータが
commit
またはrollback
を送信した後の、committing
またはrolling back
。 -
参加者の
commit
またはrollback
を処理した後、およびすべての参加者がイニシエータのcommit
またはrollback
を完了した後の、committed
またはrolled back
。
関連項目:
ビュー関連の情報については、データベース・リファレンス・ガイドの「USER_SAGAS」を参照してください