31 セッションレス・トランザクションを使用するアプリケーションの開発
この章では、セッションレス・トランザクションと、アプリケーション内のトランザクション・ワークロードを管理するためのトランザクションの実装方法について学習します。
31.1 トランザクションおよびセッションレス・トランザクションの管理の概要
トランザクション・ワークロードを使用するアプリケーションを開発する場合、データベース・リソースを効率的に使用することが課題となります。通常、トランザクションを管理するには、トランザクションのライフサイクル全体にわたって接続とセッションのリソースをトランザクションに関連付ける必要があるため、セッションまたは接続はトランザクションが終了した後にのみ解放できます。理想としては、アプリケーション・ロジックで思考時間があるアプリケーションでは、データベースからセッションを取得し、作業単位を送信し、セッションを解放し、アプリケーション・ロジックを処理し、別のセッションを取得し、トランザクションを再開し、別の作業単位を送信する、といったことを繰り返し、最終的にトランザクションをコミットするまで続けます。明らかにわかるとおり、後者のアプローチでは、トランザクションがセッションまたは接続に関連付けられず、別のクライアントが使用できるため、アプリケーションはリソースをより効率的に使用できます。セッションレス・トランザクションの機能は、後者のアプローチに基づいています。セッションレス・トランザクションでは、トランザクションを開始した後、そのライフサイクル中にセッションをまたいでトランザクションを柔軟に一時停止および再開できます。さらに、セッションレス・トランザクションを使用すると、データベースはアプリケーション側のロジックを必要とせずに、2フェーズ・コミット(2PC)プロトコルを内部的に調整します。また、セッションレス・トランザクションは、単一インスタンス構成または複数インスタンスのReal Application Clusters (RAC)構成で使用できます。
ノート:
セッションレス・トランザクションは、単一のデータベースにのみ適用されます。
セッションレス・トランザクションの機能がアプリケーションにとってどの程度重要であるかをよりよく理解するためには、セッションレス・トランザクションの機能を使用した場合と使用しない場合のアプリケーションのトランザクション管理方法を確認してください。
セッションレス・トランザクションを使用しないトランザクションの管理
セッションレス・トランザクションを使用しない場合、アプリケーションでトランザクションを管理するには、次の2つのアプローチのいずれかを選択する必要があります:
-
インスタンスのセッションでトランザクションを開始し、トランザクションがファイナライズされるまで、トランザクションを同じセッションおよびインスタンスに関連付け続けます。
下向き: トランザクションがセッションおよび接続に関連付けられている場合、リソースの使用率が非常に低い場合でも、インスタンス内のセッションおよび接続は、トランザクションがアクティブな間は保持されます。接続先が増えるにつれ、データベースの接続が不足する可能性が高くなります。
-
トランザクション・マネージャを介して、eXtendedアーキテクチャ(XA)プロトコルを調整します。トランザクション・マネージャは、異なるデータベース・インスタンスおよびセッション上のブランチ間でXAトランザクションを調整し、XAトランザクションをコミットまたはロールバックするために2PCプロセスを調整する必要があります。
下向き: 接続は保持されず、クライアント間で接続を多重化することはできますが、XAトランザクションを調整するには、トランザクション・マネージャのインフラストラクチャが必要です。トランザクション・マネージャは中間層で作業し、すべてのブランチのステータスを記憶する必要があります。ネットワーク障害によってインダウト・トランザクションが発生する可能性があり、これはリカバリ・プロセスを使用してのみ解決できます。さらに、トランザクション・マネージャのような中間層コンポーネントを追加すると、管理が必要なステートフル・サービスがさらに1つ増え、高い可用性を維持しなければならなくなり、管理上の大きな負担となります。サービスに障害が発生すると、ロックがデータベースに保持され、連鎖的な障害、ハングおよびデータベースの停止につながります。
XAオープン標準に基づくOracle XAトランザクションなど、接続とセッション間でトランザクションを多重化できるソリューションがあります。Oracle XAでは、すべての参加データベースでトランザクション更新がコミットまたは完全にロールバックされることが保証されています。ただし、Oracle XAを使用するには、MicroTX、Tuxedo、WebLogicなどのトランザクション・マネージャを、Oracle Databaseの外部にある中間層コンポーネントとしてデプロイする必要があります。
XAトランザクションを使用する上でのその他のデメリットは次のとおりです:
-
XAトランザクションは、リソース・マネージャが低速になるか、トランザクション・マネージャとリソース・マネージャ間のネットワークが信頼できないと、トランザクションの待機時間が長くなる可能性があります。
-
個々のリソース・マネージャは、Oracle Databaseのように信頼性が高く、可用性も高いものですが、XAトランザクションの信頼性は外部トランザクション・マネージャの信頼性によってのみ決まります。
-
トランザクション・マネージャは、データベース・インスタンスまたはセッション間でブランチを作成および管理する必要があるため、ブランチ上の情報を保持するメモリ領域を増やし、特定のセッションやインスタンスでトランザクションを再開する際に適切なブランチを探すための時間を増やすことが必要になります。したがって、トランザクション・マネージャは、個々のインスタンスに関する知識を持ち、データベース・インスタンス全体にわたって作成されたブランチ間で2PC操作を実行する必要があります。失敗すると、トランザクションがインダウトになり、後続の作業がブロックされる可能性があります。
関連項目:
Oracle XAの詳細は、「Oracle XAを使用したアプリケーションの開発」を参照してください
それどころか、セッションレス・トランザクションを使用する場合、単一データベース(単一インスタンスまたは複数インスタンス)と通信するときにトランザクション・マネージャを使用する必要はありません。データベースが、トランザクションを調整する作業をすべて行います。
セッションレス・トランザクションを使用したトランザクションの管理
セッションレス・トランザクションは、トランザクションとセッション間の結合を解除します。トランザクションを開始した後は、セッションまたは接続に関連付ける必要はありません。セッションまたは接続を解放して、別のクライアントで使用できるようにします。また、2PCプロセスおよびエラー・リカバリ・プロセスはデータベースによって自動的に処理されるため、XAプロトコルを調整するためのトランザクション・マネージャは必要ありません。したがって、インダウト・トランザクションが発生するリスクはなく、リカバリ・メカニズムも必要ありません。
要約すると、セッションレス・トランザクションの主な差別化要因は次のとおりです:
-
トランザクションがセッションまたは接続に固定されないため、セッションおよび接続を効率的に使用できます。
-
トランザクションの最終処理が内部的に行われ、必要なクライアントとサーバー間のラウンド・トリップが少なくなるため、コミットの待機時間が短縮されます。
-
リカバリ・メカニズムが内部で処理されるため、ロックがリソースを無期限に保持する可能性がなくなり、トランザクション・スループットが向上します。
-
トランザクションがインダウトになることはありません。
31.2 セッションレス・トランザクションの概要
セッションレス・トランザクションは、データベース・アプリケーションでトランザクションを効率的に管理するための機能です。
セッションレス・トランザクション機能を使用すると、ユーザーは、一意のトランザクション識別子を指定してデータベース・セッションでトランザクションを開始し、作業単位を送信し、トランザクションを一時停止し、同じトランザクション識別子を使用して別のセッションで同じトランザクションを続行できます。最終的には、さらに別のセッションから同じトランザクションをコミットできます。
セッションレス・トランザクション機能は、外部トランザクション・マネージャを使用してXAプロトコルを調整することなく、トランザクションをコミットまたはロールバックする固有のメカニズムをアプリケーションに提供します。トランザクションのコミットは、データベースによって内部的に調整され、複数のデータベース・インスタンスまたはセッション間でデータの整合性が確保されます。
RACまたはRAC以外のデプロイメントでセッションレス・トランザクションを使用できます。RACデプロイメントの場合、各作業単位で異なるデータベース・インスタンス上のセッションを使用できます。RAC以外のデプロイメントでは、単一のインスタンスがあり、各作業単位で同じデータベース・インスタンス上の異なるセッションを使用できます。
RACデプロイメントでのワークフローの例を次に示します。
- インスタンス1でセッションを取得します。
- トランザクションの一意の識別子を設定します。
- 新しいセッションレス・トランザクションを開始します。
- 作業単位(DMLまたはSQL文)を送信します。
- セッションレス・トランザクションを一時停止します。
- セッションを解放します。
- インスタンス2でセッションを取得します。
- トランザクションに同じ一意の識別子を設定します。
- セッションレス・トランザクションを再開します。
- 作業単位(DMLまたはSQL文)を送信します。
- セッションレス・トランザクションを一時停止します。
- セッションを解放します。
- インスタンス3でセッションを取得します。
- トランザクションに同じ一意の識別子を設定します。
- セッションレス・トランザクションを再開します。
COMMIT
します。- セッションを解放します。
関連項目:
Oracle RACの詳細は、『Oracle Real Application Clusters管理およびデプロイメント・ガイド』を参照
セッションレス・トランザクションを開始、一時停止および再開するには、サーバー側(PL/SQL)またはクライアント側APIを使用できます。APIを使用すると、次のことができます:
-
トランザクションに対してユーザー指定または自動生成された一意のトランザクション識別子を設定します。
-
指定されたトランザクション識別子で新しいトランザクションを開始します。
-
トランザクション識別子で識別されたトランザクションを一時停止します。
-
トランザクション識別子で識別されたトランザクションを再開します。
これらのAPIの詳細は、このドキュメントの後続の項で説明します。
31.3 セッションレス・トランザクション機能
セッションレス・トランザクションでは、次の機能が提供されます。
セッション間および接続間のトランザクションの多重化
セッションレス・トランザクションは、次の図31-1および図31-2に示すように、トランザクションを開始、一時停止および再開、そして最後にトランザクションをコミットまたはロールバックする固有のメカニズムを提供します。セッションレス・トランザクションでは、XAプロトコルおよび外部トランザクション・マネージャが不要になる柔軟性が得られます。そのため、待機時間の短縮とOracle Databaseの高可用性という主な利点を得ることができます。
図31-1 RACデプロイメントでのセッション間のトランザクションの多重化

図31-2 単一インスタンス・デプロイメントでのセッション間のトランザクションの多重化

単一データベース使用時の外部トランザクション・マネージャの排除
エンタープライズ・アプリケーションは、Oracle Databaseなどのトランザクション・リソース・マネージャでの一連の操作の完了を保証するために、トランザクション・マネージャを利用します。セッションレス・トランザクションでは、内部的に2PCを使用してセッションレス・トランザクションをコミットまたはロールバックするため、同じデータベースとの通信時に外部トランザクション・マネージャが不要になります。ただし、異なるデータベース間でトランザクションを調整する場合は、トランザクション・マネージャが必要です。
パフォーマンスの向上
セッションレス・トランザクション機能は、Oracle Database内にコミット・プロトコルを実装し、アプリケーションまたはクライアントがトランザクションをファイナライズするための単純なコミット・コマンドを発行できるようにします。実際、適切なトランザクション識別子を持つ任意の接続またはセッションからコミット・コマンドを発行することができ、この機能の接続およびセッションの多重化機能を際立たせています。セッションレス・トランザクションのコミット待機時間の短縮は、アプリケーションまたはトランザクション・マネージャがコミット・プロトコルを駆動しなければならず、データベース・サーバーへの複数のラウンド・トリップが必要となるため、コミット待機時間が長くなるXAプロトコルとは対照的です。次の図31-3に示すように、Oracleはセッションレス・トランザクションに対して2PCを内部的に調整します。クライアントは、Oracleサーバー・インスタンス間の通信と比較して待機時間の長いラウンドトリップ・コールを発行する必要はありません。
図31-3 XAトランザクションおよびセッションレス・トランザクション2PC

