Toegang krijgen tot SQL Server in de context van een XA-transactie met de Easysoft SQL Server ODBC-driver en Oracle Tuxedo.
Inleiding
Waarom gedistribueerde transacties nodig zijn
Een transactie is een reeks acties die worden uitgevoerd als een enkele bewerking waarbij ofwel alle acties worden uitgevoerd of geen ervan. Een transactie eindigt met een commit-actie die de wijzigingen permanent maakt. Als een van de wijzigingen niet kan worden doorgevoerd, wordt de transactie teruggedraaid en worden alle wijzigingen ongedaan gemaakt.
Een gedistribueerde transactie is een transactie die meerdere bronnen kan omvatten. Bijvoorbeeld een of meer databases of een database en een berichtenwachtrij. Om de transactie succesvol te laten plaatsvinden, moeten alle individuele resources succesvol worden vastgelegd; als een van deze niet succesvol is, moet de transactie in alle bronnen worden teruggedraaid. Een gedistribueerde transactie kan bijvoorbeeld bestaan uit een geldoverdracht tussen twee bankrekeningen, gehost door verschillende banken, en dus ook op verschillende databases. U wilt niet dat een van beide transacties wordt uitgevoerd zonder de garantie dat beide met succes worden voltooid. Anders kunnen gegevens worden gedupliceerd (als het invoegen is voltooid en het verwijderen mislukt) of verloren gaan (als het verwijderen is voltooid en het invoegen mislukt).
Wanneer een toepassing daarom toegang moet krijgen tot de gegevens in meerdere transactiebronnen of deze moet bijwerken, moet deze een gedistribueerde transactie gebruiken. Het is mogelijk om voor elk van de resources een afzonderlijke transactie te gebruiken, maar deze aanpak is foutgevoelig. Als de transactie in de ene resource met succes wordt doorgevoerd, maar een andere mislukt en moet worden teruggedraaid, kan de eerste transactie niet meer worden teruggedraaid, waardoor de status van de toepassing inconsistent wordt. Als de ene bron met succes vastlegt, maar het systeem crasht voordat de andere bron met succes kan vastleggen, is de toepassing opnieuw inconsistent.
XA
Het X/Open Distributed Transaction Processing (DTP)-model definieert een architectuur voor gedistribueerde transactieverwerking. In de DTP-architectuur vertelt een coördinerende transactiemanager elke resource hoe een transactie moet worden verwerkt, op basis van zijn kennis van alle resources die aan de transactie deelnemen. Resources die normaal gesproken hun eigen transactie vastleggen en herstellen, delegeren deze taak aan de transactiemanager.
De XA-specificatie van de architectuur biedt een open standaard die zorgt voor interoperabiliteit tussen conforme transactionele middleware- en databaseproducten. Deze verschillende bronnen kunnen daarom samen deelnemen aan een gedistribueerde transactie.
Het DTP-model omvat drie onderling gerelateerde componenten:
- Een applicatieprogramma dat transactiegrenzen definieert en acties specificeert die een transactie vormen.
- Bronnenbeheerders zoals databases of bestandssystemen die toegang bieden tot gedeelde bronnen.
- Een transactiemanager die identifiers toewijst aan transacties, hun voortgang bewaakt en verantwoordelijkheid neemt voor het voltooien van transacties en het herstellen van fouten.
De XA-standaard definieert het tweefasige commit-protocol en de interface die wordt gebruikt voor communicatie tussen een transactiemanager en een resourcemanager. Het tweefasige commit-protocol biedt een alles-of-niets-garantie dat alle deelnemers die bij de transactie betrokken zijn, zich committeren of samen terugdraaien. De hele transactie wordt dus vastgelegd of de hele transactie wordt teruggedraaid.
De tweefasen-commit bestaat uit een voorbereidingsfase en een commit-fase. Tijdens de voorbereidingsfase moeten alle deelnemers aan de transactie akkoord gaan met het voltooien van de wijzigingen die vereist zijn voor de transactie. Als een van de deelnemers een probleem meldt, mislukt de voorbereidingsfase en wordt de transactie teruggedraaid. Als de voorbereidingsfase succesvol is, fase twee, begint de vastleggingsfase. Tijdens de vastleggingsfase instrueert de Transactiemanager alle deelnemers om de transactie door te voeren.
SQL Server en XA
Om XA-ondersteuning in SQL Server 2019 in te schakelen, volgt u de instructies in de sectie "De MS DTC-service uitvoeren" in dit document:
XA-transacties begrijpen
Volg de instructies in dit document om XA-ondersteuning in eerdere versies van SQL Server in te schakelen:
XA-transacties configureren in Microsoft SQL Server voor IBM Business Process Manager (BPM)
Het SQL Server ODBC-stuurprogramma is getest met XA-compatibele SQL Server 2016- en 2019-instanties.
Het Easysoft SQL Server ODBC-stuurprogramma
XA-ondersteuning is toegevoegd aan de SQL Server ODBC-driver in versie 1.11.3. De XA-ondersteuning van het stuurprogramma is getest met Oracle Tuxedo en SQL Server 2016 en 2019.
Om het SQL Server ODBC-stuurprogramma in te schakelen in een XA-transactie, moet u een structuur gebruiken met de naam es_xa_context
in uw aanvraag. es_xa_context
maakt verbinding met de ODBC-gegevensbron die u hebt opgegeven in uw XA resource manager-configuratie en retourneert een verbindingshandle. Bijvoorbeeld:
int ret; SQLHANDLE hEnv, hConn; ret = es_xa_context( NULL, &hEnv, &hConn );
In Tuxedo, de ODBC-gegevensbron die es_xa_context
verbindt met is gespecificeerd in de Resource Manager OPENINFO
string in het Tuxedo-configuratiebestand. In dit voorbeeld is dat "SQLSERVER_SAMPLE":
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"
De door het stuurprogramma gedefinieerde XA Resource Manager-naam en XA-switch zijn EASYSOFT_SQLSERVER_ODBC
en essql_xaosw
.
In Tuxedo specificeert u deze in het Tuxedo Resource Manager-definitiebestand, ${TUXDIR}/udataobj/RM
. Bijvoorbeeld:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst
Voorbeeld Easysoft / Tuxedo / SQL Server XA-toepassing
Stel eerst een SQL Server ODBC-stuurprogrammagegevensbron in die verbinding maakt met een XA-enabled SQL Server-exemplaar:
- Installeer het SQL Server ODBC-stuurprogramma op uw Tuxedo-machine.
- Maak een SQL Server ODBC-stuurprogrammagegevensbron in odbc.ini. Bijvoorbeeld:
[SQLSERVER_SAMPLE] Driver=Easysoft ODBC-SQL Server Description=Easysoft SQL Server ODBC driver Server=mymachine\myxaenabledinstance User=mydomain\myuser Password=mypassword Database=XA1
- Maak een voorbeeldtabel voor de Tuxedo-toepassing:
$ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)
Maak en voer de voorbeeldtoepassing Tuxedo XA uit.
-
$ cd ~ $ mkdir simpdir $ cd simpdir $ touch simpcl.c simpserv.c ubbsimple
- Voeg deze regels toe aan 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); }
- Voeg deze regels toe aan 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); } }
- Voeg deze regels toe aan 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
- Uw omgeving instellen:
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
- Bouw de voorbeeldclient:
buildclient -o simpcl -f simpcl.c
Als je de foutmelding "undefined reference to dlopen" krijgt bij het bouwen van de client, probeer dan dit commando:
buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
- Bouw de voorbeeldserver:
buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \ -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
- Maak het TUXCONFIG-bestand voor de voorbeeldtoepassing:
tmloadcf ubbsimple
- Maak een Tuxedo-registratieapparaat voor de voorbeeldtoepassing:
$ tmadmin -c > crdl -z /home/myuser/simpdir/tuxlog -b 512
- Bouw een Tuxedo-transactiemanager die communiceert met de SQL Server ODBC-driver:
$ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
- Start de voorbeeldserver op:
$ tmboot
- Test de voorbeeldtoepassing:
./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 | +------------+--------------+
- Als u de gegevens in de SQL Server-tabel ziet, sluit u de voorbeeldserver af:
tmshutdown
Raadpleeg anders ULOG.nnn in de voorbeeldtoepassingsmap.