Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Inscrevendo o SQL Server em uma transação XA distribuída


Como acessar o SQL Server no contexto de uma transação XA com o driver ODBC Easysoft SQL Server e Oracle Tuxedo.

Introdução

Por que as transações distribuídas são necessárias


Uma transação é uma série de ações executadas como uma única operação na qual todas as ações são executadas ou nenhuma delas é executada. Uma transação termina com uma ação de confirmação que torna as alterações permanentes. Se alguma das alterações não puder ser confirmada, a transação será revertida, revertendo todas as alterações.

Uma transação distribuída é uma transação que pode abranger vários recursos. Por exemplo, um ou mais bancos de dados ou um banco de dados e uma fila de mensagens. Para que a transação seja confirmada com êxito, todos os recursos individuais devem ser confirmados com êxito; se algum deles for malsucedido, a transação deverá ser revertida em todos os recursos. Por exemplo, uma transação distribuída pode consistir em uma transferência de dinheiro entre duas contas bancárias, hospedadas por bancos diferentes e, portanto, também em bancos de dados diferentes. Você não deseja que nenhuma das transações seja confirmada sem a garantia de que ambas serão concluídas com êxito. Caso contrário, os dados podem ser duplicados (se a inserção for concluída e a exclusão falhar) ou perdidas (se a exclusão for concluída e a inserção falhar).

Sempre que um aplicativo precisar acessar ou atualizar os dados em vários recursos transacionais, portanto, ele deve usar uma transação distribuída. É possível usar uma transação separada em cada um dos recursos, mas essa abordagem é propensa a erros. Se a transação em um recurso for confirmada com êxito, mas outra falhar e precisar ser revertida, a primeira transação não poderá mais ser revertida, portanto, o estado do aplicativo se tornará inconsistente. Se um recurso for confirmado com êxito, mas o sistema travar antes que o outro recurso possa ser confirmado com êxito, o aplicativo novamente será inconsistente.

XA


O modelo X/Open Distributed Transaction Processing (DTP) define uma arquitetura para processamento distribuído de transações. Na arquitetura DTP, um gerente de transação coordenador informa a cada recurso como processar uma transação, com base em seu conhecimento de todos os recursos participantes da transação. Os recursos que normalmente gerenciam sua própria confirmação e recuperação de transações delegam essa tarefa ao gerenciador de transações.

A especificação XA da arquitetura fornece um padrão aberto que garante a interoperabilidade entre middleware transacional compatível e produtos de banco de dados. Esses diferentes recursos podem, portanto, participar juntos de uma transação distribuída.

O modelo DTP inclui três componentes inter-relacionados:
  • Um Programa Aplicativo que define os limites da transação e especifica as ações que constituem uma transação.
  • Gerenciadores de recursos, como bancos de dados ou sistemas de arquivos que fornecem acesso a recursos compartilhados.
  • Um gerenciador de transações que atribui identificadores a transações, monitora seu progresso e assume a responsabilidade pela conclusão da transação e recuperação de falhas.

O padrão XA define o protocolo de confirmação de duas fases e a interface usada para comunicação entre um Transaction Manager e um Resource Manager. O protocolo de confirmação de duas fases fornece uma garantia de tudo ou nada de que todos os participantes envolvidos na transação confirmam ou revertem juntos. A transação inteira é confirmada ou a transação inteira é revertida, portanto.

A confirmação de duas fases consiste em uma fase de preparação e uma fase de confirmação. Durante a fase de preparação, todos os participantes da transação devem concordar em concluir as alterações exigidas pela transação. Se algum dos participantes relatar um problema, a fase de preparação falhará e a transação será revertida. Se a fase de preparação for bem-sucedida, fase dois, a fase de confirmação será iniciada. Durante a fase de confirmação, o Transaction Manager instrui todos os participantes a confirmar a transação.

SQL Server e XA


Para habilitar o suporte a XA no SQL Server 2019, siga as instruções na seção "Executando o serviço MS DTC" contida neste documento:

Entendendo as transações XA

Para habilitar o suporte a XA em versões anteriores do SQL Server, siga as instruções neste documento:

Configurando transações XA no Microsoft SQL Server para IBM Business Process Manager (BPM)

O driver ODBC do SQL Server foi testado com instâncias do SQL Server 2016 e 2019 habilitadas para XA.

O driver ODBC do Easysoft SQL Server


O suporte a XA foi adicionado ao driver ODBC do SQL Server na versão 1.11.3. O suporte XA do driver foi testado com Oracle Tuxedo e SQL Server 2016 e 2019.

Para inscrever o driver ODBC do SQL Server em uma transação XA, você precisa usar uma estrutura chamada es_xa_context em seu aplicativo. es_xa_context conecta-se à fonte de dados ODBC que você especificou na configuração do gerenciador de recursos XA e retorna um identificador de conexão. Por exemplo:
int ret;
SQLHANDLE hEnv, hConn;
ret = es_xa_context( NULL, &hEnv, &hConn );

No Tuxedo, a fonte de dados ODBC que es_xa_context se conecta é especificado no Gerenciador de Recursos OPENINFO string no arquivo de configuração do Tuxedo. Neste exemplo, é "SQLSERVER_SAMPLE":
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"

O nome do gerenciador de recursos XA definido pelo driver e a opção XA são EASYSOFT_SQLSERVER_ODBC e essql_xaosw .
No Tuxedo, você especifica isso no arquivo de definição do Tuxedo Resource Manager, ${TUXDIR}/udataobj/RM . Por exemplo:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst

Exemplo de aplicativo Easysoft / Tuxedo / SQL Server XA


Primeiro, configure uma fonte de dados do driver ODBC do SQL Server que se conecta a uma instância do SQL Server habilitada para XA:
  1. Na sua máquina Tuxedo, instale o driver ODBC do SQL Server.
  2. Crie uma fonte de dados do driver ODBC do SQL Server em odbc.ini. Por exemplo:
    [SQLSERVER_SAMPLE]
    Driver=Easysoft ODBC-SQL Server
    Description=Easysoft SQL Server ODBC driver
    Server=mymachine\myxaenabledinstance
    User=mydomain\myuser
    Password=mypassword
    Database=XA1
  3. Crie uma tabela de amostra para o aplicativo Tuxedo:
    $ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE
    SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)

Crie e execute o aplicativo Tuxedo XA de amostra.
  1. $ cd ~
    $ mkdir simpdir
    $ cd simpdir
    $ touch simpcl.c simpserv.c ubbsimple
  2. Adicione estas linhas ao 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. Adicione estas linhas 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. Adicione estas linhas ao 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. Defina seu ambiente:
    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. Crie o cliente de amostra:
    buildclient -o simpcl -f simpcl.c

    Se você receber o erro "referência indefinida para dlopen" ao compilar o cliente, tente este comando:
    buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
  7. Crie o servidor de amostra:
    buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \
    -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
  8. Crie o arquivo TUXCONFIG para o aplicativo de amostra:
    tmloadcf ubbsimple
  9. Crie um dispositivo de log Tuxedo para o aplicativo de amostra:
    $ tmadmin -c
    > crdl -z /home/myuser/simpdir/tuxlog -b 512
  10. Crie um gerenciador de transações Tuxedo que faça interface com o driver ODBC do SQL Server:
    $ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
  11. Inicie o servidor de amostra:
    $ tmboot
  12. Teste o aplicativo de amostra:
    ./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. Se você vir os dados na tabela do SQL Server, desligue o servidor de amostra:
    tmshutdown

    Caso contrário, consulte ULOG.nnn no diretório de aplicativos de amostra.