コミット待機時間の短縮に加えて、セッションレス・トランザクションを開始、再開または一時停止するためのコールを次のネットワーク・ラウンドトリップにアタッチできます。そのため、ユーザーが想定するセッションレス・トランザクションの開始、再開および一時停止の待機時間は、XAトランザクションのそれより短くなります。図31-4は、XAトランザクションとセッションレス・トランザクションの開始時と連結解除/一時停止時の待機時間の違いを示しています。セッションレス・トランザクションでは、OCITransStart
およびOCITransDetach
コールは、データベース・サーバーへのラウンド・トリップを行わずにただちに返されます。これらのリクエストは、データベース・サーバーへの後続のサーバー・ラウンドトリップとともにサーバーに送信されます。
図31-4 XAトランザクションおよびセッションレス・トランザクション

フォルト・トレラントでの自動リカバリ可能なアプリケーションの構築
セッションレス・トランザクションでは、コミット・プロトコルはデータベースによって調整されます。したがって、トランザクション・リカバリの負担がアプリケーション(またはトランザクション・マネージャ)から取り除かれます。トランザクションがインダウト状態または一時停止状態になることはありません。したがって、アプリケーションはリカバリ・メカニズムをデプロイする必要はありません。
関連項目:
インダウト・トランザクションの管理の詳細は、『データベース管理者ガイド』の「分散トランザクションの管理」にを参照してください
31.3.1 ユースケース例
動的データベース・サービスを使用するアプリケーション
RACクラスタで複数のサービスを作成し、サービス・レベルのしきい値や優先度などの属性を指定できます。セッションは特定のサービスに接続されます。セッションレス・トランザクション機能を使用すると、アプリケーションは異なるサービスを使用して行われた変更を単一のセッションレス・トランザクションにラップできます。次の図31-5では、クライアント・アプリケーションのスレッドが、様々なサービスに接続された複数のセッションを使用して、トランザクションの変更を行っています。
関連項目:
動的データベース・サービスの詳細は、『RAC管理およびデプロイメント・ガイド』の動的データベース・サービスによる自動ワークロード管理の概要に関する項を参照してください
図31-5セッションレス・トランザクションを使用する動的データベース・サービス

マイクロサービス
マイクロサービスは、アプリケーション・デプロイメントのステートレスなパラダイムに従います。複数のマイクロサービスをトランザクションの下で論理的に結合する必要がある場合、次の2つの選択肢があります:
-
MicroTxなどの外部TMをデプロイして、個々のマイクロサービスをグローバル・トランザクションのブランチとして管理します。
-
各マイクロサービス・リクエストを自動コミットします。エラーが発生した場合、アプリケーションは失敗したタスクを元に戻すために補正マイクロサービスを呼び出す必要がありますが、そのリカバリ・ロジックをアプリケーション側で処理する必要があるため、アプリケーションでの実装は複雑です。
図31-6と図31-7はそれぞれ、XAトランザクションとセッションレス・トランザクションを使用して、複数のマイクロサービスからの作業を単一のトランザクションに結合する方法を示しています。セッションレス・トランザクションは、よりシンプルなソリューションを提供し、中間層としてのトランザクション・マネージャを削除します。
図31-6 XAトランザクションを使用するマイクロサービス

図31-7 単一のセッションレス・トランザクションを使用するマイクロサービス

対話型アプリケーション
思考時間と計算ロジックを備えたアプリケーションとして、対話型アプリケーションは、セッションレス・トランザクションの利点を享受できます。たとえば、旅行予約サービスを使用すると、顧客は利用可能なホテルの空室状況や航空券を閲覧できる場合があります。閲覧中、顧客は興味のあるホテルや航空券の予約を一定期間保持することができます。旅程がファイナライズされると、顧客は口座残高から予約料金の支払を求められます。セッションレス・トランザクションでは、実際の操作中のみデータベース・セッションが使用され、顧客が閲覧や入力を行っている間はデータベース・セッションを解放できます。そのため、同じデータベース・セッションのセットで、より多くの同時リクエストを処理できます。図31-8および図31-9は、単一インスタンス・デプロイメントおよびRACデプロイメントにおけるこのようなユースケースをそれぞれ示しています。
図31-8単一インスタンス・デプロイメントでのセッションレス・トランザクションを使用する対話型アプリケーション

対話型アプリケーションをRACデプロイメントで実行すると、RACによって提供される可用性とロード・バランシングを活用できます。
図31-9 RACデプロイメントでのセッションレス・トランザクションを使用する対話型アプリケーション

