Sådan får du adgang til SQL Server i forbindelse med en XA-transaktion med Easysoft SQL Server ODBC-driveren og Oracle Tuxedo.
Introduktion
Hvorfor er der behov for distribuerede transaktioner
En transaktion er en række handlinger, der udføres som en enkelt operation, hvor enten alle handlingerne udføres, eller ingen af dem udføres. En transaktion ender med en commit-handling, der gør ændringerne permanente. Hvis nogen af ændringerne ikke kan forpligtes, vil transaktionen rulle tilbage og tilbageføre alle ændringerne.
En distribueret transaktion er en transaktion, der kan spænde over flere ressourcer. For eksempel en eller flere databaser eller en database og en beskedkø. For at transaktionen skal forpligtes med succes, skal alle de individuelle ressourcer forpligte sig med succes; hvis nogen af dem ikke lykkes, skal transaktionen rulle tilbage i alle ressourcerne. For eksempel kan en distribueret transaktion bestå af en pengeoverførsel mellem to bankkonti, hostet af forskellige banker, og så også på forskellige databaser. Du ønsker ikke, at nogen af transaktionerne udføres uden en garanti for, at begge vil gennemføres med succes. Ellers kan data duplikeres (hvis indsættelsen fuldføres, og sletningen mislykkes) eller gå tabt (hvis sletningen fuldføres, og indsættelsen mislykkes).
Når en applikation skal have adgang til eller opdatere dataene i flere transaktionsressourcer, bør den derfor bruge en distribueret transaktion. Det er muligt at bruge en separat transaktion på hver af ressourcerne, men denne tilgang er udsat for fejl. Hvis transaktionen i en ressource forpligtes, men en anden mislykkes og skal rulle tilbage, kan den første transaktion ikke længere rulles tilbage, så applikationens tilstand bliver inkonsekvent. Hvis en ressource commiterer med succes, men systemet går ned, før den anden ressource kan commiterer med succes, er applikationen igen inkonsekvent.
XA
X/Open Distributed Transaction Processing (DTP)-modellen definerer en arkitektur for distribueret transaktionsbehandling. I DTP-arkitekturen fortæller en koordinerende transaktionsadministrator hver ressource, hvordan en transaktion skal behandles, baseret på dens viden om alle de ressourcer, der deltager i transaktionen. Ressourcer, der normalt administrerer deres egen transaktionsbekræftelse og -gendannelse, uddelegerer denne opgave til transaktionsadministratoren.
Arkitekturens XA-specifikation giver en åben standard, der sikrer interoperabilitet på tværs af konforme transaktions-middleware og databaseprodukter. Disse forskellige ressourcer er derfor i stand til at deltage sammen i en distribueret transaktion.
DTP-modellen inkluderer tre indbyrdes forbundne komponenter:
- Et applikationsprogram, der definerer transaktionsgrænser og specificerer handlinger, der udgør en transaktion.
- Ressourceadministratorer såsom databaser eller filsystemer, der giver adgang til delte ressourcer.
- En Transaction Manager, der tildeler identifikatorer til transaktioner, overvåger deres fremskridt og tager ansvar for transaktionsgennemførelse og fejlgendannelse.
XA-standarden definerer den to-fasede commit-protokol og den grænseflade, der bruges til kommunikation mellem en Transaction Manager og en Resource Manager. To-fase commit-protokollen giver en alt-eller-intet-garanti for, at alle deltagere, der er involveret i transaktionen, enten forpligter sig eller ruller tilbage sammen. Hele transaktionen commits eller hele transaktionen ruller tilbage, derfor.
Den tofasede commit består af en forberedelsesfase og en commitfase. Under forberedelsesfasen skal alle deltagere i transaktionen acceptere at gennemføre de ændringer, som transaktionen kræver. Hvis nogen af deltagerne rapporterer et problem, vil forberedelsesfasen mislykkes, og transaktionen vil rulle tilbage. Hvis forberedelsesfasen er vellykket, fase to, starter commit-fasen. Under forpligtelsesfasen instruerer Transaction Manager alle deltagere om at forpligte transaktionen.
SQL-server og XA
For at aktivere XA-understøttelse i SQL Server 2019 skal du følge instruktionerne i afsnittet "Køre MS DTC-tjenesten" i dette dokument:
Forstå XA-transaktioner
For at aktivere XA-understøttelse i tidligere versioner af SQL Server skal du følge instruktionerne i dette dokument:
Konfiguration af XA-transaktioner i Microsoft SQL Server til IBM Business Process Manager (BPM)
SQL Server ODBC-driveren er blevet testet med XA-aktiverede SQL Server 2016- og 2019-instanser.
Easysoft SQL Server ODBC-driveren
XA-understøttelse blev tilføjet til SQL Server ODBC-driver i version 1.11.3. Driverens XA-understøttelse er blevet testet med Oracle Tuxedo og SQL Server 2016 og 2019.
For at inddrage SQL Server ODBC-driveren i en XA-transaktion skal du bruge en struktur med navnet es_xa_context
i din ansøgning. es_xa_context
opretter forbindelse til den ODBC-datakilde, du har angivet i din XA-ressourcestyringskonfiguration og returnerer et forbindelseshåndtag. For eksempel:
int ret; SQLHANDLE hEnv, hConn; ret = es_xa_context( NULL, &hEnv, &hConn );
I Tuxedo er ODBC-datakilden, der es_xa_context
forbinder til er angivet i Ressourcestyring OPENINFO
streng i Tuxedo-konfigurationsfilen. I dette eksempel er det "SQLSERVER_SAMPLE":
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"
Det driverdefinerede XA Resource Manager-navn og XA-switch er EASYSOFT_SQLSERVER_ODBC
og essql_xaosw
.
I Tuxedo angiver du disse i Tuxedo Resource Manager-definitionsfilen, ${TUXDIR}/udataobj/RM
. For eksempel:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst
Eksempel på Easysoft / Tuxedo / SQL Server XA-applikation
Først skal du konfigurere en SQL Server ODBC-driverdatakilde, der forbinder til en XA-aktiveret SQL Server-instans:
- På din Tuxedo-maskine skal du installere SQL Server ODBC-driveren.
- Opret en SQL Server ODBC-driverdatakilde i odbc.ini. For eksempel:
[SQLSERVER_SAMPLE] Driver=Easysoft ODBC-SQL Server Description=Easysoft SQL Server ODBC driver Server=mymachine\myxaenabledinstance User=mydomain\myuser Password=mypassword Database=XA1
- Opret en eksempeltabel til 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)
Opret og kør prøven Tuxedo XA Application.
-
$ cd ~ $ mkdir simpdir $ cd simpdir $ touch simpcl.c simpserv.c ubbsimple
- Tilføj disse linjer til 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); }
- Tilføj disse linjer til 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); } }
- Tilføj disse linjer til 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
- Indstil dit 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
- Byg prøveklienten:
buildclient -o simpcl -f simpcl.c
Hvis du får fejlen "undefined reference to dlopen", når du bygger klienten, så prøv denne kommando i stedet:
buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
- Byg prøveserveren:
buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \ -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
- Opret TUXCONFIG-filen til eksempelapplikationen:
tmloadcf ubbsimple
- Opret en Tuxedo-logningsenhed til eksempelapplikationen:
$ tmadmin -c > crdl -z /home/myuser/simpdir/tuxlog -b 512
- Byg en Tuxedo-transaktionsadministrator, der har grænseflader med SQL Server ODBC-driveren:
$ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
- Start prøveserveren:
$ tmboot
- Test eksempelapplikationen:
./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 | +------------+--------------+
- Hvis du ser dataene i SQL Server-tabellen, skal du lukke prøveserveren ned:
tmshutdown
Ellers skal du konsultere ULOG.nnn i eksempelprogrambiblioteket.