目次 前 次 PDF


グローバル・トランザクションのコーディング

グローバル・トランザクションのコーディング
このトピックには次の項が含まれます:
グローバル・トランザクションとは
グローバル・トランザクションとは、複数のリソース・マネージャを使用し、複数のサーバー上で行われる複数の操作を1つの論理単位として処理できるようにするメカニズムです。
プロセスがトランザクション・モードになると、サーバーにリクエストされたサービスが現在のトランザクションにかわって処理されます。呼び出されてトランザクションに参加したサービスは、「トランザクションの参加リソース」と呼ばれます。参加リソースから返される値によって、トランザクションの結果が変わる場合があります。
グローバル・トランザクションは複数のローカル・トランザクションから構成され、各トランザクションは同じリソース・マネージャにアクセスします。リソース・マネージャは、同時実行性制御とデータ更新の原子性を実現します。ローカル・トランザクションでは、アクセスが正常に終了するか、または全体が失敗します。つまり、一部だけが成功することはありません。
1つのトランザクションに参加可能なサーバー・グループは最大16個です。
Oracle Tuxedoシステムでは、グローバル・トランザクションが参加しているリソース・マネージャと共に管理され、原子性、一貫性、独立性、および持続性という特徴を持つ特定シーケンスの操作として処理されます。つまり、グローバル・トランザクションは、以下のような特徴を持つ論理的な作業単位と言えます。
すべての部分が成功するか、または何も効果が発生しません。
操作が実行されて、リソースがある一貫した状態から別の状態に正しく移行します。
ほかのトランザクションから中間の結果にアクセスすることはできません。ただし、トランザクションに含まれるプロセスには、別のプロセスに関連付けられたデータにアクセスできるものもあります。
シーケンスが完了すると、その結果はどのような失敗による影響も受けません。
Oracle Tuxedoシステムでは、個々のグローバル・トランザクションのステータスがトラッキングされ、そのトランザクションをコミットするかロールバックするかが決定されます。
注意:
トランザクションで、flags引数に明示的にTPNOTRANを設定してtpcall()tpacall()またはtpconnect()を呼び出した場合、呼び出されたサービスによって実行される操作はトランザクションに含まれません。その場合、呼出し側プロセスは、呼び出されたサービスを現在のトランザクションの参加リソースとは見なしません。結果として、呼び出されたプロセスによって実行されたサービスは、現在のトランザクションの結果からの影響を受けません。XA準拠のサーバー・グループのサービスに対する呼出しにTPNOTRANが設定されている場合、その呼出しはトランザクション・モード外、または別のトランザクションで実行されます。どちらで実行されるかは、サービスの設定方法とコーディング方法によって決まります。詳細については、9-15ページの「グローバル・トランザクションの暗黙的な定義」を参照してください。
トランザクションの開始
グローバル・トランザクションを開始するには、次のシグネチャを使用してtpbegin(3c)関数を呼び出します。
int
tpbegin(unsigned long timeout, long flags)
表9-1は、tpbegin()関数の引数を示しています
 
表9-1 tpbegin()関数の引数
フィールド
説明
timeout
トランザクションがタイムアウトになるまでの時間(秒単位)。この引数に0を指定すると、システムで可能な最長時間(秒単位)に設定されます。つまり、timeoutには、システムで定義された符号なしlong型の最大値が設定されます。
timeoutに0または非現実的な大きな値を指定すると、システムによるエラー検出と報告が遅れる原因となります。timeoutパラメータを使用すると、サービス・リクエストに対するレスポンスが妥当な時間内に確実に返されるようになります。また、ネットワーク障害などの問題が発生した場合に、コミットされる前にトランザクションを終了できます。
レスポンスを人が待っている場合、このパラメータには小さな値(可能な場合は30秒未満)を設定します。
本番システムの場合、timeoutに大きな値を設定して、システムの負荷やデータベースの競合に起因する遅延に対応できるようにします。予測される平均レスポンス時間を2、3倍した時間が最適です。
注意:
timeoutパラメータに設定する値は、Oracle Tuxedoのアプリケーション管理者が構成ファイルに設定したSCANUNITパラメータの値と一致していなければなりません。SCANUNITパラメータには、タイムアウトになったトランザクションとサービス・リクエストでブロックされた呼出しがないかどうかを確認、つまりスキャンする頻度を指定します。このパラメータの値は、定期的なスキャンの間隔(スキャニング単位)を表します。

