C 言語を使用した Oracle Tuxedo アプリケーションのプログラミング

     前  次    新しいウィンドウで目次を開く     
ここから内容の開始

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

ここでは、以下の内容について説明します。

 


グローバル トランザクションとは

グローバル トランザクションとは、複数のリソース マネージャを使用し、複数のサーバ上で行われる複数の操作を 1 つの論理単位として処理できるようにするメカニズムです。

プロセスがトランザクション モードになると、サーバに要求されたサービスが現在のトランザクションに代わって処理されます。呼び出されてトランザクションに参加したサービスは、「トランザクションの参加リソース」と呼ばれます。参加リソースから返される値によって、トランザクションの結果が変わる場合があります。

グローバル トランザクションは複数のローカル トランザクションから構成され、各トランザクションは同じリソース マネージャにアクセスします。リソース マネージャは、同時実行制御とデータ更新の原子性を実現します。ローカル トランザクションでは、アクセスが正常に終了するか、または全体が失敗します。つまり、一部だけが成功することはありません。

1 つのトランザクションに参加可能なサーバ グループは最大 16 個です。

Oracle Tuxedo システムでは、グローバル トランザクションが参加しているリソース マネージャと共に管理され、原子性、一貫性、独立性、および持続性という特徴を持つ特定シーケンスの操作として処理されます。つまり、グローバル トランザクションは、以下のような特徴を持つ論理的な作業単位と言えます。

Oracle Tuxedo システムでは、個々のグローバル トランザクションの状態がトラッキングされ、そのトランザクションをコミットするかロールバックするかが決定されます。

注意 : トランザクションで、flags 引数に明示的に TPNOTRAN を設定して tpcall()tpacall() または tpconnect() を呼び出した場合、呼び出されたサービスによって実行される操作はトランザクションに含まれません。その場合、呼び出し側プロセスは、呼び出されたサービスを現在のトランザクションの参加リソースとは見なしません。その結果、呼び出されたプロセスによって実行されるサービスは、現在のトランザクションの結果の影響を受けません。XA 準拠のサーバ グループのサービスに対する呼び出しに TPNOTRAN が設定されている場合、その呼び出しはトランザクション モード外、または別のトランザクションで実行されます。どちらで実行されるかは、サービスの設定方法とコーディング方法によって決まります。詳細については、「グローバル トランザクションの暗黙的な定義」を参照してください。

 


トランザクションの開始

グローバル トランザクションを開始するには、次の文法を使用して tpbegin(3c) 関数を呼び出します。

int
tpbegin(unsigned long timeout, long flags)

次の表は、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 グローバル トランザクションの定義 - 簡単な例
. . .
if (tpbegin(timeout,flags) == -1)
error routine
program statements
. . .
if (tpcommit(flags) == -1)
error routine

次のサンプル コードは、トランザクションの定義方法をより詳細に示しています。このコードは、Oracle Tuxedo システムで提供される銀行業務のサンプル アプリケーション bankappaudit.c クライアント プログラムから引用したものです。

コード リスト 9-2 グローバル トランザクションの定義 - 詳細な例
#include <stdio.h>         /* UNIX */
#include <string.h> /* UNIX */
#include <atmi.h> /* BEA Tuxedo システム */
#include <Uunix.h> /* BEA Tuxedo システム */
#include <userlog.h> /* BEA Tuxedo システム */
#include "bank.h" /* 銀行業務アプリケーションのマクロ定義 */
#include "aud.h" /* 銀行業務アプリケーションの VIEW 定義 */

#define INVI 0 /* 残高照会 */
#define ACCT 1 /* 残高照会 */
#define TELL 2 /* 窓口の残高照会 */

static int sum_bal _((char *, char *));
static long sitelist[NSITE] = SITEREP; /* 監査用マシンのリスト */
static char pgmname[STATLEN]; /* プログラム名 = argv[0] */
static char result_str[STATLEN]; /* 照会の結果を入れる配列 */

