9.2 トランザクションの開始

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

int
tpbegin(unsigned long timeout, longflags)

次の表は、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>            /* ORACLE Tuxedo System */
#include <Uunix.h>           /* ORACLE Tuxedo System */
#include <userlog.h>         /* ORACLE 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が設定されます。

次のリストは、トランザクションのタイムアウトをテストする方法を示しています。timeoutの値が30秒に設定されていることに注目してください。

トランザクションのタイムアウトの確認のリスト

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フラグの影響については、「エラーの管理」を参照してください。