timeoutパラメータには、スキャニング単位より大きな値を設定します。timeoutパラメータに設定された値がスキャニング単位より小さいと、トランザクションがタイムアウトになる時間と、そのタイムアウトが検出される時間にずれが生じます。SCANUNITのデフォルト値は10秒です。timeoutパラメータの設定値についてはアプリケーション管理者と検討し、timeoutに設定した値がシステム・パラメータの値と矛盾しないようにします。
flags
現在は定義されていません。0を設定します。
tpbegin()は、どのプロセスからも呼び出すことができます。ただし、既にトランザクション・モードになっているプロセス、または未処理の応答を待っているプロセスからは呼び出すことができません。トランザクション・モードでtpbegin()が呼び出されると、プロトコル・エラーになって呼出しが失敗し、tperrno(5)TPEPROTOが設定されます。プロセスがトランザクション・モードの場合でも、この失敗はトランザクションには影響しません。
リスト9-1は、グローバル・トランザクションの定義方法の概要を示しています。
リスト9-1 グローバル・トランザクションの定義 - 簡単な例
. . .
if (tpbegin(timeout,flags) == -1)
 error routine
program statements
. . .
if (tpcommit(flags) == -1)
 error routine
 
リスト9-2は、トランザクションの定義方法をより詳細に示しています。このサンプル・コードは、Oracle Tuxedoシステムで提供される銀行業務のサンプル・アプリケーション bankappaudit.cクライアント・プログラムから引用したものです。
リスト9-2 グローバル・トランザクションの定義 - 詳細な例
#include <stdio.h> /* UNIX */
#include <string.h> /* UNIX */
#include <atmi.h> /* BEA Tuxedo System */
#include <Uunix.h> /* BEA Tuxedo System */
#include <userlog.h> /* BEA Tuxedo System */
#include "bank.h" /* BANKING #defines */
#include "aud.h" /* BANKING view defines */

#define INVI 0 /* account inquiry */
#define ACCT 1 /* account inquiry */
#define TELL 2 /* teller inquiry */

static int sum_bal _((char *, char *));
static long sitelist[NSITE] = SITEREP; /* list of machines to audit */
static char pgmname[STATLEN]; /* program name = argv[0] */
static char result_str[STATLEN]; /* string to hold results of query */

