Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Arruolamento di SQL Server in una transazione XA distribuita

Come accedere a SQL Server nel contesto di una transazione XA con il driver ODBC di Easysoft SQL Server e Oracle Tuxedo.

Introduzione

Perché sono necessarie le transazioni distribuite

Una transazione è una serie di azioni eseguite come una singola operazione in cui vengono eseguite tutte le azioni o nessuna di esse. Una transazione termina con un'azione di commit che rende permanenti le modifiche. Se non è possibile eseguire il commit di una qualsiasi delle modifiche, la transazione verrà ripristinata, annullando tutte le modifiche.

Una transazione distribuita è una transazione che può estendersi su più risorse. Ad esempio, uno o più database o un database e una coda di messaggi. Affinché la transazione si impegni correttamente, tutte le singole risorse devono impegnarsi correttamente; se qualcuno di essi non riesce, la transazione deve eseguire il rollback di tutte le risorse. Ad esempio, una transazione distribuita potrebbe consistere in un trasferimento di denaro tra due conti bancari, ospitati da banche diverse, e quindi anche su database diversi. Non vorresti che nessuna transazione fosse impegnata senza la garanzia che entrambe si completeranno con successo. In caso contrario, i dati potrebbero essere duplicati (se l'inserimento viene completato e l'eliminazione non riesce) o persi (se l'eliminazione viene completata e l'inserimento non riesce).

Pertanto, ogni volta che un'applicazione deve accedere o aggiornare i dati in più risorse transazionali, dovrebbe utilizzare una transazione distribuita. È possibile utilizzare una transazione separata su ciascuna delle risorse, ma questo approccio è soggetto a errori. Se la transazione in una risorsa viene eseguita correttamente ma un'altra ha esito negativo e deve essere eseguito il rollback, non è più possibile eseguire il rollback della prima transazione, quindi lo stato dell'applicazione diventa incoerente. Se una risorsa viene eseguita correttamente ma il sistema si arresta in modo anomalo prima che l'altra risorsa possa eseguire correttamente il commit, l'applicazione è di nuovo incoerente.

XA

Il modello X/Open Distributed Transaction Processing (DTP) definisce un'architettura per l'elaborazione delle transazioni distribuite. Nell'architettura DTP, un gestore delle transazioni di coordinamento indica a ciascuna risorsa come elaborare una transazione, in base alla sua conoscenza di tutte le risorse che partecipano alla transazione. Le risorse che normalmente gestiscono il commit e il ripristino delle proprie transazioni delegano questa attività al gestore delle transazioni.

La specifica XA dell'architettura fornisce uno standard aperto che garantisce l'interoperabilità tra middleware transazionale conforme e prodotti di database. Queste diverse risorse sono quindi in grado di partecipare insieme a una transazione distribuita.

Il modello DTP include tre componenti correlati:

  • Un programma applicativo che definisce i limiti di transazione e specifica le azioni che costituiscono una transazione.
  • Gestione risorse come database o file system che forniscono l'accesso alle risorse condivise.
  • Un Transaction Manager che assegna identificatori alle transazioni, ne monitora l'andamento e si assume la responsabilità del completamento delle transazioni e del ripristino degli errori.

Lo standard XA definisce il protocollo di commit a due fasi e l'interfaccia utilizzata per la comunicazione tra un Transaction Manager e un Resource Manager. Il protocollo di commit in due fasi fornisce una garanzia "tutto o niente" che tutti i partecipanti coinvolti nella transazione eseguiranno il commit o il rollback insieme. L'intera transazione esegue il commit o l'intera transazione viene ripristinata, quindi.

Il commit a due fasi consiste in una fase di preparazione e una fase di commit. Durante la fase di preparazione, tutti i partecipanti alla transazione devono accettare di completare le modifiche richieste dalla transazione. Se uno dei partecipanti segnala un problema, la fase di preparazione avrà esito negativo e la transazione verrà annullata. Se la fase di preparazione ha esito positivo, fase due, inizia la fase di commit. Durante la fase di commit, il Transaction Manager istruisce tutti i partecipanti a eseguire il commit della transazione.