31.4 セッションレス・トランザクションの利点
アプリケーションでセッションレス・トランザクションを使用する利点は次のとおりです。
セッションおよび接続リソースの効率的な使用
セッションレス・トランザクションを使用すると、アプリケーションはeXtendedアーキテクチャ(XA)の複雑さなしにセッションとトランザクションを多重化できます。
関連項目:
Oracle XAの詳細は、「Oracle XAを使用したアプリケーションの開発」を参照してください
データベース管理のリカバリ
Oracle Databaseは、セッションレス・トランザクションのトランザクション・リカバリおよびコミット操作を担当します。クライアント・アプリケーションには、トランザクション管理や外部トランザクション・マネージャは必要ありません。また、トランザクションがインダウトになることはありません。
アプリケーション・クライアントと(Oracle)データベース・サーバー間のネットワーク・ラウンドトリップの削減
セッションレス・トランザクションを使用すると、アプリケーションがコミット・コマンドを送信するだけで、サーバーがトランザクションのコミット・プロトコルを内部的に管理します。クライアント・アプリケーションとデータベース・インスタンス間のラウンド・トリップの回数が大幅に削減されます。
トランザクションはRACデータベース・インスタンス間で再開できます
XAプロトコルとは異なり、セッションレス・トランザクションは、1つのRACデータベース・インスタンスで開始および一時停止し、別のRACデータベース・インスタンスで再開できます。トランザクションで使用されるRACデータベース・インスタンスをアプリケーションが認識する必要はありません。
Oracle XAでは、異なるRACデータベース・インスタンスでXAトランザクションを一時停止および再開するには、外部トランザクション・マネージャが各RACデータベース・インスタンスに関する情報を保持し、インスタンスごとに少なくとも1つのブランチ(XAトランザクションで使用される)を作成し、これらのブランチの2PCを管理する必要があります。XAドライバは、1つのインスタンスでXAトランザクションを一時停止して別のインスタンスで再開することはできません。ユーザーは、特定のXAトランザクションで作業を続行するために、新しいインスタンスにブランチを作成する必要があります。
2PCプロトコルの待機時間の短縮
セッションレス・トランザクションは内部的にコミット操作を調整するため、トランザクションのファイナライズに必要なクライアント/サーバー・ラウンドトリップが少なくなり、アプリケーションはコミット(2PCプロトコル)の待機時間を短縮できます。
XAトランザクションが複数のブランチに分散している場合、外部トランザクション・マネージャは、これらすべてのブランチに2PCメッセージを送信する必要があります。
外部トランザクション・マネージャは不要です
セッションレス・トランザクションでは、1つのデータベースと通信する際に外部トランザクション・マネージャを必要としません。
31.5 セッションレス・トランザクションの使用
次の項では、アプリケーションでセッションレス・トランザクションを使用する方法について説明します。
31.5.1 アクティブなセッションレス・トランザクションの理解
現在セッションに関連付けられているセッションレス・トランザクションは、アクティブなセッションレス・トランザクションであるとみなされます。セッションレス・トランザクションは、トランザクションが新しく開始または再開されてから、トランザクションが一時停止、コミットまたはロールバックされた時点までアクティブになります。
31.5.2 セッションレス・トランザクションのライフサイクルの理解
セッションレス・トランザクションは、サーバー側(DBMS_TRANSACTION
パッケージのPL/SQLファンクションおよびプロシージャを使用)またはクライアント側(Oracle Call Interface (OCI)やOracle JDBCなどのクライアントAPIを使用)で開始できます。サーバー上でセッションレス・トランザクションを開始、再開および一時停止するためのAPIセットは、セッションレス・トランザクションがアクティブである期間中、クライアント上のAPIと相互運用できません。
GTRID(トランザクションIDとも呼ばれるグローバル・トランザクションID)と呼ばれる一意のトランザクション識別子は、各セッションレス・トランザクションを識別します。セッションレス・トランザクションを開始する際には、トランザクションを識別する必要なGTRIDを指定できます。GTRIDを指定しない場合、データベース・サーバー(データベース・サーバーでセッションレス・トランザクションを開始する場合)またはクライアント・ドライバ(クライアントでセッションレス・トランザクションを開始する場合)によって、トランザクションのGTRIDとして使用される識別子が生成されます。Oracle Databaseサーバーに送信される後続の作業単位は、トランザクション・コンテキスト内にあります。時間の経過とともに、トランザクションが作業単位間で複数回、一時停止および再開される可能性があります。トランザクションは、コミットまたはロールバックされた時点で終了します。
行ロックの待機期間が無限であるローカル・トランザクションとは異なり、セッションレス・トランザクションでは、行ロックの待機期間はDISTRIBUTED_LOCK_TIMEOUT
データベース・パラメータによって制限されます。タイムアウト後、行ロックで待機している文はエラーを受け取り、ロールバックされます。
31.5.3 サーバー・ラウンドトリップおよび事前コールと事後コール関数の理解
サーバー・ラウンドトリップ
サーバー・ラウンドトリップとは、クライアントからサーバーへ、そして再びクライアントへと戻る一連の移動のことです。サーバー・ラウンドトリップを発生させるユーザー・コールがメイン・コールです。各サーバー・ラウンドトリップにはメイン・コールがあり、メイン・コールに事前コール関数と事後コール関数が接続されている場合があります。
事前コール関数と事後コール関数
特定のユーザー・コールでは、クライアント・ライブラリはすぐにサーバー・ラウンドトリップを発生させません。かわりに、クライアント・ライブラリはコールの情報を格納し、次のサーバー・ラウンドトリップでコールをアタッチします。アタッチされたコールがサーバー・ラウンドトリップのメインのコール前に実行された場合、そのようなコールは事前コール関数をコールしており、アタッチされたコールがサーバー・ラウンドトリップのメイン・コール後に実行された場合、そのようなコールは事後コール関数をコールしています。
事前コール関数
事前コール関数の例を次に示します:
int main()
{
...
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW); /* a pre-call that starts a Sessionless transaction */
OCIStmtExecute(...) /* a call that incurs a round-trip to server. This is the "server round trip." */
...
}
前述の例では、セッションレス・トランザクションを開始するための最初のOCIコール(OCITransStart
)は、事前コール関数です。この事前コールは、クライアント・ライブラリがコール情報を記録した後、サーバー・ラウンドトリップを発生させることなく返されます。OCIStmtExecute
コールは、サーバー・ラウンドトリップ(つまり、ラウンド・トリップのメイン・コール)を発生させ、OCITransStart
事前コール関数自体がアタッチされます。このサーバー・ラウンドトリップを処理するために、データベース・サーバーはまず事前コール関数(OCITransStart
)を実行し、次にメイン・コール(OCIStmtExecute
)を実行します。
事前コール関数でエラーが発生した場合、メイン・コールおよび事後コール関数は呼び出されません。
事後コール関数
事後コール関数は、次のサーバー・ラウンドトリップに接続され、メイン・コールが正常に実行された後に実行されます。
事後コール関数の例を次に示します:
int main()
{
...
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_TRANS_POST_CALL)); /* a post-call which suspends a Sessionless transaction */
OCIStmtExecute(...) /* a call that incurs a round-trip to server. This is the "main call". */
...
}
前述の例では、セッションレス・トランザクションを一時停止するための最初のOCIコール(OCITransDetach
)は事後コール関数です。これは、クライアント・ライブラリがこのコールを記録した直後に返されますが、コールはまだサーバーに送信されません。OCIStmtExecute
は、サーバー・ラウンドトリップ(つまり、ラウンド・トリップのメイン・コール)を発生させ、事後コール関数OCITransDetach
自体がアタッチされます。このサーバー・ラウンドトリップを処理するために、データベース・サーバーはまずメイン・コール(OCIStmtExecute
)を実行し、次に事後コール関数(OCITransDetach
とOCI_TRANS_POST_CALL
)を実行します。
メイン・コールでエラーが発生した場合、事後コール関数は呼び出されません。
31.5.4 セッションレス・トランザクションを使用するための前提条件
サーバーでセッションレス・トランザクションを使用するための前提条件
サーバーでセッションレス・トランザクションを使用するための前提条件はありません。
OCIクライアントでセッションレス・トランザクションを使用するための前提条件
OCIクライアントでセッションレス・トランザクションAPIの開始、一時停止および再開を使用するには、トランザクション・ハンドルを割り当て、サービス・コンテキスト・ハンドルのOCI_ATTR_TRANS
属性として設定する必要があります。
OCISvcCtx *svchp = NULL;
/* get a service context handle from the session pool. */
OCISessionGet (envhp, errhp, &svchp, authInfop, poolName, poolNameLen, NULL, 0,
NULL, NULL, NULL, OCI_SESSGET_SPOOL | OCI_SESSGET_PURITY_NEW);
/* create a transaction handle */
OCITrans *txnhp = (OCITrans *)0; /* Sessionless transaction */
OCIHandleAlloc(envhp, (void **)&txnhp, OCI_HTYPE_TRANS, 0, 0);
/* set txn handle as service context handle's OCI_ATTR_TRANS attribute */
OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp);
31.5.5 グローバル・トランザクションIDの設定
すべてのセッションレス・トランザクションは、グローバル・トランザクションID(GTRID)と呼ばれる一意のトランザクション識別子によって識別されます。必要なGTRIDを指定するか、OracleでGTRIDを生成して、開始するセッションレス・トランザクションを識別できます。いずれの場合も、GTRIDフィールドを適切に設定して、クライアント・ドライバまたはデータベース・サーバーが適切なアクションを実行できるようにする必要があります。
セッションレスAPIはクライアント側とサーバー側の両方で提供されるため、データベース・サーバーおよびクライアント・バージョンを個別にアップグレードする柔軟性を最大限に高めることができます。
ノート:
自動生成されたGTRIDの場合、クライアント・ライブラリはクライアントで開始されるセッションレス・トランザクションのGTRIDを生成し、データベース・サーバーはデータベース・サーバーで開始されるセッションレス・トランザクションのGTRIDを生成します。
データベース・サーバーでのGTRIDの設定
GTRIDは、サーバー上でセッションレス・トランザクションを開始する際の一部としてサーバーに設定されます。
関連項目:
OCIクライアントでのGTRIDの設定
ノート:
これは、トランザクションを開始する前に行う必要があります。特定のGTRIDを持つ新しいセッションレス・トランザクションを識別する場合は、XID構造を作成し、XIDのdata
メンバーにGTRIDを設定し、XIDのgtrid_length
メンバーをGTRIDのサイズ(バイト単位)に設定します。次に、コード・サンプルに示すように、OCIAttrSet
をコールして、トランザクション・ハンドルのOCI_ATTR_XID
属性をXID構造のポインタに設定します。クライアント・ライブラリはGTRIDを記録し、それをトランザクション・ハンドルに格納します。
例31-1 特定のGTRIDの設定
/* Get current transaction handle */
OCITrans *current_txnhp;
OCIAttrGet(svchp, OCI_HTYPE_SVCCTX, ¤t_txnhp, NULL,
errhp);
/* set GTRID in XID struct */
XID res_xid;
strcpy(res_xid.data, "client_user_gtrid_1");
res_xid.gtrid_length = strlen("client_user_gtrid_1");
/* set XID as txn handle's attribute */
OCIAttrSet(current_txnhp, OCI_HTYPE_TRANS, &res_xid, sizeof(XID), OCI_ATTR_XID, errhp);
新しいセッションレス・トランザクションを識別するGTRIDとして生成されたUUIDを使用する場合は、次のコード・サンプルに示すように、トランザクション・ハンドルのOCI_ATTR_XID
属性をNULL
ポインタに設定します。トランザクションを開始すると、クライアント・ドライバによってUUIDが生成され、GTRIDとして使用されます。
生成されたUUIDをGTRIDとして使用する場合、トランザクションが一時停止される前に、トランザクション・ハンドルからXIDを取得して、トランザクションの再開またはコミットに使用できるようにする必要があります。
関連項目:
例31-2 生成されたGTRIDの設定
/* Get current transaction handle */
OCITrans *current_txnhp;
OCIAttrGet(svchp, OCI_HTYPE_SVCCTX, ¤t_txnhp, NULL,
errhp);
/* set XID as txn handle's attribute */
OCIAttrSet(current_txnhp, OCI_HTYPE_TRANS, NULL, sizeof(XID), OCI_ATTR_XID, errhp);
次の場合は、XID (OCI_ATTR_XID
)を設定できません:
-
データベース・サーバーで開始または再開されたアクティブなセッションレス・トランザクションがあります。この場合、サービス・コンテキスト・ハンドルの
OCI_ATTR_TRANS
属性は、OCIライブラリによって管理される読取り専用のトランザクション・ハンドルです(エラー26210を返します)。 -
アクティブなセッションレス・トランザクションが、クライアント・ライブラリで生成されたGTRIDを使用して開始されましたが、そのようなGTRIDはまだ取得されていません(エラー26204を返します)。
-
新しいセッションレス・トランザクションを開始する、または既存のセッションレス・トランザクションを再開するための事前コール関数は記録されていますが、データベース・サーバーにはまだ送信されていません(エラー26216を返します)。
関連項目:
OCIAttrSet()およびOCIAttrGet()はOracle Call Interface開発者ガイドを参照してください
31.5.6 新しいセッションレス・トランザクションの開始
セッションレス・トランザクションは、サーバーまたはクライアントで開始できます。新しいトランザクションの開始をリクエストされた時点でアクティブなセッションレス・トランザクションが存在する場合、新しいトランザクション・リクエストが成功するかどうかに関係なく、このようなアクティブなトランザクションは一時停止されます。
新しいセッションレス・トランザクションを開始するときに、一意のGTRIDを指定(または生成されたGTRIDを使用)し、トランザクションが一時停止されている場合に使用するタイムアウト値を指定できます。
関連項目:
一時停止されたセッションレス・トランザクションのタイムアウトの詳細は、「一時停止されたセッションレス・トランザクションのタイムアウト」を参照してください
データベース・サーバーでの新しいセッションレス・トランザクションの開始
サーバーで新しいセッションレス・トランザクションを開始するには、DBMS_TRANSACTION.START_TRANSACTION
PL/SQLファンクションを実行します。フラグをTRANSACTION_NEW
に設定して、リクエストが新しいトランザクションを開始する必要があることを指定します。
ユーザー指定のGTRIDまたは生成されたGTRIDを使用して、サーバー上でセッションレス・トランザクションを開始できます。
生成されたUUIDをGTRIDとして使用してデータベース・サーバーでセッションレス・トランザクションを開始するには、DBMS_TRANSACTION.START_TRANSACTION
PL/SQLファンクションのRAWタイプ・パラメータXID
をNULL
に設定します。このファンクションは、GUIDジェネレータを呼び出し、GTRIDとして使用される16バイトのランダム識別子を作成します。または、既存のSYS_GUID()
APIを使用して、グローバルに一意の識別子を取得し、それをセッションレス・トランザクションのGTRIDとして渡すことができます。
関連項目:
-
START_TRANSACTION
ファンクションの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』のDBMS_TRANSACTION
パッケージを参照してください -
『SQL言語リファレンス・ガイド』のSYS_GUID
ノート:
セッションレス・トランザクションをサポートしない古いクライアント・ライブラリを使用するプログラムでも、セッションレス・トランザクションをサポートするサーバーでセッションレス・トランザクションが開始されると、セッションレス・トランザクションを使用できます。
例31-3 ユーザー指定のGTRIDを使用したサーバーでの新しいセッションレス・トランザクションの開始
次の例では、特定のGTRIDを使用して、サーバー上で新しいセッションレス・トランザクションを開始します:
SET SERVEROUTPUT ON
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('user_specified_gtrid')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(gtrid));
END;
/
出力は次のとおりです。
user_specified_gtrid
PL/SQL procedure successfully completed.
次では、新しく作成されたセッションレス・トランザクションのトランザクションIDを取得します。
BEGIN
IF DBMS_TRANSACTION.GET_TRANSACTION_TYPE() = DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS THEN
DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(DBMS_TRANSACTION.GET_TRANSACTION_ID()));
ELSE
DBMS_OUTPUT.PUT_LINE('Not a Sessionless transaction');
END IF;
END;
/
出力は次のとおりです。
user_specified_gtrid
PL/SQL procedure successfully completed.
例31-4生成されたGTRIDを使用したサーバーでの新しいセッションレス・トランザクションの開始
次の例では、生成されたGTRIDを使用して、サーバー上で新しいセッションレス・トランザクションを開始します。
-- Start a new Sessionless transaction with a GTRID generated by the
-- START_TRANSACTION function.
SET SERVEROUTPUT ON
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( NULL
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
DBMS_OUTPUT.PUT_LINE(gtrid);
END;
/
出力は次のとおりです。
9AF035CCB2024FA6BF17C8CDBF7B6D6C
PL/SQL procedure successfully completed.
次では、新しく作成されたセッションレス・トランザクションのトランザクションIDを取得します。
BEGIN
IF DBMS_TRANSACTION.GET_TRANSACTION_TYPE() = DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS THEN
DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(DBMS_TRANSACTION.GET_TRANSACTION_ID()));
ELSE
DBMS_OUTPUT.PUT_LINE('Not a Sessionless transaction');
END IF;
END;
/
出力は次のとおりです。
9AF035CCB2024FA6BF17C8CDBF7B6D6C
PL/SQL procedure successfully completed.
セッションレス・トランザクションがサーバー側で開始された際のクライアント・ライブラリの動作
セッションレス・トランザクションをサポートするクライアント・ライブラリの場合、サーバー上でセッションレス・トランザクションが開始されると、サーバーはセッションレス・トランザクションが開始、再開、一時停止または終了したタイミングについてクライアント・ライブラリに通知します。OCIの場合、情報が受信されると、サービス・コンテキスト・ハンドルのOCI_ATTR_TRANS
属性が、OCIクライアント・ライブラリによって管理されるトランザクション・ハンドルへのポインタに変更されます。この管理されたトランザクション・ハンドルは読取り専用であり、対応するセッションレス・トランザクションがアクティブな場合にのみ使用できます。ユーザーは、このハンドルからGTRIDを取得できます(「データベースOCIクライアントでのGTRIDの取得」の項を参照)。セッションレス・トランザクションがアクティブでなくなると、OCI_ATTR_TRANS
属性は、セッションレス・トランザクションがアクティブになる前に設定された値に戻されます。
OCI クライアントでの新しいセッションレス・トランザクションの開始
クライアント側で新しいセッションレス・トランザクションを開始するには、(OCI_TRANS_SESSIONLESS
| OCI_TRANS_NEW
)フラグおよびタイムアウト設定を指定してOCITransStart
関数を実行します。OCI_TRANS_SESSIONLESS
フラグは、このリクエストとXAブランチを開始するリクエストを区別します。OCITransStart
をOCI_TRANS_SESSIONLESS
フラグとともに使用すると、常に事前コール関数になります。
新しいセッションレス・トランザクションを開始するときに、一意のGTRIDを指定(または生成されたGTRIDを使用)し、トランザクションが一時停止されている場合に使用するタイムアウト値を指定できます。
関連項目:
例31-5 OCI クライアントでの新しいセッションレス・トランザクションの開始
OCISvcCtx *svchp;
OCIError *errhp;
...
ub4 timeout = 60;
/* start the transaction. This will be attached on the subsequent server round trip.
*/
OCITransStart(svchp, errhp, timeout, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW);
タイムアウト: セッションレス・トランザクションが中断されてから、サーバーがトランザクションをロールバックするまでの時間(秒)。これにより、トランザクションがデータベース・リソース(行ロックなど)を無期限に保持することが回避されます。
ノート:
タイムアウト値は正数にする必要があります。
関連項目:
-
Oracle Call Interface開発者ガイドのOCITransStart()を参照してください
31.5.6.1 セッションレス・トランザクション内でのSQL文の実行
新しいセッションレス・トランザクションを開始するか、既存のトランザクションを再開すると、発行されるすべてのSQL文はセッションレス・トランザクションのコンテキストになります。
ノート:
セッションレス・トランザクションがアクティブなときにSQL文によってロックの競合が発生した場合(行ロックが別のトランザクションによって保持されている行の更新など)、トランザクションはロックを最大distributed_lock_timeout
秒間待機してから、ORA-2049エラーを返します。エラーが返された場合、文はロールバックされますが、トランザクション内の他の作業は変更されません。
31.5.7 グローバル・トランザクションIDの取得
現在アクティブな状態にあるセッションレス・トランザクションのGTRIDを取得できます。
データベース・サーバーでのGTRIDの取得
サーバー上のアクティブなセッションレス・トランザクションのGTRIDを取得するには、DBMS_TRANSACTION.GET_TRANSACTION_ID
PL/SQLファンクションを使用します。
例31-6 データベース・サーバーでのGTRIDの取得
次のコード・サンプルは、セッションレス・トランザクションを開始し、GET_TRANSACTION_ID()
ファンクションを使用してセッションレス・トランザクションのGTRIDを取得します。
SET SERVEROUTPUT ON
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('user_specified_gtrid')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(gtrid));
END;
/
出力は次のとおりです。
user_specified_gtrid
PL/SQL procedure successfully completed.
GTRIDは次により取得されます。
BEGIN
IF DBMS_TRANSACTION.GET_TRANSACTION_TYPE() = DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS THEN
DBMS_OUTPUT.PUT_LINE(UTL_RAW.CAST_TO_VARCHAR2(DBMS_TRANSACTION.GET_TRANSACTION_ID()));
ELSE
DBMS_OUTPUT.PUT_LINE('Not a Sessionless transaction');
END IF;
END;
/
出力は次のとおりです。
user_specified_gtrid
PL/SQL procedure successfully completed.
関連項目:
GET_TRANSACTION_ID
ファンクションの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』のDBMS_TRANSACTION
パッケージを参照してください
OCIクライアントでのGTRIDの取得
OCIクライアントでGTRIDを取得するには、OCIAttrGet
関数をコールして、トランザクション・ハンドルのOCI_ATTR_XID
属性(XID構造体へのポインタ)を取得します。次に、XID構造のgtrid_length
メンバーを読み、GTRIDのバイト単位のサイズを確認し、data
メンバーを読み、GTRIDの実際のコンテンツを取得します。
ノート:
-
GTRIDがクライアント・ライブラリによって生成された場合、ユーザーはトランザクションを一時停止する前にトランザクションのGTRIDを取得する必要があります。ユーザーがトランザクションを再開してコミットするには、GTRIDが必要です。
-
OCIAttrGet
から取得したポインタは読取り専用です。ユーザーは、「OCIクライアントでのGTRIDの設定」で指定された方法を使用してGTRIDを設定する必要があります。
例31-7 OCIクライアントでのGTRIDの取得
OCITrans *current_txnhp;
XID *current_xidp;
char gtrid_buffer[65];
/* get transaction handle from service context handle */
OCIAttrGet(svchp, OCI_HTYPE_SVCCTX, ¤t_txnhp, NULL, OCI_ATTR_TRANS, errhp);
/* get XID from transaction handle */
OCIAttrGet(current_txnhp, OCI_HTYPE_TRANS, ¤t_xidp, NULL, OCI_ATTR_XID, errhp));
/* get GTRID from XID */
memcpy(gtrid_buffer, current_xidp->data, current_xidp->gtrid_length);
関連項目:
Oracle Call Interface開発者ガイドのOCIAttrGet()を参照してください
31.5.8セッションレス・トランザクションの一時停止
セッションレス・トランザクションが最後に開始または再開されたのと同じ方法(データベース・サーバーまたはクライアント上)を使用して、アクティブなセッションレス・トランザクションを一時停止できます。一時停止関数またはプロシージャは、セッションレス・トランザクションにのみ適用できます。セッション内にトランザクションがない場合、一時停止は無効です。セッション内に非セッションレスのトランザクション(ローカル・トランザクションやXAトランザクションなど)が存在する場合、一時停止関数をコールするとORA-26202エラーが発生しますが、そのトランザクションはそのままセッションに残ります。
データベース・サーバーでのアクティブなセッションレス・トランザクションの一時停止
DBMS_TRANSACTION.SUSPEND_TRANSACTION
PL/SQLプロシージャを実行して、特定のセッションでアクティブなセッションレス・トランザクションを一時停止します。このプロシージャにはパラメータはありません。
関連項目:
SUSPEND_TRANSACTION
プロシージャの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』のDBMS_TRANSACTIONパッケージを参照してください
例31-8 サーバーでのアクティブなセッションレス・トランザクションの一時停止
次に、サーバー上のアクティブなセッションレス・トランザクションを一時停止する例を示します:
SET SERVEROUTPUT ON
DECLARE
gtrid VARCHAR2(128);
BEGIN
-- start Sessionless transaction
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('user_specified_gtrid')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
-- confirm the transaction is started
IF DBMS_TRANSACTION.GET_TRANSACTION_TYPE() = DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS THEN
DBMS_OUTPUT.PUT_LINE('After start: ' || UTL_RAW.CAST_TO_VARCHAR2(DBMS_TRANSACTION.GET_TRANSACTION_ID()));
ELSE
DBMS_OUTPUT.PUT_LINE('Not a Sessionless transaction');
END IF;
-- suspend the transaction
DBMS_TRANSACTION.SUSPEND_TRANSACTION;
-- confirm the transaction is suspended
DBMS_OUTPUT.PUT_LINE('After suspend: ' || DBMS_TRANSACTION.GET_TRANSACTION_ID());
END;
/
出力は次のとおりです。
After start: user_specified_gtrid
After suspend:
PL/SQL procedure successfully completed.
OCIクライアントでのアクティブなセッションレス・トランザクションの一時停止
OCIクライアントでアクティブなセッションレス・トランザクションを一時停止するには、トランザクションを一時停止する方法に応じて、適切なフラグを指定してOCITransDetach
をコールします。必要なオプションを示すフラグビットを指定できます。フラグが(OCI_TRANS_SESSIONLESS | OCI_DEFAULT
)に設定されている場合、一時停止はただちに実行されます。フラグが(OCI_TRANS_SESSIONLESS | OCI_TRANS_PRE_CALL
)に設定されている場合、事前コール関数を一時停止します(次のサーバー・ラウンドトリップにアタッチされ、メイン・コールの前に実行されます)。フラグが(OCI_TRANS_SESSIONLESS | OCI_TRANS_POST_CALL
)に設定されている場合、事後コール関数を一時停止します(次のサーバー・ラウンドトリップにアタッチされ、メイン・コールの前に実行されます)。
ノート:
事前コールの一時停止(OCI_TRANS_PRE_CALL
が指定されている)でエラーが発生した場合、メイン・コールは実行されません。メイン・コールでエラーが発生した場合、事後コールの一時停止(OCI_TRANS_POST_CALL
が指定されている)は実行されません。
例31-9 即時一時停止
アクティブなセッションレス・トランザクションをただちに中断するには、次の例のように、フラグ(OCI_TRANS_SESSIONLESS | OCI_DEFAULT
)を指定してOCITransDetach
関数をコールします。
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_DEFAULT);
例31-10 事前コールの一時停止(次のサーバー・ラウンドトリップの前に一時停止)
事前コールの一時停止(メイン・コールの前に次のサーバー・ラウンドトリップで一時停止)を発行するには、OCI_TRANS_SESSIONLESS
| OCI_TRANS_PRE_CALL
フラグを指定してOCITransDetach
関数をコールします。事前コール関数であるため、中断コードはメイン・コールの前に実行されます。
次の例では、実際にはOCIPing()
コールの前にトランザクションが一時停止されます。
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_TRANS_PRE_CALL);
OCIPing(svchp, errhp, OCI_DEFAULT);
即時一時停止と事前コール一時停止の実行順序は同じですが、次のサーバー・ラウンドトリップに事前コール一時停止が接続されるため、ネットワーク待機時間が短縮されます。たとえば、セッションレス・トランザクションの一時停止後に問合せを行う場合、事前コール一時停止を使用することで、サーバー・ラウンドトリップを削減し、待機時間を短縮できます。
例31-11 事後コールの一時停止(次のサーバー・ラウンドトリップの前に一時停止)
事後コールの一時停止(メイン・コールの後に次のサーバー・ラウンドトリップで一時停止)を発行するには、OCI_TRANS_SESSIONLESS
| OCI_TRANS_POST_CALL
フラグを指定してOCITransDetach
関数をコールします。
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_TRANS_POST_CALL);
OCIPing(svchp, errhp, OCI_DEFAULT);
これは、サーバーへの最後のラウンド・トリップ(たとえば、この作業単位の最後のDML)がわかっている場合に役立ちます。
例31-12 事後コールの一時停止
次の例では、「my_Sessionless_txn3」GTRIDで識別されるトランザクションは、OCIStmtExecute()
が正常に実行されると自動的に一時停止されます。
OCISessionGet (envhp, errhp, &svchp, authInfop, poolName, poolNameLen, NULL, 0,
NULL, NULL, NULL, OCI_SESSGET_SPOOL | OCI_SESSGET_PURITY_NEW);
OCIAttrSet (svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp);
memcpy(xidp->data, "my_Sessionless_txn3", strlen("my_Sessionless_txn3"));
xidp->gtrid_length = strlen("my_Sessionless_txn3");
OCIAttrSet(txnhp, OCI_HTYPE_TRANS, &xidp, sizeof(XID), OCI_ATTR_XID, errhp);
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW);
// Ask a post-call suspend
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_POST_CALL);
// Do the DML
OCIStmtPrepare(…); /* insert into mytab1(c1, c2) values (1, 1); */
OCIStmtBindByPos(…);
OCIStmtExecute(…); /* Server suspends the transaction after this DML is executed. */
関連項目:
-
Oracle Call Interface開発者ガイドのOCITransDetach()およびその他のOCIライブラリ関数を参照してください
31.5.8.1 一時停止されたセッションレス・トランザクションのタイムアウト
セッションレス・トランザクションのタイムアウト値は、新しいセッションレス・トランザクションの開始時に設定されます。
セッションレス・トランザクションが一時停止されるたびに、タイマーが動き始めます。セッションレス・トランザクションがタイムアウト期間内に再開された場合、タイマーはリセットされます。セッションレス・トランザクションがタイムアウト期間内に再開されない場合、そのトランザクションはロールバックされます。これは、Oracleデータベース内のすべてのトランザクションがリソース・ロック(行ロックなど)を保持する可能性があるためです。これらのリソースはトランザクションの実行中にのみ保持されるため、トランザクションが無期限にアクティブな状態が続くと、停止の原因となります。
ノート:
タイムアウト値をゼロにすることはできません。必ず正のタイムアウト値を指定する必要があります。
例31-13 セッションレス・トランザクションのタイムアウト
これは、タイムアウトしたセッションレス・トランザクションの例です。
次により、セッションレス・トランザクションが開始されます。
SET ECHO ON
SET SERVEROUTPUT ON
DROP TABLE IF EXISTS mytab1;
CREATE TABLE mytab1(c1 NUMBER, c2 NUMBER);
-- start a Sessionless transaction with timeout=20 secs
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_timeout_ex')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
END;
/
出力は次のとおりです。
PL/SQL procedure successfully completed.
次により、mytab1
表に値が挿入されます。
INSERT INTO mytab1(c1, c2) VALUES (1, 1);
出力は次のとおりです。
1 row created.
次により、トランザクションが一時停止されます。
EXEC DBMS_TRANSACTION.SUSPEND_TRANSACTION;
出力は次のとおりです。
PL/SQL procedure successfully completed.
タイムアウトにより開始されたトランザクションがロールバックされたため、トランザクションを再開しようとするとエラー26218で失敗します。
-- idle for 25 seconds
!sleep 25
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_timeout_ex')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_RESUME
);
END;
/
出力は次のとおりです。
ERROR at line 1:
ORA-26218: sessionless transaction with GTRID
6D795F73657373696F6E6C6573735F74696D656F75745F6578 does not exist.
前のトランザクションがロールバックされたため、同じGTRIDでの新しいトランザクションの開始が成功します。
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_timeout_ex')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
END;
/
出力は次のとおりです。
PL/SQL procedure successfully completed.
31.5.9 一時停止されたセッションレス・トランザクションの再開
一時停止状態のセッションレス・トランザクションを再開できます。
一時停止中のセッションレス・トランザクションの再開は、セッションレス・トランザクションを開始するのと似ています。両者の違いは、次のとおりです:
-
サーバーでセッションレス・トランザクションを再開する場合、
DBMS_TRANSACTION.START_TRANSACTION
ファンクションでは、TRANSACTION_RESUME
フラグでTRANSACTION_NEW
フラグを置き換えます。同様に、クライアント・ライブラリでセッションレス・トランザクションを再開する場合、OCITransStart
関数では、OCI_TRANS_RESUME
フラグでOCI_TRANS_NEW
フラグを置き換えます。 -
トランザクションの再開に使用されるタイムアウト・パラメータは、新しいセッションレス・トランザクションの開始時に指定されるトランザクション・タイムアウトではなく、再開タイムアウトを参照します。
関連項目:
START_TRANSACTION
ファンクションとOCITransStart
関数で使用されるタイムアウト・パラメータの詳細はそれぞれ、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』のDBMS_TRANSACTIONパッケージと「OCIクライアントでのセッションレス・トランザクションの再開」を参照してください
再開リクエストの実行時にアクティブなセッションレス・トランザクションが存在する場合、再開リクエストが成功するかどうかに関係なく、このようなアクティブなトランザクションは再開リクエストの実行前に一時停止されます。
データベース・サーバーでの新しいセッションレス・トランザクションの再開
サーバーで一時停止中のセッションレス・トランザクションを再開するには、DBMS_TRANSACTION.START_TRANSACTION
PL/SQLファンクションを実行します。フラグをTRANSACTION_RESUME
に設定します。
関連項目:
START_TRANSACTION
ファンクションの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』のDBMS_TRANSACTIONパッケージを参照してください
例31-14 データベース・サーバーでのセッションレス・トランザクションの再開
CREATE PROCEDURE PRINT_GTRID IS
BEGIN
IF DBMS_TRANSACTION.GET_TRANSACTION_TYPE() = DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS THEN
DBMS_OUTPUT.PUT_LINE('Sessionless transaction in session: ' || UTL_RAW.CAST_TO_VARCHAR2(DBMS_TRANSACTION.GET_TRANSACTION_ID()));
ELSE
DBMS_OUTPUT.PUT_LINE('Not a Sessionless transaction');
END IF;
END PRINT_GTRID;
/
出力は次のとおりです。
Procedure created.
次により、セッションレス・トランザクションが開始され、GTRIDが出力されます。
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_timeout_ex')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
PRINT_GTRID;
DBMS_OUTPUT.PUT_LINE('gtrid: ' || gtrid);
DBMS_OUTPUT.PUT_LINE('cast gtrid: ' || UTL_RAW.CAST_TO_VARCHAR2(gtrid));
END;
/
出力は次のとおりです。
Sessionless transaction in session: my_sessionless_timeout_ex
gtrid: 6D795F73657373696F6E6C6573735F74696D656F75745F6578
cast gtrid: my_sessionless_timeout_ex
PL/SQL procedure successfully completed.
次により、mytab1
表に値が挿入され、更新がフェッチされます。
INSERT INTO mytab1(c1, c2) values (1, 1);
SELECT * FROM mytab1;
出力は次のとおりです。
1 row created.
C1 C2
---------- ----------
1 1
次により、トランザクションが一時停止されます。
EXEC DBMS_TRANSACTION.SUSPEND_TRANSACTION;
SELECT * FROM mytab1;
出力は次のとおりです。
PL/SQL procedure successfully completed.
no rows selected
次により、トランザクションが再開されます。
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_timeout_ex')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_RESUME
);
PRINT_GTRID;
DBMS_OUTPUT.PUT_LINE('gtrid: ' || gtrid);
DBMS_OUTPUT.PUT_LINE('cast gtrid: ' || UTL_RAW.CAST_TO_VARCHAR2(gtrid));
END;
/
出力は次のとおりです。
Sessionless transaction in session: my_sessionless_timeout_ex
gtrid: 6D795F73657373696F6E6C6573735F74696D656F75745F6578
cast gtrid: my_sessionless_timeout_ex
PL/SQL procedure successfully completed.
次により、表に対して行われた更新がフェッチされます。
SELECT * FROM mytab1;
出力は次のとおりです。
C1 C2
---------- ----------
1 1
次により、トランザクションがロールバックされます。
ROLLBACK;
出力は次のとおりです。
Rollback complete.
次により、PRINT_GTRID
プロシージャが削除されます。
DROP PROCEDURE PRINT_GTRID;
出力は次のとおりです。
Procedure dropped.
OCIクライアントでのセッションレス・トランザクションの再開
クライアント側でセッションレス・トランザクションを再開する前に、サービス・コンテキスト・ハンドルに属性としてトランザクション・ハンドルを持たせ、トランザクション・ハンドルにOCI_ATTR_XID
属性を設定する必要があります。OCI_TRANS_SESSIONLESS | OCI_TRANS_RESUME
フラグおよびタイムアウトを指定してOCITransStart
関数をコールすると、既存のセッションレス・トランザクションが再開されます。OCIクライアントで新しいセッションレス・トランザクションを開始する場合と同様、再開コールは事前コール関数です。
例31-15 OCIクライアントでのセッションレス・トランザクションの再開
ub4 timeout = 20;
OCITransStart(svchp, errhp, timeout, OCI_TRANS_SESSIONLESS | OCI_TRANS_RESUME);
timeout: ここでのタイムアウトは再開タイムアウトを指し、トランザクション・タイムアウトとは異なります。再開タイムアウト値は、0または正の数値になります。インスタンス内では、セッションレス・トランザクションは常に1つのセッションにのみ関連付けることができます。したがって、セッションレス・トランザクションは、同じインスタンスで別のセッションに関連付けられている場合、再開できません。このような状況が発生した場合、再開タイムアウト値は、他のセッションがトランザクションを一時停止した結果、トランザクションを再開できるようになるまで、このセッションが待機する時間(秒)を指定します。待機がタイムアウトするまでに他のセッションがトランザクションを一時停止しない場合、ORA-25351エラーが発生します。再開タイムアウト値が0の場合、エラーはただちに発生します。
関連項目:
-
Oracle Call Interface開発者ガイドのOCITransStart()を参照してください
31.5.10 セッションレス・トランザクションのファイナライズ
この項では、セッションレス・トランザクションをコミットまたはロールバックする方法について説明します。
トピック:
31.5.10.1 セッションレス・トランザクションのコミット
セッションレス・トランザクションは、ローカル・トランザクションを終了する通常の方法で終了させることができます。セッションレス・トランザクションのコミットに関しては、OCI_COMMIT_ON_SUCCESS
モードのSQL文COMMIT
、OCITransCommit
およびOCIStmtExecute
を使用できます。
関連項目:
トランザクションのコミットおよびOCI_COMMIT_ON_SUCCESS
の詳細は、Oracle Call Interface開発者ガイドのコミットまたはロールバック操作に関する項およびOCIStmtExecute()を参照してください。
例31-16 アクティブなセッションレス・トランザクションのコミット
次の例では、匿名PL/SQLブロックによって、「my_sessionless_commit_ex_1」GTRIDで識別されるセッションレス・トランザクションが開始され、DML文が発行されてからトランザクションがコミットされます。
次により、セッションレス・トランザクションが開始されます。
DROP TABLE IF EXISTS mytab1;
CREATE TABLE mytab1(c1 NUMBER, c2 NUMBER);
-- start a Sessionless transaction and print the GTRID
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_commit_ex_1')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
END;
/
出力は次のとおりです。
PL/SQL procedure successfully completed.
次により、mytab1
表に値が挿入されます。
INSERT INTO mytab1(c1, c2) values (1, 1);
SELECT * FROM mytab1;
出力は次のとおりです。
1 row created.
C1 C2
---------- ----------
1 1
次により、トランザクションが一時停止されます。
EXEC DBMS_TRANSACTION.SUSPEND_TRANSACTION;
SELECT * FROM mytab1;
出力は次のとおりです。
PL/SQL procedure successfully completed.
no rows selected
次により、トランザクションが再開されます。
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_commit_ex_1')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_RESUME
);
END;
/
出力は次のとおりです。
PL/SQL procedure successfully completed.
次により、トランザクションがコミットされます。
COMMIT;
出力は次のとおりです。
Commit complete.
例31-17 OCITransCommit()
を使用したコミット
同様に、セッションでアクティブなセッションレス・トランザクションをコミットするには、クライアントからOCITransCommit()
APIを実行します。
次の例では、「my_Sessionless_txn5」GTRIDで識別されるセッションレス・トランザクションを開始し、DML文を発行してからトランザクションをコミットします。
OCISessionGet (envhp, errhp, &svchp, authInfop, poolName, poolNameLen, NULL, 0,
NULL, NULL, NULL, OCI_SESSGET_SPOOL | OCI_SESSGET_PURITY_NEW);
OCIAttrSet (svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp);
memcpy(xidp->data, "my_Sessionless_txn5", strlen("my_Sessionless_txn5"));
xidp->gtrid_length = strlen("my_Sessionless_txn5");
OCIAttrSet(txnhp, OCI_HTYPE_TRANS, &xidp, sizeof(XID), OCI_ATTR_XID, errhp);
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW);
OCIStmtPrepare(…); // Insert into mytab1(c1, c2) values (1, 1);
OCIStmtExecute(…);
OCITransCommit(svchp, errhp, OCI_DEFAULT);
例31-18 OCISessionRelease()
を使用したコミット
既存の動作に従って、OCISessionRelease
関数でアクティブなセッションレス・トランザクションをコミットすることもできます。次の例では、関数を使用して、「my_Sessionless_txn6」GTRIDを持つセッションレス・トランザクションをコミットします。
/* start Sessionless transaction */
OCISessionGet (envhp, errhp, &svchp, authInfop, poolName, poolNameLen, NULL, 0, NULL,
NULL, NULL, OCI_SESSGET_SPOOL | OCI_SESSGET_PURITY_NEW);
OCIAttrSet (svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp);
memcpy(xidp->data, "my_Sessionless_txn6", strlen("my_Sessionless_txn6"));
xidp->gtrid_length = strlen("my_Sessionless_txn6");
OCIAttrSet(txnhp, OCI_HTYPE_TRANS, &xidp, sizeof(XID), OCI_ATTR_XID, errhp);
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW);
/* done start Sessionless transaction */
OCIStmtPrepare(…); // Insert into mytab1(c1, c2) values (1, 1);
OCIStmtExecute(…);
OCISessionRelease(…); /* Commits the Sessionless transaction */
例31-19 OCI_COMMIT_ON_SUCCESS
を使用したコミット
次の例では、OCI_COMMIT_ON_SUCCESS
を使用してセッションレス・トランザクションをコミットします。
OCIError *errhp;
OCISvcCtx *svchp;
OCIStmt *stmthp;
OCIBind *bndhp[2];
char *dml = "INSERT INTO mytab1(c1, c2) VALUES (:1, :2)";
int number[2];
...
OCIStmtPrepare2(svchp, &stmthp, errhp, dml, strlen(dml), 0, 0, OCI_NTV_SYNTAX,
OCI_DEFAULT);
OCIBindByPos(stmthp, &bndhp[0], errhp, 1, &number[0], sizeof(int), SQLT_INT, 0, 0, 0,
0, 0, OCI_DEFAULT);
OCIBindByPos(stmthp, &bndhp[1], errhp, 2, &number[1], sizeof(int), SQLT_INT, 0, 0, 0,
0, 0, OCI_DEFAULT);
OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, OCI_COMMIT_ON_SUCCESS);
OCIStmtRelease(stmthp, errhp, 0, 0, OCI_DEFAULT);
関連項目:
-
START_TRANSACTION
ファンクションの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』のDBMS_TRANSACTIONパッケージを参照してください -
OCITransCommit()、OCISessionRelease()およびOCIStmtExecute()の詳細は、Oracle Call Interface開発者ガイドを参照してください
31.5.10.2 セッションレス・トランザクションのロールバック
セッションレス・トランザクションは、SQL文ROLLBACK
、OCITransRollback()
、OCIRequestEnd()
など、ローカル・トランザクションを終了させる通常の方法でロールバックできます。
関連項目:
オープン・トランザクションをロールバックできるOCIRequestEnd()
およびOCITransRollback()
の詳細は、Oracle Call Interface開発者ガイドのOCIRequestEnd() - TAC-23cおよびOCITransRollback()を参照してください
例31-20 PL/SQLを使用したロールバック
次の例では、匿名PL/SQLブロックによって、「my_sessionless_rollback_ex_1」GTRIDで識別されるセッションレス・トランザクションが開始され、DML文が発行されて、トランザクションがロールバックされます。
次のコードは、セッションレス・トランザクションを開始します。
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_rollback_ex_1')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_NEW
);
END;
/
出力は次のとおりです。
PL/SQL procedure successfully completed.
次により、mytab1
表に値が挿入され、更新が表からフェッチされます。
INSERT INTO mytab1(c1, c2) values (1, 1);
SELECT * FROM mytab1;
出力は次のとおりです。
1 row created.
C1 C2
---------- ----------
1 1
次により、トランザクションが一時停止されます。
EXEC DBMS_TRANSACTION.SUSPEND_TRANSACTION;
出力は次のとおりです。
PL/SQL procedure successfully completed.
表からフェッチしようとしても結果は表示されません。
no rows selected
それでは、トランザクションを再開します。
DECLARE
gtrid VARCHAR2(128);
BEGIN
gtrid := DBMS_TRANSACTION.START_TRANSACTION
( UTL_RAW.CAST_TO_RAW('my_sessionless_rollback_ex_1')
, DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
, 20
, DBMS_TRANSACTION.TRANSACTION_RESUME
);
END;
/
出力は次のとおりです。
PL/SQL procedure successfully completed.
次により、表からトランザクションの詳細がフェッチされます。
SELECT * FROM mytab1;
出力は次のとおりです。
C1 C2
---------- ----------
1 1
次により、トランザクションがロールバックされます。
ROLLBACK;
出力は次のとおりです。
Rollback complete.
次は、ロールバックが実際に成功したことを確認します。
SELECT * FROM mytab1;
出力は次のとおりです。
no rows selected
例31-21 OCITransRollback()
同様に、セッション内のアクティブなセッションレス・トランザクションをロールバックするには、クライアントからOCITransRollback()
APIを実行します。
次の例では、「my_Sessionless_txn8」GTRIDで識別されるセッションレス・トランザクションを開始し、DML文を発行してから、トランザクションをロールバックします。
/* start Sessionless transaction */
OCISessionGet (envhp, errhp, &svchp, authInfop, poolName, poolNameLen, NULL, 0, NULL,
NULL, NULL, OCI_SESSGET_SPOOL | OCI_SESSGET_PURITY_NEW);
OCIAttrSet (svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp);
char gtrid = "my_Sessionless_txn8";
memcpy(xidp->data, gtrid, strlen(gtrid));
xidp->gtrid_length = strlen(gtrid);
OCIAttrSet(txnhp, OCI_HTYPE_TRANS, &xidp, sizeof(XID), OCI_ATTR_XID, errhp);
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW);
/* done start Sessionless transaction */
OCIStmtPrepare(…); // Insert into mytab1(c1, c2) values (1, 1);
OCIStmtExecute(…);
OCITransRollback(svchp, errhp, OCI_DEFAULT);
例31-22 OCIRequestEnd()
を使用したロールバック
既存の動作と同様に、OCIRequestEnd()
関数は、開いているセッションレス・トランザクションをロールバックすることもできます。
次の例では、「my_Sessionless_txn9」GTRIDで識別されるトランザクションは、OCIRequestEnd()
が正常に実行された後にロールバックされます。
/* start Sessionless transaction */
OCISessionGet (envhp, errhp, &svchp, authInfop, poolName, poolNameLen, NULL, 0, NULL,
NULL, NULL, OCI_SESSGET_SPOOL | OCI_SESSGET_PURITY_NEW);
OCIAttrSet (svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp);
char gtrid = "my_Sessionless_txn9";
memcpy(xidp->data, gtrid, strlen(gtrid));
xidp->gtrid_length = strlen(gtrid);
OCIAttrSet(txnhp, OCI_HTYPE_TRANS, &xidp, sizeof(XID), OCI_ATTR_XID, errhp);
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW);
/* done start Sessionless transaction */
OCIRequestBegin(…);
OCIStmtPrepare(…); // Insert into mytab1(c1, c2) values (1, 1);
OCIStmtExecute(…);
OCIRequestEnd(…); /* Rolled back the Sessionless transaction */
関連項目:
-
START_TRANSACTION
ファンクションの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』のDBMS_TRANSACTIONパッケージを参照してください -
Oracle Call Interface開発者ガイドのOCITransRollback()、OCIRequestEnd() - TAC-23cおよびOCIStmtExecute()を参照してください
31.5.11 例: OCI APIを使用したセッションレス・トランザクション
OCI APIを使用したセッションレス・トランザクションのエンドツーエンドの例を次に示します。
ノート:
この例では、サンプル・スキーマのscott.dept
表が必要です。
#ifndef OCI_ORACLE
#include <oci.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define USERNAME_LEN 256
#define PASSWORD_LEN 1025
#define DBNAME_LEN 1025
OCIEnv *envhp = (OCIEnv *)0;
OCIError *errhp = (OCIError *)0;
OCIAuthInfo *authp = (OCIAuthInfo *)0;
OCISPool *poolhp = (OCISPool *)0;
OCISvcCtx *svchp = (OCISvcCtx *)0;
OCITrans *txnhp = (OCITrans *)0;
OCIStmt *stmthp = (OCIStmt *)0;
OCIBind *bndhp[3];
OCIDefine *defhp[3];
char *username;
char *password;
char *dbname;
OraText *pool_name;
ub4 pool_name_len;
void insert_dept(int deptno, const char *dname, const char *loc);
void query_dept(const char *description);
void query_transaction_type(const char *description);
void chk_err(sword ret, OCIError *errhp, sb2 linenum);
#define CHK_ERR(cmd) chk_err(cmd, errhp, __LINE__)
int main(int argc, char **argv)
{
int nread;
size_t usernameLen = USERNAME_LEN;
size_t passwordLen = PASSWORD_LEN;
size_t dbnameLen = DBNAME_LEN;
unsigned int i;
unsigned char *gtrid;
unsigned int gtridlen = 0;
unsigned char gtridbuf[64];
/* get username, password, dbname (connect string) */
username = malloc(USERNAME_LEN * sizeof(char));
password = malloc(PASSWORD_LEN * sizeof(char));
dbname = malloc(DBNAME_LEN * sizeof(char));
memset(username, 0, USERNAME_LEN);
memset(password, 0, PASSWORD_LEN);
memset(dbname, 0, DBNAME_LEN);
printf("enter username:");
if ((nread = (int)getline((char **)&username, &usernameLen, stdin)) == -1)
{
printf("ERROR: Failed to get username\n");
return -1;
}
if(strlen(username) >= 1)
username[strlen(username)-1] = '\0';
printf("enter password:");
if ((nread = (int)getline((char **)&password, &passwordLen, stdin)) == -1)
{
printf("ERROR: Failed to get password\n");
return -1;
}
if(strlen(password) >= 1)
password[strlen(password)-1] = '\0';
printf("enter dbname:");
if ((nread = (int)getline((char **)&dbname, &dbnameLen, stdin)) == -1)
{
printf("ERROR: Failed to get dbname\n");
return -1;
}
if(strlen(dbname) >= 1)
dbname[strlen(dbname)-1] = '\0';
printf("\n");
/* set up */
{
int ret;
/* Create Environment */
if ((ret = OCIEnvCreate((OCIEnv **)&envhp, (ub4)OCI_DEFAULT | OCI_OBJECT,
(dvoid *)0, (dvoid * (*)(dvoid *, size_t))0,
(dvoid * (*)(dvoid *, dvoid *, size_t))0,
(void (*)(dvoid *, dvoid *))0, (size_t)0,
(dvoid **)0)) != OCI_SUCCESS)
{
printf("failed to create OCIEnv. ret = %d\n", ret);
return 1;
}
/* Allocate error handle */
CHK_ERR(OCIHandleAlloc(envhp, (void **)&errhp, OCI_HTYPE_ERROR, 0, 0));
/* create a session pool and get a session from it */
CHK_ERR(OCIHandleAlloc(envhp, (void **)&poolhp, OCI_HTYPE_SPOOL, 0, 0));
CHK_ERR(OCISessionPoolCreate(
envhp, errhp, poolhp, &pool_name, &pool_name_len, (OraText *)dbname,
strlen(dbname), 4, 10, 1, (OraText *)username, strlen(username),
(OraText *)password, strlen((char *)password), OCI_DEFAULT));
CHK_ERR(OCISessionGet(envhp, errhp, &svchp, NULL, pool_name,
pool_name_len, 0, 0, 0, 0, 0, OCI_SESSGET_SPOOL));
/* allocate a transaction handle and set is as OCI_ATTR_TRANS */
CHK_ERR(OCIHandleAlloc(envhp, (void **)&txnhp, OCI_HTYPE_TRANS, 0, 0));
CHK_ERR(
OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp));
}
/* Query dept table before changes. */
query_dept("before begin");
/* Start a new Sessionless transaction with a generated GTRID. Insert a row
* in dept table, then query the table. We should see the inserted row. */
CHK_ERR(
OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp));
CHK_ERR(
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW));
insert_dept(50, "DEVELOPMENT1", "SEATTLE");
query_dept("in sessionless txn, after insert");
/* Get the generated GTRID so we can suspend and resume transaction. */
CHK_ERR(OCIAttrGet(txnhp, OCI_HTYPE_TRANS, >rid, >ridlen,
OCI_ATTR_TRANS_NAME, errhp));
memcpy(gtridbuf, gtrid, gtridlen);
printf("Generated GTRID in hexadecimal is: ");
for (i = 0; i < gtridlen; ++i)
printf("%02X", (unsigned int)gtridbuf[i]);
printf("\n");
/* Suspend Sessionless transaction and query dept table. We should not see
* the inserted row. */
CHK_ERR(OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_DEFAULT));
query_dept("outside sessionless txn");
/* Set OCI_ATTR_TRANS to NULL. */
CHK_ERR(OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, NULL, 0, OCI_ATTR_TRANS, errhp));
/* release the session here and get another session connected to the
* same database. This sessions can be a different session and can connect
* to a different instance in RAC configuration. */
CHK_ERR(OCISessionRelease(svchp, errhp, 0, 0, OCI_DEFAULT));
CHK_ERR(OCISessionGet(envhp, errhp, &svchp, NULL, pool_name,
pool_name_len, 0, 0, 0, 0, 0, OCI_SESSGET_SPOOL));
/* resume the Sessionless transaction and query dept. We can see the previous
* change. */
CHK_ERR(OCIAttrSet(txnhp, OCI_HTYPE_TRANS, gtrid, gtridlen,
OCI_ATTR_TRANS_NAME, errhp));
CHK_ERR(
OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, txnhp, 0, OCI_ATTR_TRANS, errhp));
CHK_ERR(
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_RESUME));
query_dept("resumed sessionless txn");
/* insert an additional row, and commit the Sessionless transaction. */
insert_dept(51, "DEVELOPMENT2", "SAN FRANCISCO");
CHK_ERR(OCITransCommit(svchp, errhp, OCI_DEFAULT));
/* after commit, we can confirm no transaction is active in the session. when
* query the table, we can see both rows got inserted. */
query_transaction_type("after commit");
query_dept("after commit");
/* tear down */
{
if (txnhp)
CHK_ERR(OCIHandleFree(txnhp, OCI_HTYPE_TRANS));
if (svchp)
{
CHK_ERR(OCISessionRelease(svchp, errhp, 0, 0, OCI_DEFAULT));
svchp = NULL;
}
CHK_ERR(OCISessionPoolDestroy(poolhp, errhp, OCI_DEFAULT));
if (poolhp)
OCIHandleFree((void *)poolhp, OCI_HTYPE_SPOOL);
}
free(dbname);
free(password);
free(username);
return 0;
}
void insert_dept(int deptno, const char *dname, const char *loc)
{
OraText dml[] = "INSERT INTO dept(deptno, dname, loc) values (:1, :2, :3)";
CHK_ERR(OCIStmtPrepare2(svchp, &stmthp, errhp, dml, strlen((char *)dml), 0,
0, OCI_NTV_SYNTAX, OCI_DEFAULT));
CHK_ERR(OCIBindByPos(stmthp, &bndhp[0], errhp, 1, &deptno, sizeof(deptno),
SQLT_INT, 0, 0, 0, 0, 0, OCI_DEFAULT));
CHK_ERR(OCIBindByPos(stmthp, &bndhp[1], errhp, 2, (void *)dname,
strlen(dname) + 1, SQLT_STR, 0, 0, 0, 0, 0,
OCI_DEFAULT));
CHK_ERR(OCIBindByPos(stmthp, &bndhp[1], errhp, 3, (void *)loc,
strlen(loc) + 1, SQLT_STR, 0, 0, 0, 0, 0, OCI_DEFAULT));
CHK_ERR(OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, OCI_DEFAULT));
CHK_ERR(OCIStmtRelease(stmthp, errhp, 0, 0, OCI_DEFAULT));
}
void query_dept(const char *description)
{
OraText query[] = "SELECT deptno, dname, loc FROM dept ORDER BY deptno";
int deptno;
OraText dname[20];
OraText loc[20];
int i = 1;
int ret;
printf("DEPT table [%s]\n", description);
CHK_ERR(OCIStmtPrepare2(svchp, &stmthp, errhp, query, strlen((char *)query),
0, 0, OCI_NTV_SYNTAX, OCI_DEFAULT));
CHK_ERR(OCIDefineByPos(stmthp, &defhp[0], errhp, 1, &deptno, sizeof(deptno),
SQLT_INT, 0, 0, 0, OCI_DEFAULT));
CHK_ERR(OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (void *)dname, 20,
SQLT_STR, 0, 0, 0, OCI_DEFAULT));
CHK_ERR(OCIDefineByPos(stmthp, &defhp[2], errhp, 3, (void *)loc, 20,
SQLT_STR, 0, 0, 0, OCI_DEFAULT));
CHK_ERR(OCIStmtExecute(svchp, stmthp, errhp, 0, 0, 0, 0, OCI_DEFAULT));
while ((ret = OCIStmtFetch2((OCIStmt *)stmthp, (OCIError *)errhp, (ub4)1,
(ub4)OCI_FETCH_NEXT, (sb4)0, (ub4)OCI_DEFAULT)) == 0)
{
printf("Row %d: (%d, %s, %s)\n", i++, deptno, dname, loc);
}
if (ret && ret != OCI_NO_DATA)
CHK_ERR(ret);
printf("\n");
CHK_ERR(OCIStmtRelease(stmthp, errhp, 0, 0, OCI_DEFAULT));
}
void query_transaction_type(const char *description)
{
OraText query[] =
"SELECT NVL(TO_CHAR(DBMS_TRANSACTION.GET_TRANSACTION_TYPE), 'NULL') from "
"dual";
char txn_type[10];
int ret;
printf("Query transaction type [%s]: ", description);
CHK_ERR(OCIStmtPrepare2(svchp, &stmthp, errhp, query, strlen((char *)query),
0, 0, OCI_NTV_SYNTAX, OCI_DEFAULT));
CHK_ERR(OCIDefineByPos(stmthp, &defhp[0], errhp, 1, &txn_type,
sizeof(txn_type), SQLT_STR, 0, 0, 0, OCI_DEFAULT));
CHK_ERR(OCIStmtExecute(svchp, stmthp, errhp, 0, 0, 0, 0, OCI_DEFAULT));
while (
(ret = OCIStmtFetch2((OCIStmt *)stmthp, (OCIError *)errhp, (ub4)1,
(ub4)OCI_FETCH_NEXT, (sb4)0, (ub4)OCI_DEFAULT)) == 0)
printf("%s\n", txn_type);
if (ret && ret != OCI_NO_DATA)
CHK_ERR(ret);
printf("\n");
CHK_ERR(OCIStmtRelease(stmthp, errhp, 0, 0, OCI_DEFAULT));
}
void chk_err(sword ret, OCIError *errhp, sb2 linenum)
{
OraText msgbuf[1024];
sb4 errcode;
memset((void *)msgbuf, '\0', 1024);
if (ret == OCI_ERROR || ret == OCI_SUCCESS_WITH_INFO)
OCIErrorGet((dvoid *)errhp, (ub4)1, (OraText *)NULL, &errcode, msgbuf,
(ub4)sizeof(msgbuf), (ub4)OCI_HTYPE_ERROR);
if (ret == OCI_SUCCESS)
return;
printf("------------------------------------------------------------\n");
printf(":Line # %d: ", linenum);
switch (ret)
{
case OCI_ERROR:
case OCI_SUCCESS_WITH_INFO:
printf(ret == OCI_SUCCESS_WITH_INFO ? "OCI_SUCCESS_WITH_INFO\n"
: "OCI_ERROR\n");
printf("%s\n", msgbuf);
break;
case OCI_NEED_DATA:
printf("OCI_NEED_DATA\n");
break;
case OCI_NO_DATA:
printf("OCI_NO_DATA\n");
break;
case OCI_INVALID_HANDLE:
printf("OCI_INVALID_HANDLE\n");
exit(1);
case OCI_STILL_EXECUTING:
printf("OCI_STILL_EXECUTING\n");
break;
case OCI_CONTINUE:
printf("OCI_CONTINUE\n");
break;
default:
printf("Error Code %d\n", ret);
break;
}
}
出力は次のようになります。出力には、異なるユーザー名、パスワード、dbname (TNS名)、生成されたGTRIDが含まれる場合があります。
enter username:scott
enter password:tiger
enter dbname:cdb1_pdb1
DEPT table [before begin]
Row 1: (10, ACCOUNTING, NEW YORK)
Row 2: (20, RESEARCH, DALLAS)
Row 3: (30, SALES, CHICAGO)
Row 4: (40, OPERATIONS, BOSTON)
DEPT table [in sessionless txn, after insert]
Row 1: (10, ACCOUNTING, NEW YORK)
Row 2: (20, RESEARCH, DALLAS)
Row 3: (30, SALES, CHICAGO)
Row 4: (40, OPERATIONS, BOSTON)
Row 5: (50, DEVELOPMENT1, SEATTLE)
Generated GTRID in hexadecimal is: 688330D5C1764F96BFF8267F58E5709E
DEPT table [outside sessionless txn]
Row 1: (10, ACCOUNTING, NEW YORK)
Row 2: (20, RESEARCH, DALLAS)
Row 3: (30, SALES, CHICAGO)
Row 4: (40, OPERATIONS, BOSTON)
DEPT table [resumed sessionless txn]
Row 1: (10, ACCOUNTING, NEW YORK)
Row 2: (20, RESEARCH, DALLAS)
Row 3: (30, SALES, CHICAGO)
Row 4: (40, OPERATIONS, BOSTON)
Row 5: (50, DEVELOPMENT1, SEATTLE)
Query transaction type [after commit]: NULL
DEPT table [after commit]
Row 1: (10, ACCOUNTING, NEW YORK)
Row 2: (20, RESEARCH, DALLAS)
Row 3: (30, SALES, CHICAGO)
Row 4: (40, OPERATIONS, BOSTON)
Row 5: (50, DEVELOPMENT1, SEATTLE)
Row 6: (51, DEVELOPMENT2, SAN FRANCISCO)
31.5.12 セッションレス・トランザクションを使用するためのルールおよびガイドライン
これらのルールおよびガイドラインは、OCIクライアントAPIに適用されます。
セッションレス・トランザクション機能は、OCIクライアントAPIでセッションレス・トランザクションを使用する場合に、次のルールおよびガイドラインが適用されます。
現在のトランザクション・ハンドルに対する制限
セッションレス・トランザクションがアクティブな場合、サービス・コンテキスト・ハンドルのOCI_ATTR_TRANS
属性を変更したり、トランザクション・ハンドルを解放することはできません。
OCIコール関数のルール
OCIコール関数は、次のように区別されます:
-
メイン・コール - この関数は即時に実行されます
-
事前コール - この関数は、次のサーバーラウンドトリップに接続された状態で、コールの前に実行されます
-
事後コール - この関数は、次のサーバーラウンドトリップに接続された状態で、コールの後に実行されます
最後の事前コール関数コールを優先
1回のラウンドトリップにつき、最大1つの事前コール関数(開始、再開または一時停止のいずれか)および1つの事後コール一時停止を実行できます。サーバー・ラウンドトリップの前に、次のサーバー・ラウンドトリップが行われる前に開始、再開または事前コール一時停止のコールが複数ある場合は、最後のコールのみがデータベース・サーバーに送信されます。
次に、事前コール再開[ OCITransStart
(OCI_TRANS_SESSIONLESS
| OCI_TRANS_RESUME
) ]および事後コール一時停止[ OCITransDetach(OCI_TRANS_SESSIONLESS
| OCI_TRANS_POST_CALL
) ]コールのみがサーバーに送信される例を示します。
OCIAttrSet(txnhp, OCI_HTYPE_TRANS, xidp, sizeof(XID), OCI_ATTR_XID, errhp);
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW);
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_TRANS_PRE_CALL); /* this overrides the above OCITransStart call */
memcpy(xidp->data, "GTRID-2", strlen("GTRID-2"));
OCIAttrSet(txnhp, OCI_HTYPE_TRANS, xidp, sizeof(XID), OCI_ATTR_XID, errhp);
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_RESUME); /* this overrides the above OCITransDetach(PRE_CALL) call */
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_TRANS_POST_CALL); /* does NOT override anything since it's a post-call */
OCIStmtPrepare2(svchp, &stmthp, errhp, dml, strlen(dml), 0, 0, OCI_NTV_SYNTAX, OCI_DEFAULT);
OCIBindByPos(…);
OCIStmtExecute(svchp, stmthp, errhp, 1, 0, 0, 0, OCI_DEFAULT);
/* This is a server round trip that attaches the pre-call resume and post-call suspend */
事後コール一時停止が記録された場合、事前コール一時停止による開始トランザクションは許可されない
クライアントでは、事後コール一時停止がコールされ、次のサーバー・ラウンドトリップに接続されていない場合、新しいセッションレス・トランザクションの開始、既存のセッションレス・トランザクションの再開、または次のサーバー・ラウンドトリップの前にセッションレス・トランザクションを事前コールするリクエストはできません。実行する前に、サーバー・ラウンドトリップ(たとえば、OCIPing()
をコール)を作成してください。次に例を示します。
OCITransDetach(svchp, errhp, OCI_TRANS_SESSIONLESS | OCI_TRANS_POST_CALL); /* a post-call suspend */
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW); /* this is prohibited and will throw 26215 error */
OCIPing(svchp, errhp, OCI_DEFAULT); /* This incurs a server round trip that attaches post-call suspend */
OCITransStart(svchp, errhp, 60, OCI_TRANS_SESSIONLESS | OCI_TRANS_NEW); /* no error thrown*/
リクエスト境界の動作
リクエスト境界を定義するコールは、OCIRequestBegin
、OCIRequestEnd
およびOCISessionRelease
です。これらのリクエスト境界に関してセッション内でアクティブなセッションレス・トランザクションは、現在、セッション内のローカル・トランザクションに適用される動作に従います。
31.5.13 エラー・メッセージおよび通知
次の表に、セッションレス・トランザクションの使用中に表示される可能性のあるエラーの概要、これらのエラーが表示されるタイミングの説明、およびこれらのエラーを解決するために実行できる次のステップを示します。ただし、これは発生する可能性のあるすべてのエラーを網羅したリストではありません。
表31-1 エラー・サマリー
エラー・コード | 原因 | 解決方法 |
---|---|---|
ORA-26202 |
セッションレス・トランザクションではない(ただし、ローカル・トランザクションまたはXAトランザクション・ブランチではある)トランザクションの一時停止 |
一時停止中のトランザクションがセッションレス・トランザクションであることを確認します。 |
ORA-26204 |
アクティブなセッションレス・トランザクションは、クライアント・ライブラリによって生成されたGTRIDを使用して開始されましたが、GTRIDはユーザーによって取得されていません。アクティブなトランザクションを一時停止するアクションにより、ユーザーはトランザクションに永久にアクセスできなくなり、許可されません。 |
アクションを実行する前にGTRIDを取得します。 GTRIDを取得する方法については、このドキュメントの「グローバル・トランザクションIDの取得」の項の「OCIクライアントでのGTRIDの取得」の項を参照してください。 |
ORA-26207 |
サーバーがセッションレス・トランザクション機能をサポートしていない場合のセッションレス・トランザクションの開始または一時停止 |
Oracle Database 23ai、23.6以降のバージョンを使用していることを確認します。 |
ORA-26210 |
データベース・サーバー上で開始または再開されたセッションレス・トランザクションがアクティブである場合、 |
データベース・サーバー上で開始または再開されたアクティブなセッションレス・トランザクションを一時停止または終了します。 |
ORA-26211 |
アクティブなセッションレス・トランザクションの開始に使用されたメソッド(データベース・サーバーまたはクライアント上)は、現在の開始コールまたは一時停止コールに使用されるメソッドとは異なります。 |
アクティブなセッションレス・トランザクションを開始したのと同じ方法で、最初にアクティブなトランザクションを一時停止します。 |
ORA-26213 |
開始トランザクション関数をコールする際、(適切なフラグを使用して)それが新しいセッションレス・トランザクションを開始するためのコールであるか、または既存のトランザクションを再開するためのコールであるかを指定していません。 |
新しいセッションレス・トランザクションを開始するか、既存のトランザクションを再開するかをフラグで指定してください。 |
ORA-26214 |
クライアント上でセッションレス・トランザクションを一時停止する際に、コールをすぐに実行するか、サーバー・ラウンドトリップに接続し、メイン・コールの前に実行するか(事前コール一時停止)、または次のサーバー・ラウンドトリップに接続してメイン・コールの後に実行するか(事後コール一時停止)を指定していません。 |
セッションレス・トランザクションを一時停止する場合は、 |
ORA-26215 |
一時停止は、次のサーバー・ラウンドトリップの後に実行されるように記録されます。一時停止が完了するまでは、新しいセッションレス・トランザクションを開始したり、既存のセッションレス・トランザクションを再開することはできません。また、次のサーバー・ラウンドトリップの前に実行される一時停止は許可されません。 |
サーバーへのラウンドトリップが発生します。 |
ORA-26216 |
新規開始コールまたは再開コールは記録されていますが、まだ実行されていません。したがって、 |
サーバーへのラウンドトリップが発生します。 |
ORA-26217 |
GTRIDで識別される新規トランザクションを開始しようとしていますが、データベース・サーバーには、このようなGTRIDで識別されるセッションレス・トランザクションがすでに存在します |
新しいセッションレス・トランザクションを開始するには、一意のGTRIDを使用します。 |
ORA-26218 |
GTRIDで識別された既存のトランザクションを再開しようとしていますが、このようなセッションレス・トランザクションはデータベース・サーバーに存在しません |
再開するトランザクションのGTRIDで識別されるセッションレス・トランザクションが実際に存在することを確認してください。 |
ORA-26219 |
セッションレス・トランザクションがアクティブなときに、サービス・コンテキスト・ハンドルの |
|
ORA-26220 |
セッションレス・トランザクションがアクティブなときにトランザクション・ハンドルを解放しようとしています |
セッションレス・トランザクションがアクティブでない場合にのみ、トランザクション・ハンドルを解放してください。 |
31.6 セッションレス・トランザクションとOracle協調分散型トランザクションの相互運用性
セッションレス・トランザクションでは、データベース・リンクを使用して、直接的または間接的にリモート・データベース・オブジェクトにアクセスできます。Oracle協調分散型トランザクションは、データベース・リンクを使用してリモートOracle Database上の表にアクセスすると開始されます。セッションレス・トランザクションがリモート・データベース・オブジェクトにアクセスする場合、Oracle Databaseは、2PCプロトコル操作中にマルチレベル(セッションレス・トランザクションおよびOracle調整トランザクション)の階層型分散トランザクションをシームレスに管理します。
31.7 セッションレス・トランザクションの制限
XAトランザクションへのセッションレス・トランザクションの昇格はサポートされていません
ローカル・トランザクションはXAトランザクションにプロモートすることができます。その場合、元のローカル・トランザクションはXAトランザクションのトランザクション・ブランチになります。ただし、Oracle Database 23aiバージョン23.6では、セッションレス・トランザクションをXAトランザクションにプロモートすることはできません。この制限は近い将来に解決される予定です。
セーブポイントへのロールバックはサポートされていません
Oracle Database 23ai, Version 23.6のマルチインスタンスRAC構成におけるセッションレス・トランザクションでは、セーブポイントへのロールバックはサポートされていません。このような構成では、セッションレス・トランザクション・セーブポイントが1つのセッションで作成され、そのトランザクションが後で別のセッションで再開された場合、セーブポイントは使用できません。セーブポイントへのロールバックがコールされると、ORA-1086エラーが発生します。
セッションレス・トランザクションでは、セッション・ステートがセッション間で継承されません
トランザクションが最後に一時停止されたセッションと異なるセッションでセッションレス・トランザクションを再開する場合、セッション状態(ALTER SESSION
、NLS設定、一時LOB状態、PL/SQL状態およびUGA状態によって設定されたすべてのパラメータなど)は、新しいセッションに継承されません。したがって、あるセッションでセッションレス・トランザクションを一時停止し、別のセッションで再開する場合は、セッション状態を適切に設定する必要があります。同じ結果(NLS設定など)を取得するには、必ず同じセッション状態を再確立する必要があります。
挿入ダイレクト・ロード、オンライン・ダイレクト・ロードおよびパラレルDMLはシリアライズされます
Oracle Database 23aiバージョン23.6では、セッションレス・トランザクションで挿入ダイレクト・ロード(IDL)またはオンライン・ダイレクト・ロード(ODL)操作を行うと、操作は従来の挿入方法に戻ります。同様に、パラレルDML (PDML)文がセッションレス・トランザクションで発行されると、このようなDMLはシリアルで実行されます。ただし、これらの制限の一部は今後のリリースで削除されます。
データベース・リンクはサポートされていません
データベース・リンク(DBLink)は、現在、セッションレス・トランザクションと互換性がありません。この制限は対処されています。