Hur man får åtkomst till SQL Server i samband med en XA-transaktion med Easysoft SQL Server ODBC-drivrutinen och Oracle Tuxedo.
Introduktion
Varför distribuerade transaktioner behövs
En transaktion är en serie av åtgärder som utförs som en enda operation där antingen alla åtgärder utförs eller ingen av dem görs. En transaktion slutar med en commit-åtgärd som gör ändringarna permanenta. Om någon av ändringarna inte kan genomföras kommer transaktionen att rullas tillbaka och alla ändringar återställs.
En distribuerad transaktion är en transaktion som kan sträcka sig över flera resurser. Till exempel en eller flera databaser eller en databas och en meddelandekö. För att transaktionen ska genomföras framgångsrikt måste alla individuella resurser genomföras framgångsrikt; om någon av dem misslyckas måste transaktionen återställas i alla resurser. En distribuerad transaktion kan till exempel bestå av en överföring av pengar mellan två bankkonton, värd för olika banker, och så även på olika databaser. Du skulle inte vilja att någon av transaktionerna genomförs utan en garanti för att båda kommer att slutföras framgångsrikt. Annars kan data dupliceras (om infogningen slutförs och raderingen misslyckas) eller förloras (om raderingen slutförs och infogningen misslyckas).
Närhelst en applikation behöver komma åt eller uppdatera data i flera transaktionsresurser bör den därför använda en distribuerad transaktion. Det är möjligt att använda en separat transaktion på var och en av resurserna, men denna metod är felbenägen. Om transaktionen i en resurs genomförs framgångsrikt men en annan misslyckas och måste återställas, kan den första transaktionen inte längre återställas, så tillståndet för applikationen blir inkonsekvent. Om en resurs commit framgångsrikt men systemet kraschar innan den andra resursen kan commit framgångsrikt, är programmet igen inkonsekvent.
XA
X/Open Distributed Transaction Processing (DTP)-modellen definierar en arkitektur för distribuerad transaktionsbehandling. I DTP-arkitekturen berättar en koordinerande transaktionshanterare för varje resurs hur en transaktion ska behandlas, baserat på dess kunskap om alla resurser som deltar i transaktionen. Resurser som normalt hanterar sin egen transaktionsbekräftelse och återställning delegerar denna uppgift till transaktionshanteraren.
Arkitekturens XA-specifikation tillhandahåller en öppen standard som säkerställer interoperabilitet mellan överensstämmande transaktionsmellanprogram och databasprodukter. Dessa olika resurser kan därför delta tillsammans i en distribuerad transaktion.
DTP-modellen inkluderar tre inbördes relaterade komponenter:
- Ett applikationsprogram som definierar transaktionsgränser och specificerar åtgärder som utgör en transaktion.
- Resurshanterare som databaser eller filsystem som ger åtkomst till delade resurser.
- En transaktionshanterare som tilldelar identifierare till transaktioner, övervakar deras framsteg och tar ansvar för transaktionsslutförande och felåterställning.
XA-standarden definierar två-fas commit-protokollet och gränssnittet som används för kommunikation mellan en Transaction Manager och en Resource Manager. Det tvåfasiga commit-protokollet ger en allt-eller-inget-garanti att alla deltagare som är inblandade i transaktionen antingen commit eller rullar tillbaka tillsammans. Hela transaktionen commits eller hela transaktionen rullar tillbaka, därför.
Två-fas commit består av en förberedelsefas och en commit-fas. Under förberedelsefasen måste alla deltagare i transaktionen gå med på att genomföra de ändringar som transaktionen kräver. Om någon av deltagarna rapporterar ett problem kommer förberedelsefasen att misslyckas och transaktionen rullas tillbaka. Om förberedelsefasen är framgångsrik, fas två, startar commit-fasen. Under commit-fasen instruerar transaktionshanteraren alla deltagare att begå transaktionen.
SQL Server och XA
För att aktivera XA-stöd i SQL Server 2019, följ instruktionerna i avsnittet "Köra MS DTC-tjänsten" som finns i detta dokument:
Förstå XA-transaktioner
För att aktivera XA-stöd i tidigare versioner av SQL Server, följ instruktionerna i detta dokument:
Konfigurera XA-transaktioner i Microsoft SQL Server för IBM Business Process Manager (BPM)
SQL Server ODBC-drivrutinen har testats med XA-aktiverade SQL Server 2016 och 2019-instanser.
Easysoft SQL Server ODBC-drivrutin
XA-stöd lades till SQL Server ODBC-drivrutinen i version 1.11.3. Förarens XA-stöd har testats med Oracle Tuxedo och SQL Server 2016 och 2019.
För att koppla in SQL Server ODBC-drivrutinen i en XA-transaktion måste du använda en struktur som heter es_xa_context
i din ansökan. es_xa_context
ansluter till ODBC-datakällan som du har angett i din XA-resurshanterarkonfiguration och returnerar ett anslutningshandtag. Till exempel:
int ret; SQLHANDLE hEnv, hConn; ret = es_xa_context( NULL, &hEnv, &hConn );
I Tuxedo, ODBC-datakällan som es_xa_context
ansluter till anges i Resurshanteraren OPENINFO
sträng i Tuxedo-konfigurationsfilen. I det här exemplet är det "SQLSERVER_SAMPLE":
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"
Det förardefinierade XA Resource Manager-namnet och XA-switchen är EASYSOFT_SQLSERVER_ODBC
och essql_xaosw
.
I Tuxedo anger du dessa i Tuxedo Resource Manager-definitionsfilen, ${TUXDIR}/udataobj/RM
. Till exempel:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst
Exempel på Easysoft / Tuxedo / SQL Server XA-applikation
Konfigurera först en SQL Server ODBC-drivrutinsdatakälla som ansluter till en XA-aktiverad SQL Server-instans:
- Installera SQL Server ODBC-drivrutinen på din Tuxedo-maskin.
- Skapa en SQL Server ODBC-drivrutinsdatakälla i odbc.ini. Till exempel:
[SQLSERVER_SAMPLE] Driver=Easysoft ODBC-SQL Server Description=Easysoft SQL Server ODBC driver Server=mymachine\myxaenabledinstance User=mydomain\myuser Password=mypassword Database=XA1
- Skapa en exempeltabell för Tuxedo-applikationen:
$ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)
Skapa och kör provet Tuxedo XA Application.
-
$ cd ~ $ mkdir simpdir $ cd simpdir $ touch simpcl.c simpserv.c ubbsimple
- Lägg till dessa rader i 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); }
- Lägg till dessa rader i 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); } }
- Lägg till dessa rader till 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
- Ställ in din miljö:
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
- Bygg exempelklienten:
buildclient -o simpcl -f simpcl.c
Om du får felet "odefinierad referens till dlopen" när du bygger klienten, prova detta kommando istället:
buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
- Bygg exempelservern:
buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \ -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
- Skapa TUXCONFIG-filen för exempelapplikationen:
tmloadcf ubbsimple
- Skapa en Tuxedo-loggningsenhet för exempelapplikationen:
$ tmadmin -c > crdl -z /home/myuser/simpdir/tuxlog -b 512
- Bygg en Tuxedo-transaktionshanterare som har gränssnitt med SQL Server ODBC-drivrutinen:
$ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
- Starta provservern:
$ tmboot
- Testa exempelapplikationen:
./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 | +------------+--------------+
- Om du ser data i SQL Server-tabellen, stäng av exempelservern:
tmshutdown
Annars, konsultera ULOG.nnn i exempelprogramkatalogen.