Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Eintragen von SQL Server in eine verteilte XA-Transaktion

Zugriff auf SQL Server im Kontext einer XA-Transaktion mit dem Easysoft SQL Server ODBC-Treiber und Oracle Tuxedo.

Einführung

Warum verteilte Transaktionen benötigt werden

Eine Transaktion ist eine Reihe von Aktionen, die als einzelne Operation ausgeführt werden, bei der entweder alle Aktionen oder keine ausgeführt werden. Eine Transaktion endet mit einer Commit-Aktion, die die Änderungen dauerhaft macht. Wenn eine der Änderungen nicht festgeschrieben werden kann, wird die Transaktion rückgängig gemacht und alle Änderungen rückgängig gemacht.

Eine verteilte Transaktion ist eine Transaktion, die mehrere Ressourcen umfassen kann. Beispielsweise eine oder mehrere Datenbanken oder eine Datenbank und eine Nachrichtenwarteschlange. Damit die Transaktion erfolgreich festgeschrieben wird, müssen alle einzelnen Ressourcen erfolgreich festgeschrieben werden; Wenn einer von ihnen nicht erfolgreich ist, muss die Transaktion in allen Ressourcen rückgängig gemacht werden. Beispielsweise kann eine verteilte Transaktion aus einer Geldüberweisung zwischen zwei Bankkonten bestehen, die von verschiedenen Banken gehostet werden, und somit auch auf verschiedenen Datenbanken. Sie möchten nicht, dass eine der beiden Transaktionen ohne eine Garantie, dass beide erfolgreich abgeschlossen werden, festgeschrieben wird. Andernfalls können Daten dupliziert werden (wenn das Einfügen abgeschlossen ist und das Löschen fehlschlägt) oder verloren gehen (wenn das Löschen abgeschlossen ist und das Einfügen fehlschlägt).

Wann immer eine Anwendung auf die Daten in mehreren Transaktionsressourcen zugreifen oder diese aktualisieren muss, sollte sie daher eine verteilte Transaktion verwenden. Es ist möglich, für jede der Ressourcen eine separate Transaktion zu verwenden, aber dieser Ansatz ist fehleranfällig. Wenn die Transaktion in einer Ressource erfolgreich festgeschrieben wird, eine andere jedoch fehlschlägt und zurückgesetzt werden muss, kann die erste Transaktion nicht mehr zurückgesetzt werden, sodass der Status der Anwendung inkonsistent wird. Wenn eine Ressource erfolgreich festschreibt, aber das System abstürzt, bevor die andere Ressource erfolgreich festschreiben kann, ist die Anwendung wieder inkonsistent.

XA

Das X/Open Distributed Transaction Processing (DTP)-Modell definiert eine Architektur für die verteilte Transaktionsverarbeitung. In der DTP-Architektur teilt ein koordinierender Transaktionsmanager jeder Ressource mit, wie eine Transaktion zu verarbeiten ist, basierend auf seinem Wissen über alle an der Transaktion beteiligten Ressourcen. Ressourcen, die normalerweise ihre eigene Transaktionsfestschreibung und -wiederherstellung verwalten, delegieren diese Aufgabe an den Transaktionsmanager.

Die XA-Spezifikation der Architektur bietet einen offenen Standard, der die Interoperabilität zwischen konformen transaktionalen Middleware- und Datenbankprodukten gewährleistet. Diese unterschiedlichen Ressourcen können daher gemeinsam an einer verteilten Transaktion teilnehmen.

Das DTP-Modell umfasst drei miteinander verbundene Komponenten:

  • Ein Anwendungsprogramm, das Transaktionsgrenzen definiert und Aktionen spezifiziert, die eine Transaktion darstellen.
  • Ressourcenmanager wie Datenbanken oder Dateisysteme, die Zugriff auf gemeinsam genutzte Ressourcen bieten.
  • Ein Transaktionsmanager, der Transaktionen Kennungen zuweist, ihren Fortschritt überwacht und die Verantwortung für den Abschluss der Transaktion und die Fehlerbehebung übernimmt.

Der XA-Standard definiert das Zwei-Phasen-Commit-Protokoll und die Schnittstelle, die für die Kommunikation zwischen einem Transaktionsmanager und einem Ressourcenmanager verwendet wird. Das zweiphasige Commit-Protokoll bietet eine Alles-oder-Nichts-Garantie, dass alle an der Transaktion beteiligten Teilnehmer gemeinsam entweder Commit oder Rollback durchführen. Daher wird die gesamte Transaktion festgeschrieben oder die gesamte Transaktion zurückgesetzt.

Das zweiphasige Commit besteht aus einer Vorbereitungsphase und einer Commit-Phase. Während der Vorbereitungsphase müssen alle an der Transaktion Beteiligten zustimmen, die für die Transaktion erforderlichen Änderungen vorzunehmen. Wenn einer der Teilnehmer ein Problem meldet, schlägt die Vorbereitungsphase fehl und die Transaktion wird rückgängig gemacht. Wenn die Vorbereitungsphase erfolgreich ist, Phase zwei, beginnt die Commit-Phase. Während der Festschreibungsphase weist der Transaktionsmanager alle Teilnehmer an, die Transaktion festzuschreiben.

