Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Inscription de SQL Server dans une transaction XA distribuée

Comment accéder à SQL Server dans le cadre d'une transaction XA avec le driver ODBC Easysoft SQL Server et Oracle Tuxedo.

Présentation

Pourquoi les transactions distribuées sont nécessaires

Une transaction est une série d'actions exécutées en une seule opération dans laquelle toutes les actions sont exécutées ou aucune d'entre elles ne l'est. Une transaction se termine par une action de validation qui rend les modifications permanentes. Si l'une des modifications ne peut pas être validée, la transaction sera annulée, annulant toutes les modifications.

Une transaction distribuée est une transaction qui peut s'étendre sur plusieurs ressources. Par exemple, une ou plusieurs bases de données ou une base de données et une file d'attente de messages. Pour que la transaction soit validée avec succès, toutes les ressources individuelles doivent être validées avec succès ; si l'une d'entre elles échoue, la transaction doit être annulée dans toutes les ressources. Par exemple, une transaction distribuée peut consister en un transfert d'argent entre deux comptes bancaires, hébergés par des banques différentes, et donc également sur des bases de données différentes. Vous ne voudriez pas qu'une transaction soit validée sans la garantie que les deux se termineront avec succès. Sinon, les données peuvent être dupliquées (si l'insertion se termine et la suppression échoue) ou perdues (si la suppression se termine et l'insertion échoue).

Chaque fois qu'une application a besoin d'accéder ou de mettre à jour les données dans plusieurs ressources transactionnelles, elle doit donc utiliser une transaction distribuée. Il est possible d'utiliser une transaction distincte sur chacune des ressources, mais cette approche est sujette aux erreurs. Si la transaction d'une ressource est validée avec succès mais qu'une autre échoue et doit être annulée, la première transaction ne peut plus être annulée, de sorte que l'état de l'application devient incohérent. Si une ressource est validée avec succès mais que le système plante avant que l'autre ressource ne puisse être validée avec succès, l'application est à nouveau incohérente.

XA

Le modèle X/Open Distributed Transaction Processing (DTP) définit une architecture pour le traitement distribué des transactions. Dans l'architecture DTP, un gestionnaire de transaction coordonnateur indique à chaque ressource comment traiter une transaction, en fonction de sa connaissance de toutes les ressources participant à la transaction. Les ressources qui gèrent normalement leur propre validation et récupération de transaction délèguent cette tâche au gestionnaire de transactions.

La spécification XA de l'architecture fournit une norme ouverte qui garantit l'interopérabilité entre les intergiciels transactionnels et les produits de base de données conformes. Ces différentes ressources sont donc capables de participer ensemble à une transaction distribuée.

Le modèle DTP comprend trois composants interdépendants :

  • Un programme d'application qui définit les limites des transactions et spécifie les actions qui constituent une transaction.
  • Des gestionnaires de ressources tels que des bases de données ou des systèmes de fichiers qui permettent d'accéder à des ressources partagées.
  • Un gestionnaire de transactions qui attribue des identifiants aux transactions, surveille leur progression et assume la responsabilité de l'achèvement des transactions et de la reprise après échec.

La norme XA définit le protocole de validation en deux phases et l'interface utilisée pour la communication entre un gestionnaire de transactions et un gestionnaire de ressources. Le protocole de validation en deux phases fournit une garantie tout ou rien que tous les participants impliqués dans la transaction valident ou annulent ensemble. Par conséquent, la transaction entière est validée ou la transaction entière est annulée.

La validation en deux phases consiste en une phase de préparation et une phase de validation. Au cours de la phase de préparation, tous les participants à la transaction doivent accepter d'effectuer les modifications requises par la transaction. Si l'un des participants signale un problème, la phase de préparation échouera et la transaction sera annulée. Si la phase de préparation réussit, la phase deux, la phase de validation commence. Au cours de la phase de validation, le gestionnaire de transactions demande à tous les participants de valider la transaction.