main(argc, argv)
int argc;
char *argv[];
{
int aud_type=INVI; /* 監査の種類 - 指定されていない場合は無効 */
int clarg; /* optind からのコマンドライン引数のインデックス */
int c; /* オプション文字 */
int cflgs=0; /* コミットのフラグ。現在使用されていません。 */
int aflgs=0; /* アボートのフラグ。現在使用されていません。 */
int nbl=0; /* 支店リストのエントリ数 */
char svc_name[NAMELEN]; /* サービス名 */
char hdr_type[NAMELEN]; /* 出力の表題 */
int retc; /* sum_bal() の戻り値 */
struct aud *audv; /* audit バッファ構造体へのポインタ */
int audrl=0; /* audit の戻り値の長さ */
long q_branchid; /* 照会する支店番号 */

. . . /* コマンドライン オプションの取得と変数を設定 */

/* アプリケーションに参加 */

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

/* グローバル トランザクションを開始 */

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

if (nbl == 0) { /* 支店番号が指定されていないので、総勘定を計算 */
retc = sum_bal(svc_name, hdr_type); /* sum_bal ルーチンは省略 */

} else {

/* バッファを作成し、データ ポインタを設定 */

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);
}

/* aud 構造体を用意 */

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

/* 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);
}

/* グローバル トランザクションをコミット */

if (retc < 0) /* sum_bal の実行が失敗したので処理をアボート */
(void) tpabort(aflgs);
else {
if (tpcommit(cflgs) == -1) {
(void)userlog("%s: failed to commit transaction\n", pgmname);
(void)tpterm();
exit(1);
}
/* トランザクションのコミットが成功した場合にのみ、結果を出力 */
(void)printf("%s",result_str);
}

/* アプリケーションから分離 */

if (tpterm() == -1) {
(void)userlog("%s: failed to leave application\n", pgmname);
exit(1);
}

トランザクションがタイムアウトになった場合、tpcommit() を呼び出すと、トランザクションがアボートします。その結果、tpcommit() が失敗し、tperrno(5)TPEABORT が設定されます。

次のサンプル コードは、トランザクションのタイムアウトを確認する方法を示しています。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 フラグの影響については、「エラーの管理」を参照してください。

 


トランザクションの中断と再開

場合によっては、実行中のトランザクションから一時的にプロセスを削除し、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)

次の表は、tpsuspend() 関数の引数を示しています。

表 9-2 tpsuspend( ) 関数の引数
フィールド
説明
*t_id
トランザクション識別子を指すポインタ。
flags
現在使用されていません。将来使用するために予約されたフィールド。

未処理の非同期イベントを持つトランザクションを中断することはできません。トランザクションが中断すると、トランザクションがコミットまたはアボートされるまで、あるいはタイムアウトになるまで、中断前に行った変更はすべて保留状態のまま維持されます。

トランザクションの再開

現在のトランザクションを再開するには、次の文法を使用して tpresume(3c) 関数を呼び出します。

int
tpresume(TPTRANID *t_id,long flags)

次の表は、tpresume() 関数の引数を示しています。

表 9-3 tpresume() 関数の引数
フィールド
説明
*t_id
トランザクション識別子を指すポインタ。
flags
現在使用されていません。将来使用するために予約されたフィールド。

トランザクションを中断したプロセス以外のプロセスからトランザクションを再開できますが、制限があります。この制限については、『Oracle Tuxedo C リファレンス』の「tpsuspend(3c)」と「tpresume(3c)」を参照してください。

例: トランザクションの中断と再開

次のサンプル コードは、あるトランザクションを中断し、別のトランザクションを開始してコミットし、最初のトランザシクョンを再開する方法を示しています。コードを簡単にするために、エラー チェックは省略してあります。

コード リスト 9-4 トランザクションの中断と再開
DEBIT(SVCINFO *s)
{
TPTRANID t;
tpsuspend(&t,TPNOFLAGS); /* 実行中のトランザクションを中断 */

tpbegin(30,TPNOFLAGS); /* 別のトランザクションを開始 */
別のトランザクションで処理を実行
tpcommit(TPNOFLAGS); /* トランザクションをコミット */

tpresume(&t,TPNOFLAGS); /* 最初のトランザクションを再開 */

.
.
.
tpreturn(. . . );
}

 


トランザクションの終了

グローバル トランザクションを終了するには、tpcommit(3c) を呼び出して現在のトランザクションをコミットするか、または tpabort(3c) を呼び出して処理をアボートして、すべての操作をロールバックします。

注意 : tpcall()tpacall()、または tpconnect() を呼び出すときに flags 引数に明示的に TPNOTRAN が設定されている場合、呼び出されたサービスによって実行される操作は、トランザクションに含まれません。つまり、このようなサービスによって実行される操作は、tpabort() 関数を呼び出したときにロールバックされません。

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

tpcommit(3c) 関数は、現在のトランザクションをコミットします。tpcommit() 関数から正常に制御が戻ると、現在のトランザクションの結果としてリソースに加えられた変更は永続的なものとなります。

