libndrxxapq — Enduro/X PostgreSQL PQ XA Driver
libndrxxapq.so libndrxxapq.dylib
NDRX_XA_RES_ID=1 NDRX_XA_OPEN_STR={"url":"tcp:postgresql://${EX_PG_HOST}/${EX_PG_DB}" ,"user":"${EX_PG_USER}" ,"password":"${EX_PG_PASS}" ,"compat":"PGSQL|INFORMIX|INFORMIX_SE" }" NDRX_XA_CLOSE_STR=$NDRX_XA_OPEN_STR NDRX_XA_DRIVERLIB=libndrxxapq.so NDRX_XA_RMLIB=- NDRX_XA_LAZY_INIT=1
This is XA driver specifically written Enduro/X needs. It provides an XA switch emulation on top of PostgreSQL prepared transactions. PostgreSQL by default does not support XA switch. Also when transaction start, there is no possibility to identify the work unit performed. The identification of work done by some process on particular connection can be done by preparing the transaction. Thus there is no such thing as "active" transaction in terms of XA specification. Also there is no possibility for other processes to join the existing work and see work done by other session. Thus for example if one server process in same transaction performs some insert and other process tries insert on table which has foreign key to first insert, it will fail, as FK will not be seen. Thus Enduro/X needs to work on branch-transactions mode without join feature. The mode of PostgreSQL driver is the same as enabled by NDRX_XA_FLAG with value NOJOIN.
The emulation of XA protocol is done by following steps and assumptions:
The connection details are encoded in JSON based string which contains the database URL, user name and password.
To get connection handler, used tpgetconn(3) function. Which is available for this driver.
When starting to use Enduro/X PQ XA Driver, ensure that PosgreSQL LIBPQ is installed.
The typical configuration is done as a standard Enduro/X XA resource configuration, which can be set directly in environment variables or in [@global] section in application configuration (e.g. app.ini). This gives example of app.ini configuration.
Sample configuration app.ini for CCTAG DB1_PQ:
[@global/DB1_PQ] NDRX_XA_RES_ID=1 NDRX_XA_OPEN_STR={"url":"postgresql://testuser:testuser1@localhost:5432/testdb"} NDRX_XA_CLOSE_STR=${NDRX_XA_OPEN_STR} NDRX_XA_DRIVERLIB=libndrxxapq.so NDRX_XA_RMLIB=- NDRX_XA_LAZY_INIT=1
Sample configuration of transaction manager in ndrxconfig.xml for CCTAG DB1_PQ:
<servers> ... <server name="tmsrv"> <max>1</max> <srvid>1650</srvid> <cctag>DB1_PQ</cctag> <sysopt>-e /tmp/tmsrv-dom1.log -r -- -t1 -l/tmp</sysopt> </server> ... </servers>
This is example of programming database with libpq.
File: test_expq.c
#include <string.h> #include <stdio.h> #include <stdlib.h> #include <atmi.h> #include <libpq-fe.h> #define FAIL -1 #define SUCCEED 0 int main(int argc, char** argv) { PGconn * conn; PGresult *res = NULL; ExecStatusType estat; int ret = SUCCEED; /* open connection */ if (EXSUCCEED!=tpopen()) { fprintf(stderr, "Failed to open: %s\n", tpstrerror(tperrno)); ret = FAIL; goto out; } /* get the connection which was open by tpopen() */ conn = (PGconn *)tpgetconn(); /* create some table... */ res = PQexec(conn, "CREATE TABLE manextest(userid integer UNIQUE NOT NULL);"); estat = PQresultStatus(res); if (PGRES_COMMAND_OK != estat) { char *state = PQresultErrorField(res, PG_DIAG_SQLSTATE); char *msg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); fprintf(stderr, "Failed to create table: state: [%s]: %s\n", state, msg); if (0==strcmp(state, "42P07")) { fprintf(stderr, "Table already exist - ignore error\n"); } else { ret = FAIL; goto out; } } /* start transaction */ if (EXSUCCEED!=tpbegin(60, 0)) { fprintf(stderr, "Failed to begin: %s\n", tpstrerror(tperrno)); ret = FAIL; goto out; } /* insert data */ PQclear(res); res = PQexec(conn, "insert into manextest(userid) values ((select COALESCE(max(userid), 1)+1 from manextest));"); estat = PQresultStatus(res); if (PGRES_COMMAND_OK != estat) { char *state = PQresultErrorField(res, PG_DIAG_SQLSTATE); char *msg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); fprintf(stderr, "Failed to create table: state: [%s]: %s\n", state, msg); ret = FAIL; goto out; } if (SUCCEED!=tpcommit(0)) { fprintf(stderr, "TESTERROR: Commit OK, must fail!\n"); ret = FAIL; goto out; } out: if (SUCCEED!=ret) { tpabort(0); } tpclose(); tpterm(); }
Build the program with:
$ cc test_expq.c -o expqtest -I/usr/include/postgresql -lpq -latmi -lnstd -lubf -lrt
Run and test:
$ ./expqtest Failed to create table: state: [42P07]: relation "manextest" already exists Table already exist - ignore error $ psql -U testuser -d testdb -h localhost testdb=> select * from manextest; userid ======== 2 (1 row)
For more unit tests please see atmitest/test067_postgres unit test folder for PQ source examples and configuration.