Cómo acceder a SQL Server en el contexto de una transacción XA con el controlador ODBC de Easysoft SQL Server y Oracle Tuxedo.
Introducción
Por qué son necesarias las transacciones distribuidas
Una transacción es una serie de acciones realizadas como una sola operación en la que se realizan todas las acciones o ninguna. Una transacción finaliza con una acción de confirmación que hace que los cambios sean permanentes. Si alguno de los cambios no se puede confirmar, la transacción se revertirá, revirtiendo todos los cambios.
Una transacción distribuida es una transacción que puede abarcar múltiples recursos. Por ejemplo, una o más bases de datos o una base de datos y una cola de mensajes. Para que la transacción se confirme correctamente, todos los recursos individuales deben confirmarse correctamente; si alguno de ellos no tiene éxito, la transacción debe retrotraerse en todos los recursos. Por ejemplo, una transacción distribuida podría consistir en una transferencia de dinero entre dos cuentas bancarias, alojadas en diferentes bancos, y también en diferentes bases de datos. No le gustaría que ninguna de las transacciones se comprometiera sin una garantía de que ambas se completarán con éxito. De lo contrario, los datos pueden duplicarse (si se completa la inserción y falla la eliminación) o perderse (si se completa la eliminación y falla la inserción).
Siempre que una aplicación necesite acceder o actualizar los datos en múltiples recursos transaccionales, debe usar una transacción distribuida. Es posible usar una transacción separada en cada uno de los recursos, pero este enfoque es propenso a errores. Si la transacción en un recurso se confirma correctamente pero otro falla y debe revertirse, la primera transacción ya no se puede revertir, por lo que el estado de la aplicación se vuelve inconsistente. Si un recurso se confirma con éxito pero el sistema falla antes de que el otro recurso pueda confirmar con éxito, la aplicación vuelve a ser incoherente.
XA
El modelo de procesamiento de transacciones distribuidas (DTP) de X/Open define una arquitectura para el procesamiento de transacciones distribuidas. En la arquitectura DTP, un administrador de transacciones de coordinación le dice a cada recurso cómo procesar una transacción, en función de su conocimiento de todos los recursos que participan en la transacción. Los recursos que normalmente administran su propia confirmación y recuperación de transacciones delegan esta tarea al administrador de transacciones.
La especificación XA de la arquitectura proporciona un estándar abierto que garantiza la interoperabilidad entre productos de bases de datos y middleware transaccionales conformes. Por lo tanto, estos diferentes recursos pueden participar juntos en una transacción distribuida.
El modelo DTP incluye tres componentes interrelacionados:
- Un programa de aplicación que define los límites de la transacción y especifica las acciones que constituyen una transacción.
- Administradores de recursos, como bases de datos o sistemas de archivos que brindan acceso a recursos compartidos.
- Un administrador de transacciones que asigna identificadores a las transacciones, supervisa su progreso y asume la responsabilidad de la finalización de la transacción y la recuperación de fallas.
El estándar XA define el protocolo de compromiso de dos fases y la interfaz utilizada para la comunicación entre un administrador de transacciones y un administrador de recursos. El protocolo de compromiso de dos fases proporciona una garantía de todo o nada de que todos los participantes involucrados en la transacción se comprometen o revierten juntos. Por lo tanto, toda la transacción se confirma o se retrotrae.
La confirmación de dos fases consta de una fase de preparación y una fase de confirmación. Durante la fase de preparación, todos los participantes en la transacción deben aceptar completar los cambios requeridos por la transacción. Si alguno de los participantes informa un problema, la fase de preparación fallará y la transacción se revertirá. Si la fase de preparación tiene éxito, la fase dos, comienza la fase de confirmación. Durante la fase de confirmación, el Administrador de transacciones indica a todos los participantes que confirmen la transacción.
Servidor SQL y XA
Para habilitar la compatibilidad con XA en SQL Server 2019, siga las instrucciones de la sección "Ejecución del servicio MS DTC" contenida en este documento:
Comprender las transacciones XA
Para habilitar la compatibilidad con XA en versiones anteriores de SQL Server, siga las instrucciones de este documento:
Configuración de transacciones XA en Microsoft SQL Server para IBM Business Process Manager (BPM)
El controlador ODBC de SQL Server se probó con instancias de SQL Server 2016 y 2019 habilitadas para XA.
El controlador ODBC de Easysoft SQL Server
Se agregó compatibilidad con XA al controlador ODBC de SQL Server en la versión 1.11.3. La compatibilidad con XA del controlador se probó con Oracle Tuxedo y SQL Server 2016 y 2019.
Para dar de alta el controlador ODBC de SQL Server en una transacción XA, debe usar una estructura denominada es_xa_context
en su aplicación. es_xa_context
se conecta a la fuente de datos ODBC que especificó en la configuración del administrador de recursos XA y devuelve un identificador de conexión. Por ejemplo:
int ret; SQLHANDLE hEnv, hConn; ret = es_xa_context( NULL, &hEnv, &hConn );
En Tuxedo, la fuente de datos ODBC que es_xa_context
se conecta a se especifica en el Administrador de recursos OPENINFO
cadena en el archivo de configuración de Tuxedo. En este ejemplo, es "SQLSERVER_SAMPLE":
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"
El nombre del administrador de recursos XA definido por el controlador y el conmutador XA son EASYSOFT_SQLSERVER_ODBC
y essql_xaosw
.
En Tuxedo, los especifica en el archivo de definición de Tuxedo Resource Manager, ${TUXDIR}/udataobj/RM
. Por ejemplo:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst
Aplicación Easysoft / Tuxedo / SQL Server XA de muestra
Primero, configure una fuente de datos del controlador ODBC de SQL Server que se conecte a una instancia de SQL Server habilitada para XA:
- En su máquina Tuxedo, instale el controlador ODBC de SQL Server.
- Cree una fuente de datos del controlador ODBC de SQL Server en odbc.ini. Por ejemplo:
[SQLSERVER_SAMPLE] Driver=Easysoft ODBC-SQL Server Description=Easysoft SQL Server ODBC driver Server=mymachine\myxaenabledinstance User=mydomain\myuser Password=mypassword Database=XA1
- Cree una tabla de ejemplo para la aplicación Tuxedo:
$ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)
Cree y ejecute la aplicación Tuxedo XA de muestra.
-
$ cd ~ $ mkdir simpdir $ cd simpdir $ touch simpcl.c simpserv.c ubbsimple
- Agregue estas líneas a 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); }
- Agregue estas líneas a 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); } }
- Agregue estas líneas a 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
- Configure su entorno:
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
- Cree el cliente de muestra:
buildclient -o simpcl -f simpcl.c
Si obtiene el error "referencia indefinida a dlopen" al compilar el cliente, pruebe este comando en su lugar:
buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
- Cree el servidor de muestra:
buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \ -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
- Cree el archivo TUXCONFIG para la aplicación de muestra:
tmloadcf ubbsimple
- Cree un dispositivo de registro Tuxedo para la aplicación de muestra:
$ tmadmin -c > crdl -z /home/myuser/simpdir/tuxlog -b 512
- Cree un administrador de transacciones de Tuxedo que interactúe con el controlador ODBC de SQL Server:
$ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
- Inicie el servidor de muestra:
$ tmboot
- Pruebe la aplicación de ejemplo:
./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 | +------------+--------------+
- Si ve los datos en la tabla de SQL Server, apague el servidor de muestra:
tmshutdown
De lo contrario, consulte ULOG.nnn en el directorio de la aplicación de muestra.