tpcommit() 関数の呼び出しには、次の文法を使用します。

int
tpcommit(long flags)

flags 引数は現在使用されていませんが、今後のリリースとの互換性を保つためにゼロに設定してください。

トランザクションをコミットするための条件

tpcommit() を正常に実行するには、次の条件を満たしていることが必要です。

最初の条件を満たしていない場合、呼び出しは失敗し、プロトコル エラーを示す TPEPROTOtperrno(5) に設定されます。2 番目または 3 番目の条件を満たしていない場合、呼び出しは失敗し、トランザクションがロールバックされたことを示す TPEABORTtperrno() に設定されます。トランザクションに未処理の応答があるときに tpcommit() が開始プロセスによって呼び出されると、トランザクションはアボートされ、トランザクションに関連する応答記述子が無効になります。参加リソースが tpcommit() または tpabort() を呼び出しても、トランザクションには影響しません。

サービス呼び出しで TPFAIL が返されるか、またはサービス エラーが発生すると、トランザクションは「ロールバックのみ」の状態になります。「ロールバックのみ」のトランザクションに対して tpcommit() が呼び出されると、この関数はトランザクションを取り消し、-1 を返して tperrno(5)TPEABORT を設定します。既にタイムアウトになっているトランザクションに対して tpcommit() を呼び出した場合も同じ結果になり、tpcommit() は -1 を返して tperrno()TPEABORT が設定されます。トランザクション エラーの詳細については、「エラーの管理」を参照してください。

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 つの条件のいずれかを指定するには、コンフィグレーション ファイルの RESOURCES セクションの CMTRET パラメータに、次のいずれかの値を設定します。

デフォルトでは、CMTRETCOMPLETE に設定されます。

後でコンフィグレーション ファイルの設定値を変更する場合は、flags 引数に TP_CMT_LOGGED または TP_CMT_COMPLETE を設定して、tpscmt() を呼び出します。

コミット条件での妥協点

ほとんどの場合、グローバル トランザクションのすべての参加リソースが第 1 フェーズの正常終了を記録した場合、第 2 フェーズも正常終了します。CMTRETLOGGED を設定すると、tpcommit() の呼び出しから制御が多少早く戻るようになります。ただし、参加リソースが、コミットの決定と矛盾する方法で、トランザクションの担当部分をヒューリスティックに終了する危険性があります。

このようなリスクを負うべきかどうかの選択は、アプリケーションの性質に左右されます。たとえば、財務アプリケーションなど正確さが要求されるアプリケーションでは、すべての参加リソースが 2 フェーズ コミットを完了するまでは、制御を戻さないようにします。時間的な条件を重視するアプリケーションでは、正確さを犠牲にしても実行速度を上げます。

現在のトランザクションのアボート

tpabort(3c) 関数を使用すると、異常な状態を通知して、明示的にトランザクションをアボートできます。この関数は、トランザクションの応答に未処理のものがあると、その呼び出し記述子を無効にします。その場合、トランザクションで行われた変更はリソースには適用されません。tpabort() 関数の呼び出しには、次の文法を使用します。

int
tpabort(long flags)

flags 引数は現在使用されていませんが、今後のリリースとの互換性を保つためにゼロに設定してください。

例: 会話モードによるトランザクションのコミット

次の図は、グローバル トランザクションを行う階層構造の会話型接続を示しています。

図 9-1 トランザクション モードにおける接続階層構造

トランザクション モードにおける接続階層構造

接続階層構造は、次の処理が行われることで構築されます。

  1. クライアント (プロセス A) は、tpbegin()tpconnect() を呼び出して、トランザクション モードで接続を開始します。
  2. クライアントは、実行する従属サービスを呼び出します。
  3. 各従属サービスは、処理が完了すると、処理が成功したか失敗したか (TPEV_SVCSUCC または TPEV_SVCFAIL) を示す応答を階層構造を通じてトランザクションを開始したプロセスに送信します。この例では、トランザクションを開始したプロセスはクライアント (プロセス A) です。従属サービスは、応答の送信が終了すると、つまり未処理の応答がなくなると、tpreturn() を呼び出します。
  4. クライアント (プロセス A) は、すべての従属サービスが正常終了したかどうかを確認します。
    • すべての従属サービスが正常終了した場合、クライアントは tpcommit() を呼び出して、それらのサービスが実行した変更をコミットし、トランザクションを終了します。
    • 正常終了していない従属サービスがある場合、tpcommit() は成功しないので、クライアントは tpabort() を呼び出します。

例: 参加リソースのエラーの確認