main(argc, argv)
int argc;
char *argv[];
{
        int aud_type=INVI; /* audit type -- invalid unless specified */
        int clarg; /* command line arg index from optind */
        int c; /* Option character */
        int cflgs=0; /* Commit flags, currently unused */
        int aflgs=0; /* Abort flags, currently unused */
        int nbl=0; /* count of branch list entries */
        char svc_name[NAMELEN]; /* service name */
        char hdr_type[NAMELEN]; /* heading to appear on output */
        int retc; /* return value of sum_bal() */
        struct aud *audv; /* pointer to audit buf struct */
        int audrl=0; /* audit return length */
        long q_branchid; /* branch_id to query */

. . . /* Get Command Line Options and Set Variables */

/* Join application */

if (tpinit((TPINIT *) NULL) == -1) {
        (void)userlog("%s: failed to join application\n", pgmname);
        exit(1);
}

/* Start global transaction */

if (tpbegin(30, 0) == -1) {
        (void)userlog("%s: failed to begin transaction\n", pgmname);
        (void)tpterm();
        exit(1);
}

if (nbl == 0) { /* no branch id specified so do a global sum */
retc = sum_bal(svc_name, hdr_type); /* sum_bal routine not shown */

} else {

        /* Create buffer and set data pointer */
        
        if ((audv = (struct aud *)tpalloc("VIEW", "aud", sizeof(struct aud)))
                == (struct aud *)NULL) {
                (void)userlog("audit: unable to allocate space for VIEW\n");
                exit(1);
        }

        /* Prepare aud structure */

        audv->b_id = q_branchid;
        audv->balance = 0.0;
        audv->ermsg[0] = '\0';

        /* Do tpcall */

        if (tpcall(svc_name,(char *)audv,sizeof(struct aud),
           (char **)audv,(long *)audrl,0) == -1){
                    (void)fprintf (stderr,"%s service failed\n%s: %s\n",
                    svc_name, svc_name, audv->ermsg);
                    retc = -1;

        }else {

                 (void)sprintf(result_str,"Branch %ld %s balance is $%.2f\n",
                  audv->b_id, hdr_type, audv->balance);
        }
                 tpfree((char *)audv);
}

/* Commit global transaction */

if (retc < 0) /* sum_bal failed so abort */
       (void) tpabort(aflgs);
else {
         if (tpcommit(cflgs) == -1) {
                 (void)userlog("%s: failed to commit transaction\n", pgmname);
                 (void)tpterm();
                 exit(1);
        }
        /*print out results only when transaction has committed successfully*/
        (void)printf("%s",result_str);
}

/* Leave application */

if (tpterm() == -1) {
        (void)userlog("%s: failed to leave application\n", pgmname);
        exit(1);
}
 
トランザクションがタイムアウトになった場合、tpcommit()を呼び出すとトランザクションが中断します。その結果、tpcommit()が失敗し、tperrno(5)TPEABORTが設定されます。
リスト9-3は、トランザクションのタイムアウトをテストする方法を示しています。timeoutの値が30秒に設定されていることに注目してください。
リスト9-3 トランザクションのタイムアウトの確認
if (tpbegin(30, 0) == -1) {
  (void)userlog("%s: failed to begin transaction\n", argv[0]);
  tpterm();
  exit(1);
}
. . .
communication calls
. . .
if (tperrno == TPETIME){
    if (tpabort(0) == -1) {
      check for errors;
}
else if (tpcommit(0) == -1){
      check for errors;
}
. . .
 
注意:
トランザクション・モードのプロセスで、flags引数にTPNOTRANを設定して通信呼出しを行うと、呼び出されたサービスは現在のトランザクションに参加できません。サービス・リクエストの成功や失敗は、トランザクションの結果に影響しません。トランザクションは、サービスがトランザクションに参加しているかどうかには関係なく、そのサービスから応答が返されるのを待つ間にタイムアウトになる場合もあります。TPNOTRANフラグの影響については、11-1ページの「エラーの管理」を参照してください。
トランザクションの中断と再開
場合によっては、実行中のトランザクションから一時的にプロセスを削除し、tpbegin()またはtpresume()を呼び出して、そのプロセスで別のトランザクションを開始した方がよい場合もあります。たとえば、サーバーがデータベースの中央イベント・ログにリクエストを記録する場合、トランザクションが中断してもログ処理をロールバックしたくない場合などです。
Oracle Tuxedoシステムでは、このような場合にクライアントまたはサーバーでトランザクションを中断して再開するtpsuspend(3c)tpresume(3c)という2つの関数が提供されています。この2つの関数を使用すると、次の処理を行うことができます。
1.
tpsuspend()を呼び出して、現在のトランザクションを一時的に中断します。
2.
別のトランザクションを開始します。(前述の例では、サーバーのイベント・ログへのエントリの書込みが開始されます。)
3.
ステップ2で開始されたトランザクションをコミットします。
4.
tpresume()を呼び出して、元のトランザクションを再開します。
トランザクションの中断
tpsuspend(3c)関数を使用すると、現在のトランザクションを中断できます。tpsuspend()関数の呼出しには、次のシグネチャを使用します。
int
tpsuspend(TPTRANID *t_id,long flags)
表9-2は、tpsuspend()関数の引数を示しています。
 
表9-2 tpsuspend()関数の引数
フィールド
説明
*t_id
トランザクション識別子を指すポインタ。
flags
現在使用されていません。将来使用するために予約されたフィールド。
未処理の非同期イベントを持つトランザクションを中断することはできません。トランザクションが中断すると、トランザクションがコミットまたは中断されるまで、あるいはタイムアウトになるまで、中断前に行った変更はすべて保留状態のまま維持されます。
トランザクションの再開
現在のトランザクションを再開するには、次のシグネチャを使用してtpresume(3c)関数を呼び出します。
int
tpresume(TPTRANID *t_id,long flags)
表9-3は、tpresume()関数の引数を示しています。
 
表9-3 tpresume()関数の引数
フィールド
説明
*t_id
トランザクション識別子を指すポインタ。
flags
現在使用されていません。将来使用するために予約されたフィールド。
トランザクションを中断したプロセス以外のプロセスからトランザクションを再開できますが、制限があります。この制限については、『Oracle Tuxedo ATMI C言語関数リファレンス』「tpsuspend(3c)」および「tpresume(3c)」を参照してください。
例: トランザクションの中断と再開
リスト9-4は、あるトランザクションを中断し、別のトランザクションを開始してコミットし、最初のトランザクションを再開する方法を示しています。コードを簡単にするために、エラー・チェック・コードは省略してあります。
リスト9-4 トランザクションの中断と再開
DEBIT(SVCINFO *s)
{
       TPTRANID t;
       tpsuspend(&t,TPNOFLAGS); /* suspend invoking transaction*/

       tpbegin(30,TPNOFLAGS); /* begin separate transaction */
       Perform work in the separate transaction.
       tpcommit(TPNOFLAGS); /* commit separate transaction */

       tpresume(&t,TPNOFLAGS); /* resume invoking transaction*/

       .
       .
       .
       tpreturn(. . . );
}
 
トランザクションの終了
グローバル・トランザクションを終了するには、tpcommit(3c)を呼び出して現在のトランザクションをコミットするか、またはtpabort(3c)を呼び出して処理を中断して、すべての操作をロールバックします。
注意:
tpcall()tpacall()またはtpconnect()を呼び出すときにflags引数に明示的にTPNOTRANが設定されている場合、呼び出されたサービスによって実行される操作は、現在のトランザクションに含まれません。つまり、このようなサービスによって実行される操作は、tpabort()関数を呼び出したときにロールバックされません。
現在のトランザクションのコミット
tpcommit(3c)関数は、現在のトランザクションをコミットします。tpcommit()関数から正常に制御が戻ると、現在のトランザクションの結果としてリソースに加えられた変更は永続的なものとなります。
tpcommit()関数の呼出しには、次のシグネチャを使用します。
int
tpcommit(long flags)
flags引数は現在使用されていませんが、今後のリリースとの互換性を保つためにゼロに設定してください。
トランザクションをコミットするための条件
tpcommit()を正常に実行するには、次の条件を満たしていることが必要です。
呼出し側プロセスは、tpbegin()を呼び出してトランザクションを開始したプロセスと同じでなければなりません。
TPNOTRANフラグを設定しないで呼び出した場合、呼出し側プロセスに未処理のトランザクション応答が存在することはできません。
トランザクションの状態が「ロールバックのみ」ではなく、またタイムアウトになっていないことが必要です。
最初の条件を満たしていない場合、呼出しは失敗し、プロトコル・エラーを示すTPEPROTOtperrno(5)に設定されます。2番目または3番目の条件を満たしていない場合、呼出しは失敗し、トランザクションがロールバックされたことを示すTPEABORTtperrno()に設定されます。トランザクションに未処理の応答があるときにtpcommit()がイニシエータによって呼び出されると、トランザクションは中断され、トランザクションに関連する応答記述子が無効になります。参加リソースがtpcommit()またはtpabort()を呼び出しても、トランザクションには影響しません。
サービス呼出しでTPFAILが戻されるか、またはサービス・エラーが発生すると、トランザクションはロールバックのみの状態になります。「ロールバックのみ」のトランザクションに対してtpcommit()が呼び出されると、この関数はトランザクションを取り消し、-1を返してtperrno(5)TPEABORTを設定します。すでにタイムアウトになっているトランザクションに対してtpcommit()を呼び出した場合も同じ結果になり、tpcommit()は -1を返してtperrno()TPEABORTが設定されます。トランザクション・エラーの詳細は、11-1ページの「エラーの管理」を参照してください。
2フェーズ・コミット・プロトコル
tpcommit()関数が呼び出されると、2フェーズ・コミット・プロトコルによる通信が開始されます。このプロトコルは、その名前が示すように、次の2段階の処理に分かれています。
1.
参加する各リソース・マネージャがコミットの準備ができたことを示します。
2.
トランザクションのイニシエータが、参加する各リソース・マネージャにコミット許可を与えます。
トランザクションのイニシエータがtpcommit()関数を呼び出すと、コミット・シーケンスが開始されます。指定されたコーディネータ・グループ内のOracle Tuxedo TMSサーバー・プロセスは、コミット・プロトコルの最初のフェーズを実行する各参加リソース・グループのTMSと通信を行います。次に、各グループのTMSは、そのグループのリソース・マネージャ(RM)に、トランザクション・マネージャとRM間の通信用に定義されているXAプロトコルを使用してコミットするように指示します。RMは、安定記憶域にコミット・シーケンスの前後のトランザクションの状態を書き込み、TMSに成功か失敗かを通知します。その後、TMSはトランザクション・コーディネータのTMSにレスポンスを渡します。
トランザクション・コーディネータのTMSは、すべてのグループから成功の通知を受け取ると、トランザクションのコミット中であることをログに記録し、第2フェーズのコミット通知をすべての参加リソース・グループに送信します。その後、各グループのRMはトランザクションの更新を完了します。
トランザクション・コーディネータのTMSが、グループから第1フェーズのコミットの失敗の通知を受けた場合、またはグループからの応答の受信に失敗した場合、各RMにロールバック通知を送信し、RMはすべてのトランザクション更新を以前の状態に戻します。これにより、tpcommit()は失敗し、tperrno(5)TPEABORTが設定されます。
コミットの成功条件の選択
1つのトランザクションに複数のグループが関係している場合、tpcommit()が正常に制御を戻すための条件として、次のいずれかを指定できます。
すべての参加リソースからコミットの準備が完了したことが通知された場合。つまり、すべての参加リソースが2フェーズ・コミットの第1フェーズが完了したことを報告し、トランザクション・コーディネータのTMSが安定記憶域にコミットの決定を書き込んだ場合です。
すべての参加リソースで2フェーズ・コミットの第2フェーズが完了した場合。
この2つの条件のいずれかを指定するには、構成ファイルのRESOURCESセクションのCMTRETパラメータに、次のいずれかの値を設定します。
LOGGED - 第1フェーズの完了が必須であることを示します。
COMPLETE - 第2フェーズの完了が必須であることを示します。
デフォルトでは、CMTRETCOMPLETEに設定されます。
後で構成ファイルの設定値を変更する場合は、flags引数にTP_CMT_LOGGEDまたはTP_CMT_COMPLETEを設定して、tpscmt()を呼び出します。
コミット条件での妥協点
ほとんどの場合、グローバル・トランザクションのすべての参加リソースが第1フェーズの正常終了を記録した場合、第2フェーズも正常終了します。CMTRETLOGGEDを設定すると、tpcommit()の呼出しから制御が多少早く戻るようになります。ただし、参加リソースが、コミットの決定と矛盾する方法で、トランザクションの担当部分をヒューリスティックに終了する危険性があります。
このようなリスクを負うべきかどうかの選択は、アプリケーションの性質に左右されます。たとえば、財務アプリケーションなど正確さが要求されるアプリケーションでは、すべての参加リソースが2フェーズ・コミットを完了するまでは、制御を戻さないようにします。時間的な条件を重視するアプリケーションでは、正確さを犠牲にしても実行速度を上げます。
現在のトランザクションの中断
tpabort(3c)関数を使用すると、異常な状態を通知して、明示的にトランザクションを中断できます。この関数は、トランザクションの応答に未処理のものがあると、その呼出し記述子を無効にします。その場合、トランザクションで行われた変更はリソースには適用されません。tpabort()関数の呼出しには、次のシグネチャを使用します。
int
tpabort(long flags)
flags引数は現在使用されていませんが、今後のリリースとの互換性を保つためにゼロに設定してください。
例: 会話モードによるトランザクションのコミット
図9-1は、グローバル・トランザクションを行う階層構造の会話型接続を示しています。
図9-1 トランザクション・モードにおける接続階層構造
 
接続階層構造は、次の処理が行われることで構築されます。
1.
クライアント(プロセスA)は、tpbegin()tpconnect()を呼び出して、トランザクション・モードで接続を開始します。
2.
クライアントは、実行する従属サービスを呼び出します。
3.
各従属サービスは、処理が完了すると、処理が成功したか失敗したか(TPEV_SVCSUCCまたはTPEV_SVCFAIL)を示す応答を階層構造を通じてトランザクションを開始したプロセスに送信します。この例では、トランザクションを開始したプロセスはクライアント(プロセスA)です。従属サービスは、応答の送信が終了すると、つまり未処理の応答がなくなると、tpreturn()を呼び出します。
4.
クライアント(プロセスA)は、すべての従属サービスが正常終了したかどうかを確認します。
すべての従属サービスが正常終了した場合、クライアントはtpcommit()を呼び出して、それらのサービスが実行した変更をコミットし、トランザクションを終了します。
正常終了していない従属サービスがある場合、tpcommit()は成功しないので、クライアントはtpabort()を呼び出します。
例: 参加リソースのエラーの確認
リスト9-5では、クライアントはREPORTサービスへの同期呼出し(18行目)を行います。次に、通信呼出しで戻される可能性があるエラーを調べて(19 - 34行目)、参加リソースの失敗を確認します。
リスト9-5 参加リソースの成功/失敗の確認
001 #include <stdio.h>
002 #include "atmi.h"
003
004 main()
005 {
006 char *sbuf, *rbuf;
007 long slen, rlen;
008 if (tpinit((TPINIT *) NULL) == -1)
009 error message, exit program;
010 if (tpbegin(30, 0) == -1)
011 error message, tpterm, exit program;
012 if ((sbuf=tpalloc("STRING", NULL, 100)) == NULL)
013 error message, tpabort, tpterm, exit program;
014 if ((rbuf=tpalloc("STRING", NULL, 2000)) == NULL)
015 error message, tpfree sbuf, tpabort, tpterm, exit program;
016 (void)strcpy(sbuf, "REPORT=accrcv DBNAME=accounts");
017 slen=strlen(sbuf);
018 if (tpcall("REPORT", sbuf, slen, &rbuf, &rlen, 0) == -1) {
019 switch(tperrno) {
020 case TPESVCERR:
021 fprintf(stderr,
022 "REPORT service's tpreturn encountered problems\n");
023 break;
024 case TPESVCFAIL:
025 fprintf(stderr,
026 "REPORT service TPFAILED with return code of %d\n", tpurcode);
027 break;
028 case TPEOTYPE:
029 fprintf(stderr,
030 "REPORT service's reply is not of any known data type\n");
031 break;
032 default:
033 fprintf(stderr,
034 "REPORT service failed with error %d\n", tperrno);
035 break;
036 }
037 if (tpabort(0) == -1){
038 check for errors;
039 }
040 }
041 else
042 if (tpcommit(0) == -1)
043 fprintf(stderr, "Transaction failed at commit time\n");
044 tpfree(rbuf);
045 tpfree(sbuf);
046 tpterm();
047 exit(0);
048 }
 
グローバル・トランザクションの暗黙的な定義
アプリケーションでは、次のいずれかの方法でグローバル・トランザクションを開始できます。
ATMI関数を明示的に呼び出します。9-2ページの「トランザクションの開始」を参照してください。
サービス・ルーチン内から暗黙的に開始します。
この項では、2番目の方法について説明します。
サービス・ルーチンのトランザクションの暗黙的な定義
構成ファイルのシステム・パラメータAUTOTRANを設定すると、サービス・ルーチンがトランザクション・モードになります。AUTOTRANYを設定すると、別のプロセスからリクエストを受信したときに、サービス・サブルーチン内でトランザクションが自動的に開始されます。
暗黙的にトランザクションを定義する場合は、以下の規則に従います。
呼出し側プロセスがトランザクション・モードになっていない場合に、システム・パラメータAUTOTRANがトランザクションを開始するように設定されていると、プロセスが別のプロセスのサービスをリクエストしたときにトランザクションが開始されます。
すでにトランザクション・モードになっているプロセスが別のプロセスのサービスをリクエストした場合、システムは呼出し側のflagsパラメータがTPNOTRANに設定されているかどうかをまず確認します。
flags引数にTPNOTRANが設定されていない場合、呼び出されたプロセスは伝達の規則によってトランザクション・モードになります。システムによってAUTOTRANパラメータは確認されません。
flags引数にTPNOTRANが設定されている場合、呼び出されたプロセスによって実行されるサービスは、現在のトランザクションに含まれません。つまり、「伝達の規則」は適用されません。システムによってAUTOTRANパラメータが確認されます。
AUTOTRANNが設定されている場合(つまりAUTOTRANが有効になっていない場合)、呼び出されたプロセスはトランザクション・モードになりません。
AUTOTRANYが設定されている場合、呼び出されたプロセスはトランザクション・モードになります。ただし、新しいトランザクションとして処理されます。
注意:
サービスは自動的にトランザクション・モードにできるので、TPNOTRANフラグが設定されたサービスから、AUTOTRANパラメータが設定されたサービスを呼び出すことができます。そのようなサービスが別のサービスをリクエストした場合、サービスのデータ構造体のflagsメンバーは問合せを実行したときにTPTRANを返します。たとえば、flagsTPNOTRAN | TPNOREPLYを設定して呼出しを行い、サービスが呼び出されたときに(そのサービスによって)トランザクションが自動的に開始された場合、データ構造体のflagsメンバーは、TPTRAN | TPNOREPLYに設定されます。
XA準拠のサーバー・グループに対するグローバル・トランザクションの定義
アプリケーション・プログラマがXA準拠のサーバー・グループのサービスをコーディングする場合、グループのリソース・マネージャを介して操作を行うようにするのが一般的です。通常、サービスは1つのトランザクション内ですべての操作を行います。それに対して、flagsTPNOTRANを設定してサービスを呼び出すと、データベース操作の実行時に予期しない結果を受け取る場合があります。
予測不能な動作を防ぐには、XA準拠のリソース・マネージャに関連付けられているグループのサービスが、常にトランザクション・モードで呼び出されるようにアプリケーションを設計します。または、構成ファイルのAUTOTRANYを設定します。また、サービス・コードの早い段階で、トランザクション・レベルを確認します。
トランザクションが開始されたことの確認
トランザクション・モードのプロセスが別のプロセスのサービスをリクエストした場合、後者のプロセスは不参加の指示が特にないかぎり、そのトランザクションの参加リソースになります。
特定のエラー条件を回避したり正しく解釈するには、プロセスがトランザクション・モードかどうかを確認することが大切です。たとえば、すでにトランザクション・モードになっているプロセスがtpbegin()を呼び出すとエラーになります。そのようなプロセスがtpbegin()を呼び出すと、呼出しは失敗し、tperrno(5)TPEPROTOが設定されて、呼出し側がすでにトランザクションに参加しているにもかかわらず呼び出されたことが示されます。トランザクションに影響はありません。
サービス・サブルーチンがトランザクション・モードかどうかを確認した後で、tpbegin()を呼び出すようにアプリケーションを設計できます。次のいずれかの方法で、トランザクション・レベルを確認できます。
サービス・サブルーチンに渡されるサービスのデータ構造体のflagsフィールドに対して問合せを実行します。TPTRANに設定されていると、サービスはトランザクション・モードになっています。
tpgetlev(3c)関数を呼び出します。
tpgetlev()関数の呼出しには、次のシグネチャを使用します。
int
tpgetlev() /* Get current transaction level */
tpgetlev()関数に引数は必要ありません。この関数は、呼出し側がトランザクション・モードになっていない場合は0を返し、トランザクション・モードになっている場合は1を返します。
リスト9-6は、OPEN_ACCTサービスの1つであり、tpgetlev()関数(12行目)を使用してトランザクション・レベルを確認する方法を示しています。プロセスがトランザクション・モードになっていない場合、アプリケーションでトランザクションを開始します(14行目)。tpbegin()が失敗した場合、メッセージがステータス行に戻され(16行目)、tpreturn()rcode引数にグローバル変数tpurcode(5)で取得できるコードが設定されます(1行目と17行目)。
リスト9-6 トランザクション・レベルの確認
001 #define BEGFAIL 3 /* tpurcode setting for return if tpbegin fails */

002 void
003 OPEN_ACCT(transb)

004 TPSVCINFO *transb;

005 {
... other declarations ...
006 FBFR *transf; /* fielded buffer of decoded message */
007 int dotran; /* checks whether service tpbegin/tpcommit/tpaborts */

008 /* set pointer to TPSVCINFO data buffer */

009 transf = (FBFR *)transb->data;

010 /* Test if transaction exists; initiate if no, check if yes */

011 dotran = 0;
012 if (tpgetlev() == 0) {
013 dotran = 1;
014 if (tpbegin(30, 0) == -1) {
015 Fchg(transf, STATLIN, 0,
016 "Attempt to tpbegin within service routine failed\n");
017 tpreturn(TPFAIL, BEGFAIL, transb->data, 0, 0);
018 }
019 }
. . .
 
AUTOTRANYが設定されている場合、トランザクション関数のtpbegin()およびtpcommit()またはtpabort()を明示的に呼び出す必要はありません。その結果、トランザクション・レベルを確認するオーバーヘッドを減らすことができます。また、TRANTIMEパラメータを設定して、タイムアウト間隔を指定することもできます。タイムアウト間隔は、サービスに対するトランザクションが開始されてからの経過時間です。また、トランザクションが完了しなかった場合は、トランザクションがロールバックされるまでの時間です。
たとえば、前述のコードのOPEN_ACCTサービスを変更するとします。現在のコードでは、OPEN_ACCTにトランザクションが明示的に定義され、そのトランザクションの有無を確認しています(7行目、10 - 19行目)。これらの処理のオーバーヘッドを減らすには、そのコードを削除します。その場合、OPEN_ACCTは常にトランザクション・モードで呼び出す必要があります。この要件を指定するには、構成ファイルのAUTOTRANTRANTIMEシステム・パラメータを有効にします。
関連項目
『Oracle Tuxedoアプリケーションの設定』「グローバル・トランザクションの暗黙的な定義」AUTOTRAN構成パラメータの説明。
『Oracle Tuxedoアプリケーションの設定』のTRANTIME構成パラメータ
『Oracle Tuxedoアプリケーションの設定』「TuxedoをOracle Real Application Clusters (RAC)とともに使用する方法」

Copyright ©1994, 2017,Oracle and/or its affiliates. All rights reserved