レプリケーション・メカニズムとしてのXLAの使用

この項では、TimesTenレプリケーションの代替としてXLAを使用する方法について説明します。

ここで説明するXLA関数については、XLAリファレンスを参照してください。

レプリケーション・メカニズムとしてのXLAについて

TimesTenレプリケーションはお客様のほとんどの要件に対応しています。しかしながら、XLA関数を使用してデータベース間で更新をレプリケートすることもできます。この方法で独自のレプリケーション・スキームをXLAの上に実装することはかなり困難ですが、TimesTenのレプリケーションがなんらかの理由で実行できない場合には検討できます。 『Oracle TimesTen In-Memory Databaseレプリケーション・ガイド』TimesTenレプリケーションの概要を参照してください。

ノート:

XLAを使用して、異なるプラットフォーム間の更新をレプリケートすることはできません。

XLAを使用してデータベース間で変更内容をレプリケートするには、まずttXlaPersistOpen関数を使用してXLAハンドルを初期化します(「XLAの初期化およびXLAハンドルの取得」を参照)。

データベースに対してXLAハンドルを初期化したので、以降の各項では、次に示す、処理の各部分を説明します。

  • データベース間での表の互換性の確認。

  • データベース間での更新のレプリケート。

  • タイムアウトおよびデッドロックのエラーの処理。

  • 更新競合の確認。

この説明では、送信側のデータベースをマスターと呼び、受信側のデータベースをサブスクライバと呼びます。

データベース間での表の互換性の確認

データベース間で更新レコードを送信する場合は、事前に、マスター・データベース内とサブスクライバ・データベース内の表に相互互換性があることを確認します。

これらの方法は、次の項で説明します。

表および列の記述の確認

ttXlaTableByNamettXlaGetTableInfoおよびttXlaGetColumnInfo関数を使用すると、レプリケートする各表のttXlaTblDesc_tおよびttXlaColDesc_t記述が返されます。

「ttXlaTblDesc_t」「ttXlaColDesc_t」「更新を監視する表の指定」および「列記述の取得」を参照してください。

次に、これらの記述をttXlaTableCheck関数に渡すことができます。出力パラメータcompatは、表に互換性があるかどうかを指定します。値が1の場合は互換性があり、値が0(ゼロ)の場合は互換性がないことを示します。次の例でこれを説明します。

SQLINTEGER compat;
ttXlaTblDesc_t table;
ttXlaColDesc_t columns[20];

rc = ttXlaTableCheck(xla_handle, &table, columns, &compat);
if (compat) {
    /* Go ahead and start replicating */
}
else {
    /* Not compatible or some other error occurred */
}

表および列のバージョンの確認

ttXlaVersionTableInfoおよびttXlaVersionColumnInfo関数を使用して、レコードの生成時に更新レコードの表構造の情報を取得します。

「ttXlaVersionTableInfo」および「ttXlaVersionColumnInfo」を参照してください。

次の例では、pCmdソースのpXlaRecord更新レコードに関係付けられている表にhXlaTargetターゲットとの互換性があることを確認します。

BOOL CUTLCheckXlaTable (SCOMMAND* pCmd,
                        ttXlaHandle_h hXlaTarget,
                        const ttXlaUpdateDesc_t* pXlaRecord)
{
  /* locals */
  ttXlaTblVerDesc_t tblVerDescSource;
  ttXlaColDesc_t colDescSource [255];
  SQLINTEGER iColsReturned = 0;
  SQLINTEGER iCompatible = 0;
  SQLRETURN rc;

  /* only certain update record types should be checked */
  if (pXlaRecord->type == INSERTTUP ||
      pXlaRecord->type == UPDATETUP ||
      pXlaRecord->type == DELETETUP)
  {
     /* Get source table description associated with this record */
     /* from the time it was generated. */
     rc = ttXlaVersionTableInfo (pCmd->pCtx->con->hXla,
             (ttXlaUpdateDesc_t*) pXlaRecord, &tblVerDescSource);

     if (rc == SQL_SUCCESS)
     {
         /* Get the source column descriptors for this table */
         /* at the time the record was generated. */
         rc = ttXlaVersionColumnInfo (pCmd->pCtx->con->hXla,
                 (ttXlaUpdateDesc_t*) pXlaRecord,
                 colDescSource, 255, &iColsReturned);

         if (rc == SQL_SUCCESS)
         {
             /* Check compatibility. */
             rc = ttXlaTableCheck (hXlaTarget,
                     &tblVerDescSource.tblDesc, colDescSource,
                     &iCompatible);
         }
     }
  }
}