次のサンプル コードでは、クライアントは 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 }

 


グローバル トランザクションの暗黙的な定義

アプリケーションでは、次のいずれかの方法でグローバル トランザクションを開始できます。

この節では、2 番目の方法について説明します。

サービス ルーチンのトランザクションの暗黙的な定義

コンフィグレーション ファイルのシステム パラメータ AUTOTRAN を設定すると、サービス ルーチンがトランザクション モードになります。AUTOTRANY を設定すると、別のプロセスから要求を受信したときに、サービス サブルーチン内でトランザクションが自動的に開始されます。

暗黙的にトランザクションを定義する場合は、以下の規則に従います。

注意 : サービスは自動的にトランザクション モードにできるので、TPNOTRAN フラグが設定されたサービスから、AUTOTRAN パラメータが設定されたサービスを呼び出すことができます。そのようなサービスが別のサービスを要求した場合、サービスのデータ構造体の flags メンバーはクエリを実行したときに TPTRAN を返します。たとえば、flagsTPNOTRAN | TPNOREPLY を設定して呼び出しを行い、サービスが呼び出されたときに (そのサービスによって) トランザクションが自動的に開始された場合、データ構造体の flags メンバーは、TPTRAN | TPNOREPLY に設定されます。

 


XA 準拠のサーバ グループに対するグローバル トランザクションの定義

アプリケーション プログラマが XA 準拠のサーバ グループのサービスをコーディングする場合、グループのリソース マネージャを介して操作を行うようにするのが一般的です。通常、サービスは 1 つのトランザクション内ですべての操作を行います。それに対して、flagsTPNOTRAN を設定してサービスを呼び出すと、データベース操作の実行時に予期しない結果を受け取る場合があります。

予測不能な動作を防ぐには、XA 準拠のリソース マネージャに対応付けられているグループのサービスが、常にトランザクション モードで呼び出されるようにアプリケーションを設計します。または、コンフィグレーション ファイルの AUTOTRANY を設定します。また、サービス コードの早い段階で、トランザクション レベルを確認します。

 


トランザクションが開始されたことの確認

トランザクション モードのプロセスが別のプロセスのサービスを要求した場合、後者のプロセスは不参加の指示が特にない限り、そのトランザクションの参加リソースになります。

特定のエラー条件を回避したり正しく解釈するには、プロセスがトランザクション モードかどうかを確認することが大切です。たとえば、既にトランザクション モードになっているプロセスが tpbegin() を呼び出すとエラーになります。そのようなプロセスが tpbegin() を呼び出すと、呼び出しは失敗し、tperrno(5)TPEPROTO が設定されて、呼び出し側が既にトランザクションに参加しているにもかかわらず呼び出されたことが示されます。トランザクションに影響はありません。

サービス サブルーチンがトランザクション モードかどうかを確認した後で、tpbegin() を呼び出すようにアプリケーションを設計できます。次のいずれかの方法で、トランザクション レベルを確認できます。

tpgetlev() 関数の呼び出しには、次の文法を使用します。

int
tpgetlev() /* 現在のトランザクション レベルを取得 */

tpgetlev() 関数に引数は必要ありません。この関数は、呼び出し側がトランザクション モードになっていない場合は 0 を返し、トランザクション モードになっている場合は 1 を返します。

次のサンプル コードは、OPEN_ACCT サービスの 1 つであり、tpgetlev() 関数 (12 行目) を使用してトランザクション レベルを確認する方法を示しています。プロセスがトランザクション モードになっていない場合、アプリケーションでトランザクションを開始します (14 行目)。tpbegin() が失敗した場合、メッセージがステータス行に返され (16 行目)、tpreturn()rcode 引数にグローバル変数 tpurcode(5) で取得できるコードが設定されます (1 行目と 17 行目)。

コード リスト 9-6 トランザクション レベルの確認
001 #define BEGFAIL    3    /* tpbegin が失敗した場合に返す tpurcode の値を設定 */

002 void
003 OPEN_ACCT(transb)

004 TPSVCINFO *transb;

005 {
... other declarations ...
006 FBFR *transf; /* デコードされたメッセージのフィールド化バッファ */
007 int dotran; /* tpbegin、tpcommit、tpabort のどれであるかを確認 */

008 /* tpsvcinfo データ バッファへのポインタを設定 */

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

010 /* トランザクションがあるかどうかを確認し、ない場合は開始、ある場合は確認 */

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 システム パラメータを有効にします。

関連項目


  ページの先頭       前  次