SQL Server et XA

Pour activer la prise en charge de XA dans SQL Server 2019, suivez les instructions de la section "Exécution du service MS DTC" contenue dans ce document :

Comprendre les transactions XA

Pour activer la prise en charge de XA dans les versions antérieures de SQL Server, suivez les instructions de ce document :

Configuration des transactions XA dans Microsoft SQL Server pour IBM Business Process Manager (BPM)

Le pilote ODBC SQL Server a été testé avec des instances SQL Server 2016 et 2019 compatibles XA.

Le pilote ODBC Easysoft SQL Server

La prise en charge de XA a été ajoutée au pilote ODBC SQL Server dans la version 1.11.3. La prise en charge XA du pilote a été testée avec Oracle Tuxedo et SQL Server 2016 et 2019.

Pour inscrire le pilote ODBC SQL Server dans une transaction XA, vous devez utiliser une structure nommée es_xa_context dans votre candidature. es_xa_context se connecte à la source de données ODBC que vous avez spécifiée dans la configuration de votre gestionnaire de ressources XA et renvoie un descripteur de connexion. Par exemple :

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

Dans Tuxedo, la source de données ODBC qui es_xa_context se connecte à est spécifié dans le gestionnaire de ressources OPENINFO chaîne dans le fichier de configuration de Tuxedo. Dans cet exemple, c'est "SQLSERVER_SAMPLE":

OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"

Le nom du gestionnaire de ressources XA et le commutateur XA définis par le pilote sont EASYSOFT_SQLSERVER_ODBC et essql_xaosw .

Dans Tuxedo, vous les spécifiez dans le fichier de définition de Tuxedo Resource Manager, ${TUXDIR}/udataobj/RM . Par exemple :

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

Exemple d'application Easysoft / Tuxedo / SQL Server XA

Tout d'abord, configurez une source de données de pilote ODBC SQL Server qui se connecte à une instance SQL Server compatible XA :

  1. Sur votre ordinateur Tuxedo, installez le pilote ODBC SQL Server.
  2. Créez une source de données de pilote ODBC SQL Server dans odbc.ini. Par exemple :
    [SQLSERVER_SAMPLE]
    Driver=Easysoft ODBC-SQL Server
    Description=Easysoft SQL Server ODBC driver
    Server=mymachine\myxaenabledinstance
    User=mydomain\myuser
    Password=mypassword
    Database=XA1
  3. Créez un exemple de table pour l'application Tuxedo :
    $ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE
    SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)

Créez et exécutez l'exemple d'application Tuxedo XA.

  1. $ cd ~
    $ mkdir simpdir
    $ cd simpdir
    $ touch simpcl.c simpserv.c ubbsimple
  2. Ajoutez ces lignes à 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. Ajoutez ces lignes à 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. Ajoutez ces lignes à 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. Définissez votre environnement :
    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. Construisez l'exemple de client :
    buildclient -o simpcl -f simpcl.c

    Si vous obtenez l'erreur "référence non définie à dlopen" lors de la construction du client, essayez plutôt cette commande :

    buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
  7. Construisez l'exemple de serveur :
    buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \
    -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
  8. Créez le fichier TUXCONFIG pour l'exemple d'application :
    tmloadcf ubbsimple
  9. Créez un périphérique de journalisation Tuxedo pour l'exemple d'application :
    $ tmadmin -c
    > crdl -z /home/myuser/simpdir/tuxlog -b 512
  10. Créez un gestionnaire de transactions Tuxedo qui s'interface avec le pilote ODBC de SQL Server :
    $ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
  11. Démarrez le serveur d'exemple :
    $ tmboot
  12. Testez l'exemple d'application :
    ./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 vous voyez les données dans la table SQL Server, arrêtez l'exemple de serveur :
    tmshutdown

    Sinon, consultez ULOG.nnn dans le répertoire de l'application exemple.