PostgreSQL La base de donnees la plus sophistiquee au monde.

Forums PostgreSQL.fr

Le forum officiel de la communauté francophone de PostgreSQL

Vous n'êtes pas identifié(e).

#1 01/09/2022 08:41:11

Gwajo
Membre

Résolu : transactions distribuées - two phase commit

Bonjour,

Je mets en place une gestion de transactions distribuées sur des instances postgres,
par contre je me heurte à une problématique quand j'exécute la séquence  de code csql suivante :
Le code est volontairement simplifié afin de rentrer facilement dans ce code :

utilisation de la version 12.3 de postresql.


#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
    EXEC SQL BEGIN DECLARE SECTION;
    int v_key, v_val_ind;
    char v_val[81];
    const char *stmt = "INSERT INTO atable VALUES(?, ?);";
    char sql_text[400];
    char idtx[40];
    EXEC SQL END DECLARE SECTION;

    /* connect to the database */
    EXEC SQL CONNECT TO "db1@vm1:8232" AS one USER "user1";

    /* connect to the database */
    EXEC SQL CONNECT TO "db2@vm1:8232" AS two USER "user1";
   
    EXEC SQL SET CONNECTION TO two;
    sprintf( sql_text, "PREPARE TRANSACTION 'toto' " );
    EXEC SQL EXECUTE IMMEDIATE :sql_text ;

    EXEC SQL SET CONNECTION TO two;
    sprintf( sql_text, "COMMIT PREPARED 'toto' " );
    EXEC SQL EXECUTE IMMEDIATE :sql_text ;

}

Message d'erreur dans la log de l'instance
2022-09-01 08:30:59 CEST [679]: [3-1] LOG:  duration: 0.150 ms  statement: begin transaction
2022-09-01 08:30:59 CEST [679]: [4-1] LOG:  duration: 3.674 ms  statement: PREPARE TRANSACTION 'toto'
2022-09-01 08:30:59 CEST [679]: [5-1] LOG:  duration: 0.019 ms  statement: begin transaction
2022-09-01 08:30:59 CEST [679]: [6-1] ERROR:  COMMIT PREPARED cannot run inside a transaction block
2022-09-01 08:30:59 CEST [679]: [7-1] STATEMENT:  COMMIT PREPARED 'toto'

Le but à terme c'est que l'identifiant de transaction nommé "toto" ne soit plus fixé mais paramétrable.
sprintf( sql_text, "COMMIT PREPARED '%s' ", idGlobalTx );
EXEC SQL EXECUTE IMMEDIATE :sql_text ;

Merci de votre aide.

Dernière modification par Gwajo (07/09/2022 21:56:44)

Hors ligne

#2 01/09/2022 09:45:06

rjuju
Administrateur

Re : Résolu : transactions distribuées - two phase commit

Bonjour,


Si vous pouvez effectuer un PREPARE TRANSACTION sans avoir explicitement démarré une transaction, c'est que la connexion n'est pas en autocommit (ce qui est le défaut avec ecpg).  Et dans ce cas le COMMIT PREPARED se retrouve également dans une transaction.


Quel est le but de la connexion "one"?  Peut être voulez vous utiliser celle-ci spécifiquement pour valider les transactions, et dans ce cas vous devriez effectuer au préalable un EXEC SQL SET AUTOCOMMIT TO ON sur cette connexion ?  Sinon, il vous faudra changer l'autocommit explicitement à chaque fois.

Hors ligne

#3 02/09/2022 09:23:15

Gwajo
Membre

Re : Résolu : transactions distribuées - two phase commit

Bonjour,

Merci pour les premières pistes.
Le but de la cnx ONE est de parcourir un curseur qui est conservé au delà des transactions effectuées sur la cnx TWO ( curseur sans WITH HOLD puisqu'on est en XA).

Le EXECUTE IMMEDIATE sur un statement est fait pour variabiliser le transaction_id de la transaction globale.
Le comportement du EXECUTE IMMEDIATE est diffèrent du EXEC SQL COMMIT PREPARED. EXEC SQL COMMIT PREPARED fonctionne normalement alors que l EXEC SQL EXECUTE IMMEDIATE ouvre une transaction tout seul.

Hors ligne

#4 02/09/2022 10:11:03

rjuju
Administrateur

Re : Résolu : transactions distribuées - two phase commit

EXEC SQL COMMIT PREPARED fonctionne normalement alors que l EXEC SQL EXECUTE IMMEDIATE ouvre une transaction tout seul.

Oui, et comme indiqué dans mon précédent message ce comportement est configurable avec EXEC SQL SET AUTOCOMMIT.  Activez l'autocommit avant d'exécuter un COMMIT PREPARED dynamique et réactivez le après, ou utilisez une connexion dédiée avec l'autocommit toujours activé pour valider ou annuler les transactions préparées.

Hors ligne

#5 02/09/2022 10:45:26

Gwajo
Membre

Re : Résolu : transactions distribuées - two phase commit

Oui c'est ce que nous avons fait.

   sprintf( sql_text, "PREPARE TRANSACTION '%s' ", idtx );
    EXEC SQL EXECUTE IMMEDIATE :sql_text ;

    EXEC SQL SET AUTOCOMMIT TO ON;
    sprintf( sql_text, "COMMIT PREPARED '%s' ", idtx );
    EXEC SQL EXECUTE IMMEDIATE :sql_text ;
    EXEC SQL SET AUTOCOMMIT TO OFF;
    EXEC SQL ROLLBACK; --pour eviter  unexpected EOF on client connection with an open transaction

Par contre  j'ai encore du mal a comprendre la philosophie que: l'autocommit n'ouvre pas une transaction explicite alors qu'un ordre EXEC SQL sans BEGIN WORK pour ouvrir un transaction, va ouvrir de lui même une transaction. J'aurai pensé que c'était l'inverse.

Merci encore pour votre aide.

Hors ligne

Pied de page des forums