SQL Server e XA

Per abilitare il supporto XA in SQL Server 2019, segui le istruzioni nella sezione "Esecuzione del servizio MS DTC" contenuta in questo documento:

Comprensione delle transazioni XA

Per abilitare il supporto XA nelle versioni precedenti di SQL Server, segui le istruzioni in questo documento:

Configurazione delle transazioni XA in Microsoft SQL Server per IBM Business Process Manager (BPM)

Il driver ODBC di SQL Server è stato testato con istanze di SQL Server 2016 e 2019 abilitate per XA.

Il driver ODBC di Easysoft SQL Server

Il supporto XA è stato aggiunto al driver ODBC di SQL Server nella versione 1.11.3. Il supporto XA del driver è stato testato con Oracle Tuxedo e SQL Server 2016 e 2019.

Per integrare il driver ODBC di SQL Server in una transazione XA, è necessario utilizzare una struttura denominata es_xa_context nella tua applicazione. es_xa_context si connette all'origine dati ODBC specificata nella configurazione del gestore risorse XA e restituisce un handle di connessione. Ad esempio:

int ret;
SQLHANDLE hEnv, hConn;
ret = es_xa_context( NULL, &hEnv, &hConn );

In Tuxedo, l'origine dati ODBC che es_xa_context si connette è specificato in Gestione risorse OPENINFO stringa nel file di configurazione di Tuxedo. In questo esempio, è "SQLSERVER_SAMPLE":

OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"

Il nome di XA Resource Manager e l'opzione XA definiti dal driver sono EASYSOFT_SQLSERVER_ODBC e essql_xaosw .

In Tuxedo, li specifichi nel file di definizione di Tuxedo Resource Manager, ${TUXDIR}/udataobj/RM . Ad esempio:

EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst

Esempio di applicazione Easysoft / Tuxedo / SQL Server XA

Innanzitutto, configura un'origine dati del driver ODBC di SQL Server che si connette a un'istanza di SQL Server abilitata per XA:

  1. Sul tuo computer Tuxedo, installa il driver ODBC di SQL Server.
  2. Crea un'origine dati del driver ODBC di SQL Server in odbc.ini. Ad esempio:
    [SQLSERVER_SAMPLE]
    Driver=Easysoft ODBC-SQL Server
    Description=Easysoft SQL Server ODBC driver
    Server=mymachine\myxaenabledinstance
    User=mydomain\myuser
    Password=mypassword
    Database=XA1
  3. Crea una tabella di esempio per l'applicazione Tuxedo:
    $ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE
    SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)

Crea ed esegui l'applicazione di esempio Tuxedo XA.

  1. $ cd ~
    $ mkdir simpdir
    $ cd simpdir
    $ touch simpcl.c simpserv.c ubbsimple
  2. Aggiungi queste righe 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. Aggiungi queste righe 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. Aggiungi queste righe 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. Imposta il tuo 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. Crea il client di esempio:
    buildclient -o simpcl -f simpcl.c

    Se ricevi l'errore "riferimento non definito a dlopen" durante la creazione del client, prova invece questo comando:

    buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
  7. Crea il server di esempio:
    buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \
    -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
  8. Crea il file TUXCONFIG per l'applicazione di esempio:
    tmloadcf ubbsimple
  9. Crea un dispositivo di registrazione Tuxedo per l'applicazione di esempio:
    $ tmadmin -c
    > crdl -z /home/myuser/simpdir/tuxlog -b 512
  10. Crea un gestore delle transazioni Tuxedo che si interfaccia con il driver ODBC di SQL Server:
    $ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
  11. Avvia il server di esempio:
    $ tmboot
  12. Testare l'applicazione di esempio:
    ./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 vedi i dati nella tabella di SQL Server, spegni il server di esempio:
    tmshutdown

    Altrimenti, consulta ULOG.nnn nella directory dell'applicazione di esempio.