sql >> Base de Datos >  >> RDS >> Sqlserver

Inscripción de SQL Server en una transacción XA distribuida

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:

  1. En su máquina Tuxedo, instale el controlador ODBC de SQL Server.
  2. 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
  3. 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.

  1. $ cd ~
    $ mkdir simpdir
    $ cd simpdir
    $ touch simpcl.c simpserv.c ubbsimple
  2. 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);
    }
  3. 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);
            }
    }
  4. 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
  5. 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
  6. 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"
  7. 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"
  8. Cree el archivo TUXCONFIG para la aplicación de muestra:
    tmloadcf ubbsimple
  9. Cree un dispositivo de registro Tuxedo para la aplicación de muestra:
    $ tmadmin -c
    > crdl -z /home/myuser/simpdir/tuxlog -b 512
  10. 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
  11. Inicie el servidor de muestra:
    $ tmboot
  12. 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  |                                                                                         
    +------------+--------------+
  13. 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.