Easysoft SQLServerODBCドライバーとOracleTuxedoを使用してXAトランザクションのコンテキストでSQLServerにアクセスする方法。
はじめに
分散トランザクションが必要な理由
トランザクションは、すべてのアクションが実行されるか、実行されない単一の操作として実行される一連のアクションです。トランザクションは、変更を永続的にするコミットアクションで終了します。いずれかの変更をコミットできない場合、トランザクションはロールバックされ、すべての変更が元に戻されます。
分散トランザクションは、複数のリソースにまたがる可能性のあるトランザクションです。たとえば、1つ以上のデータベースまたはデータベースとメッセージキュー。トランザクションが正常にコミットするには、すべての個々のリソースが正常にコミットする必要があります。それらのいずれかが失敗した場合、トランザクションはすべてのリソースでロールバックする必要があります。たとえば、分散トランザクションは、異なる銀行によってホストされている2つの銀行口座間の送金で構成されている場合があります。両方が正常に完了するという保証なしに、どちらかのトランザクションをコミットすることは望ましくありません。そうしないと、データが複製されたり(挿入が完了して削除が失敗した場合)、失われたり(削除が完了して挿入が失敗した場合)になる可能性があります。
したがって、アプリケーションが複数のトランザクションリソースのデータにアクセスまたは更新する必要がある場合は常に、分散トランザクションを使用する必要があります。リソースごとに個別のトランザクションを使用することは可能ですが、このアプローチではエラーが発生しやすくなります。あるリソースのトランザクションが正常にコミットされ、別のリソースが失敗してロールバックする必要がある場合、最初のトランザクションはロールバックできなくなるため、アプリケーションの状態に一貫性がなくなります。一方のリソースが正常にコミットしたが、もう一方のリソースが正常にコミットする前にシステムがクラッシュした場合、アプリケーションは再び不整合になります。
XA
X / Open分散トランザクション処理(DTP)モデルは、分散トランザクション処理のアーキテクチャを定義します。 DTPアーキテクチャでは、調整トランザクションマネージャは、トランザクションに参加しているすべてのリソースに関する知識に基づいて、トランザクションの処理方法を各リソースに指示します。通常、独自のトランザクションコミットとリカバリを管理するリソースは、このタスクをトランザクションマネージャーに委任します。
アーキテクチャのXA仕様は、準拠したトランザクションミドルウェアとデータベース製品間の相互運用性を保証するオープンスタンダードを提供します。したがって、これらのさまざまなリソースは、分散トランザクションに一緒に参加できます。
DTPモデルには、相互に関連する3つのコンポーネントが含まれています。
- トランザクションの境界を定義し、トランザクションを構成するアクションを指定するアプリケーションプログラム。
- 共有リソースへのアクセスを提供するデータベースやファイルシステムなどのリソースマネージャー。
- トランザクションに識別子を割り当て、その進行状況を監視し、トランザクションの完了と障害の回復に責任を持つトランザクションマネージャー。
XA標準は、2フェーズコミットプロトコルと、トランザクションマネージャーとリソースマネージャー間の通信に使用されるインターフェイスを定義します。 2フェーズコミットプロトコルは、トランザクションに関与するすべての参加者が一緒にコミットまたはロールバックすることを保証します。したがって、トランザクション全体がコミットされるか、トランザクション全体がロールバックされます。
2フェーズコミットは、準備フェーズとコミットフェーズで構成されます。準備フェーズでは、トランザクションのすべての参加者が、トランザクションに必要な変更を完了することに同意する必要があります。参加者のいずれかが問題を報告した場合、準備フェーズは失敗し、トランザクションはロールバックされます。準備フェーズが成功すると、フェーズ2、コミットフェーズが開始されます。コミットフェーズ中に、トランザクションマネージャーはすべての参加者にトランザクションをコミットするように指示します。
SQLServerとXA
SQL Server 2019でXAサポートを有効にするには、このドキュメントに含まれている「MSDTCサービスの実行」セクションの手順に従ってください。
XAトランザクションを理解する
以前のバージョンのSQLServerでXAサポートを有効にするには、次のドキュメントの手順に従ってください。
IBM Business Process Manager(BPM)用のMicrosoftSQLServerでのXAトランザクションの構成
SQL Server ODBCドライバーは、XA対応のSQLServer2016および2019インスタンスでテストされています。
Easysoft SQLServerODBCドライバー
XAサポートは、バージョン1.11.3でSQLServerODBCドライバーに追加されました。ドライバのXAサポートは、OracleTuxedoおよびSQLServer2016および2019でテストされています。
SQL Server ODBCドライバーをXAトランザクションに参加させるには、es_xa_context
という名前の構造体を使用する必要があります。 アプリケーションで。 es_xa_context
XAリソースマネージャー構成で指定したODBCデータソースに接続し、接続ハンドルを返します。例:
int ret; SQLHANDLE hEnv, hConn; ret = es_xa_context( NULL, &hEnv, &hConn );
タキシードでは、es_xa_context
であるODBCデータソース 接続先は、リソースマネージャーのOPENINFO
で指定されます そんなTuxedo設定ファイルの文字列。この例では、「SQLSERVER_SAMPLE」です:
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"
ドライバー定義のXAリソースマネージャー名とXAスイッチはEASYSOFT_SQLSERVER_ODBC
です。 およびessql_xaosw
。
そんなTuxedoでは、TuxedoResourceManager定義ファイル${TUXDIR}/udataobj/RM
でこれらを指定します。 。例:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst
サンプルEasysoft/Tuxedo / SQLServerXAアプリケーション
まず、XA対応のSQLServerインスタンスに接続するSQLServerODBCドライバーデータソースを設定します。
- あなたのTuxedoマシンに、SQLServerODBCドライバーをインストールします。
- odbc.iniにSQLServerODBCドライバーデータソースを作成します。例:
[SQLSERVER_SAMPLE] Driver=Easysoft ODBC-SQL Server Description=Easysoft SQL Server ODBC driver Server=mymachine\myxaenabledinstance User=mydomain\myuser Password=mypassword Database=XA1
- Tuxedoアプリケーションのサンプルテーブルを作成します。
$ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)
サンプルのTuxedoXAアプリケーションを作成して実行します。
-
$ cd ~ $ mkdir simpdir $ cd simpdir $ touch simpcl.c simpserv.c ubbsimple
- 次の行をsimpcl.cに追加します。
#include <stdio.h> #include "atmi.h" /* TUXEDO Header File */ #if defined(__STDC__) || defined(__cplusplus) main(int argc, char *argv[]) #else main(argc, argv) int argc; char *argv[]; #endif { char *sendbuf, *rcvbuf; long sendlen, rcvlen; int ret; if(argc != 2) { (void) fprintf(stderr, "Usage: simpcl <SQL>\n"); exit(1); } /* Attach to System/T as a Client Process */ if (tpinit((TPINIT *) NULL) == -1) { (void) fprintf(stderr, "Tpinit failed\n"); exit(1); } sendlen = strlen(argv[1]); /* Allocate STRING buffers for the request and the reply */ if((sendbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) { (void) fprintf(stderr,"Error allocating send buffer\n"); tpterm(); exit(1); } if((rcvbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) { (void) fprintf(stderr,"Error allocating receive buffer\n"); tpfree(sendbuf); tpterm(); exit(1); } (void) strcpy(sendbuf, argv[1]); /* Request the service EXECUTE, waiting for a reply */ ret = tpcall("EXECUTE", (char *)sendbuf, 0, (char **)&rcvbuf, &rcvlen, (long)0); if(ret == -1) { (void) fprintf(stderr, "Can't send request to service EXECUTE\n"); (void) fprintf(stderr, "Tperrno = %d\n", tperrno); tpfree(sendbuf); tpfree(rcvbuf); tpterm(); exit(1); } (void) fprintf(stdout, "Returned string is: %s\n", rcvbuf); /* Free Buffers & Detach from System/T */ tpfree(sendbuf); tpfree(rcvbuf); tpterm(); return(0); }
- 次の行をsimpserv.cに追加します。
#include <stdio.h> #include <ctype.h> #include <atmi.h> /* TUXEDO Header File */ #include <userlog.h> /* TUXEDO Header File */ #include <xa.h> #include <sql.h> #include <sqlext.h> #include <string.h> /* tpsvrinit is executed when a server is booted, before it begins processing requests. It is not necessary to have this function. Also available is tpsvrdone (not used in this example), which is called at server shutdown time. */ int tpsvrinit(int argc, char *argv[]) { int ret; /* Some compilers warn if argc and argv aren't used. */ argc = argc; argv = argv; /* simpapp is non-transactional, so there is no need for tpsvrinit() to call tx_open() or tpopen(). However, if this code is modified to run in a Tuxedo group associated with a Resource Manager then either a call to tx_open() or a call to tpopen() must be inserted here. */ /* userlog writes to the central TUXEDO message log */ userlog("Welcome to the simple server"); ret = tpopen(); userlog("tpopen returned %d, error=%x", ret, tperrno ); return(0); } void tpsvrdone( void ) { int ret; ret = tpclose(); userlog("tpclose returned %d", ret); } /* This function performs the actual service requested by the client. Its argument is a structure containing among other things a pointer to the data buffer, and the length of the data buffer. */ xa_open_entry() call. int es_xa_context( int* rmid, SQLHANDLE* henv, SQLHANDLE* hdbc ); void EXECUTE(TPSVCINFO *rqst) { int ret; char *result; SQLHANDLE hStmt; char str[ 256 ]; SQLHANDLE hEnv, hConn; SQLSMALLINT slen; ret = es_xa_context( NULL, &hEnv, &hConn ); userlog("es_xa_context returns %d, hEnv = %p, hConn = %p", ret, hEnv, hConn ); if ( ret != 0 ) { result = tpalloc( "STRING", "*", 128 ); sprintf( result, "es_xa_context returned %d", ret ); /* Return the transformed buffer to the requestor. */ tpreturn(TPSUCCESS, 0, result, strlen( result ), 0); } else { ret = tpbegin( 0, 0 ); ret = SQLAllocHandle( SQL_HANDLE_STMT, hConn, &hStmt ); ret = SQLExecDirect( hStmt, rqst -> data, rqst -> len ); ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt ); ret = tpcommit( 0 ); result = tpalloc( "STRING", "*", 128 ); sprintf( result, "tpcommit returns %d", ret ); /* Return the transformed buffer to the requestor. */ tpreturn(TPSUCCESS, 0, result, strlen( result ), 0); } }
- 次の行をubbsimpleに追加します。
*RESOURCES IPCKEY 123456 DOMAINID simpapp MASTER simple MAXACCESSERS 20 MAXSERVERS 10 MAXSERVICES 10 MODEL SHM LDBAL N *MACHINES DEFAULT: APPDIR="/home/myuser/simpdir" TUXCONFIG="/home/myuser/simpdir/tuxconfig" TUXDIR="/home/myuser/OraHome/tuxedo12.2.2.0.0" mymachine LMID=simple TLOGNAME=TLOG TLOGDEVICE="/home/myuser/simpdir/tuxlog" *GROUPS GROUP1 LMID=simple GRPNO=1 OPENINFO=NONE TMSNAME=mySQLSERVER_TMS OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE" *SERVERS DEFAULT: CLOPT="-A" simpserv SRVGRP=GROUP1 SRVID=1 *SERVICES EXECUTE
- 環境を設定します:
export TUXDIR=/home/myuser/OraHome/tuxedo12.2.2.0.0 export TUXCONFIG=/home/myuser/simpdir/tuxconfig export PATH=$PATH:$TUXDIR/bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TUXDIR/lib:/usr/local/easysoft/unixODBC/lib: \ /usr/local/easysoft/sqlserver/lib:/usr/local/easysoft/lib
- サンプルクライアントをビルドします:
buildclient -o simpcl -f simpcl.c
クライアントのビルド中に「dlopenへの未定義の参照」というエラーが発生した場合は、代わりに次のコマンドを試してください。
buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
- サンプルサーバーをビルドします:
buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \ -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
- サンプルアプリケーションのTUXCONFIGファイルを作成します:
tmloadcf ubbsimple
- サンプルアプリケーション用のTuxedoロギングデバイスを作成します:
$ tmadmin -c > crdl -z /home/myuser/simpdir/tuxlog -b 512
- SQLServerODBCドライバーとインターフェイスするTuxedoトランザクションマネージャーを構築します。
$ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
- サンプルサーバーを起動します:
$ tmboot
- サンプルアプリケーションをテストします:
./simpcl "insert into tx_test1 values( 1, 'hello world' )" /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> select * from tx_test1 +------------+--------------+ | i | c | +------------+--------------+ | 1 | hello world | +------------+--------------+
- SQL Serverテーブルにデータが表示されている場合は、サンプルサーバーをシャットダウンします:
tmshutdown
それ以外の場合は、サンプルアプリケーションディレクトリのULOG.nnnを参照してください。