データベース間での更新のレプリケート

レプリケートを開始する準備の完了後、ttXlaNextUpdateまたはttXlaNextUpdateWait関数を使用してマスター・データベースから更新レコードのバッチを取得し、ttXlaApplyを使用してサブスクライバ・データベースにレコードを書き込みます。

「ttXlaNextUpdate」「ttXlaNextUpdateWait」および「ttXlaApply」を参照してください。

この例を次に示します。

int j;
ttXlaHandle_h h;
SQLINTEGER records;
ttXlaUpdateDesc_t** arry;

  do {
    /* get up to 15 updates */
    rc = ttXlaNextUpdate(h,&arry,15,&records);
    if (rc != SQL_SUCCESS) {
      /* See XLA Error Handling */
    }
 
    /* print number of updates returned */
    printf("Records returned by ttXlaNextUpdate : %d\n",records);
 
    /* apply the received updates */  
    for (j=0;j < records;j++) {
      ttXlaUpdateDesc_t* p;
 
      p = arry[j];
      rc = ttXlaApply(h, p, 0);
      if (rc != SQL_SUCCESS){
      /* See XLA Error Handling and */
      /* Handling Timeout and Deadlock Errors below */
      }
    }
 
    /* print number of updates applied */
    printf("Records applied successfully : %d\n",records);
 
  } while (records != 0);

ヒント:

  • XLAレコードのバージョンに互換性があるデータベース間でXLA更新を送信できるようにするには、すべてのデータベースでttXlaGetVersion関数およびttXlaVersionCompare関数を使用します。

  • ネットワーク上、または同じメモリー領域を使用しないプロセス間の任意の場所でレプリケートするデータをパッケージ化する場合は、ttXlaUpdateDesc_tデータ構造体を完全に含める必要があります。その長さはttXlaUpdateDesc_t ->header.lengthで示されます。ここで、header要素はttXlaNodeHdr_t構造体であり、この構造体にはlength要素があります。「ttXlaUpdateDesc_t」および「ttXlaNodeHdr_t」も参照してください。

タイムアウトおよびデッドロックのエラーへの対処

ttXlaApplyからのリターン・コードは、更新が正常に行われたかどうかを示します。

「ttXlaApply」を参照してください。

リターン・コードがSQL_SUCCESSでない場合は、更新で一時的な問題(デッドロック、タイムアウトなど)または永続的な問題が発生した可能性があります。ttXlaErrorを使用して、tt_ErrDeadlockVictimtt_ErrTimeoutVictimなどのエラーを確認できます。一時的なエラーは、レプリケートしたトランザクションをロールバックし、再実行することでリカバリできます。その他のエラーは、重複キー違反、キーが見つからないなどの永続的エラーである可能性があります。このようなエラーは、トランザクションを再実行しても、繰り返し発生する可能性があります。

ttXlaApplyで、トランザクションのコミット・レコード(ttXlaUpdateDesc_t ->flags = TT_UPDCOMMIT)をサブスクライバ・データベースに適用する前にタイムアウトまたはデッドロック・エラーが返された場合は、次のいずれかの処理を実行できます。

  • ttXlaRollbackを使用してトランザクションをロール・バックします。

  • ttXlaCommitを使用して、レコード内の、サブスクライバ・データベースに適用済の変更をコミットします。