SQL-Server und XA

Um die XA-Unterstützung in SQL Server 2019 zu aktivieren, befolgen Sie die Anweisungen im Abschnitt „Ausführen des MS DTC-Dienstes“ in diesem Dokument:

XA-Transaktionen verstehen

Befolgen Sie die Anweisungen in diesem Dokument, um die XA-Unterstützung in früheren Versionen von SQL Server zu aktivieren:

Konfigurieren von XA-Transaktionen in Microsoft SQL Server für IBM Business Process Manager (BPM)

Der SQL Server ODBC-Treiber wurde mit XA-fähigen SQL Server 2016- und 2019-Instanzen getestet.

Der Easysoft SQL Server ODBC-Treiber

XA-Unterstützung wurde dem SQL Server-ODBC-Treiber in Version 1.11.3 hinzugefügt. Die XA-Unterstützung des Treibers wurde mit Oracle Tuxedo und SQL Server 2016 und 2019 getestet.

Um den SQL Server-ODBC-Treiber in eine XA-Transaktion einzutragen, müssen Sie eine Struktur namens es_xa_context verwenden in Ihrer Bewerbung. es_xa_context stellt eine Verbindung zu der ODBC-Datenquelle her, die Sie in Ihrer XA-Ressourcenmanagerkonfiguration angegeben haben, und gibt ein Verbindungshandle zurück. Beispiel:

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

In Tuxedo die ODBC-Datenquelle, die es_xa_context Verbindung zu wird im Ressourcenmanager OPENINFO angegeben Zeichenfolge in der Tuxedo-Konfigurationsdatei. In diesem Beispiel ist es „SQLSERVER_SAMPLE“:

OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"

Der treiberdefinierte XA-Ressourcen-Manager-Name und XA-Schalter sind EASYSOFT_SQLSERVER_ODBC und essql_xaosw .

In Tuxedo geben Sie diese in der Tuxedo Resource Manager-Definitionsdatei ${TUXDIR}/udataobj/RM an . Zum Beispiel:

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

Beispielanwendung für Easysoft / Tuxedo / SQL Server XA

Richten Sie zunächst eine SQL Server-ODBC-Treiberdatenquelle ein, die eine Verbindung zu einer XA-fähigen SQL Server-Instanz herstellt:

  1. Installieren Sie auf Ihrem Tuxedo-Computer den SQL Server ODBC-Treiber.
  2. Erstellen Sie eine SQL Server-ODBC-Treiberdatenquelle in odbc.ini. Beispiel:
    [SQLSERVER_SAMPLE]
    Driver=Easysoft ODBC-SQL Server
    Description=Easysoft SQL Server ODBC driver
    Server=mymachine\myxaenabledinstance
    User=mydomain\myuser
    Password=mypassword
    Database=XA1
  3. Erstellen Sie eine Beispieltabelle für die Tuxedo-Anwendung:
    $ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE
    SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)

Erstellen Sie die Tuxedo XA-Beispielanwendung und führen Sie sie aus.

  1. $ cd ~
    $ mkdir simpdir
    $ cd simpdir
    $ touch simpcl.c simpserv.c ubbsimple
  2. Fügen Sie diese Zeilen zu simpcl.c hinzu:
    #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. Fügen Sie diese Zeilen zu simpserv.c hinzu:
    #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. Fügen Sie diese Zeilen zu ubbsimple hinzu:
    *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. Stellen Sie Ihre Umgebung ein:
    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. Erstellen Sie den Beispielclient:
    buildclient -o simpcl -f simpcl.c

    Wenn Sie beim Erstellen des Clients den Fehler "undefinierte Referenz auf dlopen" erhalten, versuchen Sie stattdessen diesen Befehl:

    buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
  7. Erstellen Sie den Beispielserver:
    buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \
    -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
  8. Erstellen Sie die TUXCONFIG-Datei für die Beispielanwendung:
    tmloadcf ubbsimple
  9. Erstellen Sie ein Tuxedo-Protokollierungsgerät für die Beispielanwendung:
    $ tmadmin -c
    > crdl -z /home/myuser/simpdir/tuxlog -b 512
  10. Erstellen Sie einen Tuxedo-Transaktionsmanager, der mit dem SQL Server-ODBC-Treiber interagiert:
    $ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
  11. Starten Sie den Beispielserver:
    $ tmboot
  12. Testen Sie die Beispielanwendung:
    ./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. Wenn Sie die Daten in der SQL Server-Tabelle sehen, fahren Sie den Beispielserver herunter:
    tmshutdown

    Konsultieren Sie andernfalls ULOG.nnn im Musteranwendungsverzeichnis.