Zugriff auf SQL Server im Kontext einer XA-Transaktion mit dem Easysoft SQL Server ODBC-Treiber und Oracle Tuxedo.
Einführung
Warum verteilte Transaktionen benötigt werden
Eine Transaktion ist eine Reihe von Aktionen, die als einzelne Operation ausgeführt werden, bei der entweder alle Aktionen oder keine ausgeführt werden. Eine Transaktion endet mit einer Commit-Aktion, die die Änderungen dauerhaft macht. Wenn eine der Änderungen nicht festgeschrieben werden kann, wird die Transaktion rückgängig gemacht und alle Änderungen rückgängig gemacht.
Eine verteilte Transaktion ist eine Transaktion, die mehrere Ressourcen umfassen kann. Beispielsweise eine oder mehrere Datenbanken oder eine Datenbank und eine Nachrichtenwarteschlange. Damit die Transaktion erfolgreich festgeschrieben wird, müssen alle einzelnen Ressourcen erfolgreich festgeschrieben werden; Wenn einer von ihnen nicht erfolgreich ist, muss die Transaktion in allen Ressourcen rückgängig gemacht werden. Beispielsweise kann eine verteilte Transaktion aus einer Geldüberweisung zwischen zwei Bankkonten bestehen, die von verschiedenen Banken gehostet werden, und somit auch auf verschiedenen Datenbanken. Sie möchten nicht, dass eine der beiden Transaktionen ohne eine Garantie, dass beide erfolgreich abgeschlossen werden, festgeschrieben wird. Andernfalls können Daten dupliziert werden (wenn das Einfügen abgeschlossen ist und das Löschen fehlschlägt) oder verloren gehen (wenn das Löschen abgeschlossen ist und das Einfügen fehlschlägt).
Wann immer eine Anwendung auf die Daten in mehreren Transaktionsressourcen zugreifen oder diese aktualisieren muss, sollte sie daher eine verteilte Transaktion verwenden. Es ist möglich, für jede der Ressourcen eine separate Transaktion zu verwenden, aber dieser Ansatz ist fehleranfällig. Wenn die Transaktion in einer Ressource erfolgreich festgeschrieben wird, eine andere jedoch fehlschlägt und zurückgesetzt werden muss, kann die erste Transaktion nicht mehr zurückgesetzt werden, sodass der Status der Anwendung inkonsistent wird. Wenn eine Ressource erfolgreich festschreibt, aber das System abstürzt, bevor die andere Ressource erfolgreich festschreiben kann, ist die Anwendung wieder inkonsistent.
XA
Das X/Open Distributed Transaction Processing (DTP)-Modell definiert eine Architektur für die verteilte Transaktionsverarbeitung. In der DTP-Architektur teilt ein koordinierender Transaktionsmanager jeder Ressource mit, wie eine Transaktion zu verarbeiten ist, basierend auf seinem Wissen über alle an der Transaktion beteiligten Ressourcen. Ressourcen, die normalerweise ihre eigene Transaktionsfestschreibung und -wiederherstellung verwalten, delegieren diese Aufgabe an den Transaktionsmanager.
Die XA-Spezifikation der Architektur bietet einen offenen Standard, der die Interoperabilität zwischen konformen transaktionalen Middleware- und Datenbankprodukten gewährleistet. Diese unterschiedlichen Ressourcen können daher gemeinsam an einer verteilten Transaktion teilnehmen.
Das DTP-Modell umfasst drei miteinander verbundene Komponenten:
- Ein Anwendungsprogramm, das Transaktionsgrenzen definiert und Aktionen spezifiziert, die eine Transaktion darstellen.
- Ressourcenmanager wie Datenbanken oder Dateisysteme, die Zugriff auf gemeinsam genutzte Ressourcen bieten.
- Ein Transaktionsmanager, der Transaktionen Kennungen zuweist, ihren Fortschritt überwacht und die Verantwortung für den Abschluss der Transaktion und die Fehlerbehebung übernimmt.
Der XA-Standard definiert das Zwei-Phasen-Commit-Protokoll und die Schnittstelle, die für die Kommunikation zwischen einem Transaktionsmanager und einem Ressourcenmanager verwendet wird. Das zweiphasige Commit-Protokoll bietet eine Alles-oder-Nichts-Garantie, dass alle an der Transaktion beteiligten Teilnehmer gemeinsam entweder Commit oder Rollback durchführen. Daher wird die gesamte Transaktion festgeschrieben oder die gesamte Transaktion zurückgesetzt.
Das zweiphasige Commit besteht aus einer Vorbereitungsphase und einer Commit-Phase. Während der Vorbereitungsphase müssen alle an der Transaktion Beteiligten zustimmen, die für die Transaktion erforderlichen Änderungen vorzunehmen. Wenn einer der Teilnehmer ein Problem meldet, schlägt die Vorbereitungsphase fehl und die Transaktion wird rückgängig gemacht. Wenn die Vorbereitungsphase erfolgreich ist, Phase zwei, beginnt die Commit-Phase. Während der Festschreibungsphase weist der Transaktionsmanager alle Teilnehmer an, die Transaktion festzuschreiben.
SQL-Server und XA
Um die XA-Unterstützung in SQL Server 2019 zu aktivieren, befolgen Sie die Anweisungen im Abschnitt „Ausführen des MS DTC-Dienstes“ in diesem Dokument:
XA-Transaktionen verstehen
Befolgen Sie die Anweisungen in diesem Dokument, um die XA-Unterstützung in früheren Versionen von SQL Server zu aktivieren:
Konfigurieren von XA-Transaktionen in Microsoft SQL Server für IBM Business Process Manager (BPM)
Der SQL Server ODBC-Treiber wurde mit XA-fähigen SQL Server 2016- und 2019-Instanzen getestet.
Der Easysoft SQL Server ODBC-Treiber
XA-Unterstützung wurde dem SQL Server-ODBC-Treiber in Version 1.11.3 hinzugefügt. Die XA-Unterstützung des Treibers wurde mit Oracle Tuxedo und SQL Server 2016 und 2019 getestet.
Um den SQL Server-ODBC-Treiber in eine XA-Transaktion einzutragen, müssen Sie eine Struktur namens es_xa_context
verwenden in Ihrer Bewerbung. es_xa_context
stellt eine Verbindung zu der ODBC-Datenquelle her, die Sie in Ihrer XA-Ressourcenmanagerkonfiguration angegeben haben, und gibt ein Verbindungshandle zurück. Beispiel:
int ret; SQLHANDLE hEnv, hConn; ret = es_xa_context( NULL, &hEnv, &hConn );
In Tuxedo die ODBC-Datenquelle, die es_xa_context
Verbindung zu wird im Ressourcenmanager OPENINFO
angegeben Zeichenfolge in der Tuxedo-Konfigurationsdatei. In diesem Beispiel ist es „SQLSERVER_SAMPLE“:
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"
Der treiberdefinierte XA-Ressourcen-Manager-Name und XA-Schalter sind EASYSOFT_SQLSERVER_ODBC
und essql_xaosw
.
In Tuxedo geben Sie diese in der Tuxedo Resource Manager-Definitionsdatei ${TUXDIR}/udataobj/RM
an . Zum Beispiel:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst
Beispielanwendung für Easysoft / Tuxedo / SQL Server XA
Richten Sie zunächst eine SQL Server-ODBC-Treiberdatenquelle ein, die eine Verbindung zu einer XA-fähigen SQL Server-Instanz herstellt:
- Installieren Sie auf Ihrem Tuxedo-Computer den SQL Server ODBC-Treiber.
- Erstellen Sie eine SQL Server-ODBC-Treiberdatenquelle in odbc.ini. Beispiel:
[SQLSERVER_SAMPLE] Driver=Easysoft ODBC-SQL Server Description=Easysoft SQL Server ODBC driver Server=mymachine\myxaenabledinstance User=mydomain\myuser Password=mypassword Database=XA1
- Erstellen Sie eine Beispieltabelle für die Tuxedo-Anwendung:
$ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)
Erstellen Sie die Tuxedo XA-Beispielanwendung und führen Sie sie aus.
-
$ cd ~ $ mkdir simpdir $ cd simpdir $ touch simpcl.c simpserv.c ubbsimple
- Fügen Sie diese Zeilen zu simpcl.c hinzu:
#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); }
- Fügen Sie diese Zeilen zu simpserv.c hinzu:
#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); } }
- Fügen Sie diese Zeilen zu ubbsimple hinzu:
*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
- Stellen Sie Ihre Umgebung ein:
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
- Erstellen Sie den Beispielclient:
buildclient -o simpcl -f simpcl.c
Wenn Sie beim Erstellen des Clients den Fehler "undefinierte Referenz auf dlopen" erhalten, versuchen Sie stattdessen diesen Befehl:
buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
- Erstellen Sie den Beispielserver:
buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \ -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
- Erstellen Sie die TUXCONFIG-Datei für die Beispielanwendung:
tmloadcf ubbsimple
- Erstellen Sie ein Tuxedo-Protokollierungsgerät für die Beispielanwendung:
$ tmadmin -c > crdl -z /home/myuser/simpdir/tuxlog -b 512
- Erstellen Sie einen Tuxedo-Transaktionsmanager, der mit dem SQL Server-ODBC-Treiber interagiert:
$ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
- Starten Sie den Beispielserver:
$ tmboot
- Testen Sie die Beispielanwendung:
./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 | +------------+--------------+
- Wenn Sie die Daten in der SQL Server-Tabelle sehen, fahren Sie den Beispielserver herunter:
tmshutdown
Konsultieren Sie andernfalls ULOG.nnn im Musteranwendungsverzeichnis.