一時的なエラーからのリカバリを有効にするには、マスター・データベースのトランザクション境界を追跡し、サブスクライバに現在適用中のトランザクションに関連付けられているレコードをユーザー・バッファに保存することで、必要に応じてそれらのレコードを再適用できます。トランザクション境界は、ttXlaUpdateDesc_t構造体のflagsメンバーを調べるとわかります。次の例を想定してください。この条件がtrueの場合、レコードはコミットされています。

(pXlaRecords [iRecordIndex]->flags & TT_UPDCOMMIT)

トランザクションをロールバックする必要があるエラーが発生した場合は、ttXlaRollbackをコールして、サブスクライバ・データベースに適用済のレコードをロールバックします。その後、ttXlaApplyをコールして、バッファに保存されているすべてのロールバック・レコードを再適用します。

ノート:

トランザクション・レコードをユーザー・バッファに保存するかわりに、ttXlaGetLSNをコールしてトランザクション・ログ内の各コミット・レコードのトランザクション・ログ・レコード識別子を取得する方法があります(「ブックマークの位置の変更」を参照)。トランザクションをロールバックする必要があるエラーが発生した場合は、ttXlaSetLSNをコールして、ブックマークをトランザクション・ログ内のトランザクションの先頭に再設定し、レコードを再適用します。ただし、この方法は、ttXlaGetLSN関数関連の追加のオーバーヘッドが発生するため、効率が悪くなる場合があります。

更新競合の確認

アプリケーションで、マスター・データベースおよびサブスクライバ・データベースの両方に対して同時に更新を行うと、更新競合が発生する場合があります。

更新競合については、『Oracle TimesTen In-Memory Databaseレプリケーション・ガイド』レプリケーション競合の解消を参照してください。

XLAで更新競合を確認するには、ttXlaApplytestパラメータを設定して、UPDATETUP型の各レコード内のold row値(ttXlaUpdateDesc_t ->tuple1)を、サブスクライバ・データベース内の既存の行と比較します。更新記述のold row値がサブスクライバ・データベースの対応する行と一致しない場合は、原因はおそらく更新競合です。この場合、ttXlaApplyはサブスクライバへの更新を適用せず、sb_ErrXlaTupleMismatchエラーを返します。

TimesTen以外のデータベースに対する更新のレプリケート

TimesTen以外のデータベースへの変更をレプリケートする場合は、ttXlaGenerateSQL関数を使用して、レコード・データをTimesTen以外のサブスクライバで読取りが可能なSQL文に変換します。 更新および削除のレコードの場合、正しいSQLを生成するために、ttXlaGenerateSQLには、NULL値可能でない列に対する主キー索引または一意索引が必要です。

「ttXlaGenerateSQL」を参照してください。

ttXlaGenerateSQL関数は、パラメータとしてttXlaUpdateDesc_tレコードを受け入れ、それと同等のSQLをバッファに出力します。

ノート:

ttXlaGenerateSQLによって返されるSQLでは、TimesTen SQL構文を使用します。2つのシステム間でSQL構文に互換性がない場合、TimesTen以外のサブスクライバではSQL文が失敗することがあります。さらに、SQL文は、XLAハンドルに関連付けられている接続文字セットでエンコードされます。

この例では、レコード(record)を変換し、その結果のSQL出力を200文字のバッファ(buffer)に保存します。バッファの実際のサイズはactualLengthパラメータによって返されます。

ttXlaUpdateDesc_t record;
char buffer[200];
SQLINTEGER actualLength;

rc = ttXlaGenerateSQL(xla_handle, &record, buffer, 200, &actualLength);

if (rc != SQL_SUCCESS) {
    handleXLAerror (rc, xla_handle, err_buf, &native_error);
    if ( native_error == 8034 ) { // tt_ErrXlaNoSQL
      printf("Unable to translate to SQL\n");
    }
}