diff options
author | Michael P | 2011-06-29 23:40:18 +0000 |
---|---|---|
committer | Michael P | 2011-06-29 23:40:18 +0000 |
commit | 9cf414760f30f6fb377f47ced0d0315460bf85a7 (patch) | |
tree | 8a7e5369c07c1c721d83b18752037f96b773b8f0 | |
parent | bc0decce1cb03d4f645f38bb61c7f73b16445fcb (diff) |
Support for GTM-Standby
GTM is the unique SPOF of Postgres-XC cluster.
This commit adds support for the creation of a Standby node related
to GTM to which it is possible to perform a failover in case of
a GTM failure.
GTM standby supports only serializable transactions and is not able
to keep consistent transaction IDs on both GTM and standby when
running transactions in parallel.
This commits adds 3 options for GTM at startup:
- s, to indicate if GTM instance is a standby or not
- i, to indicate host/IP of GTM to connect to
- q, to indicate port number of GTM to connect to
An example of standby startup command:
gtm -D $STANDBY_DATA -l $LOG_DATA/standbylog -p $STANDBY_PORT
-i $GTM_HOST -q $GTM_PORT -s -n 2 &
Two additional keywords have been added in gtm_ctl:
- promote, to promote a GTM-Standby to be a GTM
- status, to look if a GTM instance is active or standby
Ex:
gtm_ctl status -S gtm -D $STANDBY_DATA
gtm_ctl promote -S gtm -D $STANDBY_DATA
Former implementation by Satoshi Nagayasu and Koichi Suzuki.
Cleanup and some code refactoring by me.
95 files changed, 9948 insertions, 669 deletions
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index 6d31e4e075..7e6346b2be 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -713,3 +713,17 @@ pgport_pfree(void *pointer) } #endif + +#ifdef PGXC +#include "gen_alloc.h" + +void *current_memcontext(void); + +void *current_memcontext() +{ + return((void *)CurrentMemoryContext); +} + +Gen_Alloc genAlloc_class = {MemoryContextAlloc, MemoryContextAllocZero, repalloc, pfree, current_memcontext}; + +#endif diff --git a/src/gtm/client/fe-connect.c b/src/gtm/client/fe-connect.c index 7cd3440e4e..bf035decde 100644 --- a/src/gtm/client/fe-connect.c +++ b/src/gtm/client/fe-connect.c @@ -82,6 +82,11 @@ PQconnectGTM(const char *conninfo) if (conn && conn->status != CONNECTION_BAD) (void) connectGTMComplete(conn); + else if (conn != NULL) + { + freeGTM_Conn(conn); + conn = NULL; + } return conn; } @@ -607,7 +612,7 @@ keep_going: /* We will come back to here until there is */ if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, - (char *) &optval, &optlen) == -1) + (char *) &optval, (socklen_t *)&optlen) == -1) { appendGTMPQExpBuffer(&conn->errorMessage, libpq_gettext("could not get socket error status: \n")); @@ -644,7 +649,7 @@ keep_going: /* We will come back to here until there is conn->laddr.salen = sizeof(conn->laddr.addr); if (getsockname(conn->sock, (struct sockaddr *) & conn->laddr.addr, - &conn->laddr.salen) < 0) + (socklen_t *)&conn->laddr.salen) < 0) { appendGTMPQExpBuffer(&conn->errorMessage, "could not get client address from socket:\n"); diff --git a/src/gtm/client/fe-misc.c b/src/gtm/client/fe-misc.c index 5c9e25704d..930c8ec41e 100644 --- a/src/gtm/client/fe-misc.c +++ b/src/gtm/client/fe-misc.c @@ -224,7 +224,7 @@ gtmpqGetInt(int *result, size_t bytes, GTM_Conn *conn) *result = (int) ntohl(tmp4); break; default: - fprintf(conn->Pfdebug, "Integer size of (%d) bytes not supported", bytes); + fprintf(conn->Pfdebug, "Integer size of (%ld) bytes not supported", bytes); return EOF; } @@ -258,7 +258,7 @@ gtmpqPutInt(int value, size_t bytes, GTM_Conn *conn) return EOF; break; default: - fprintf(conn->Pfdebug, "Integer size of (%d) bytes not supported", bytes); + fprintf(conn->Pfdebug, "Integer size of (%ld) bytes not supported", bytes); return EOF; } @@ -620,12 +620,14 @@ retry3: * amount of data already read in the current message. We consider * the message "long" once we have acquired 32k ... */ +#ifdef NOT_USED if (conn->inEnd > 32768 && (conn->inBufSize - conn->inEnd) >= 8192) { someread = 1; goto retry3; } +#endif return 1; } diff --git a/src/gtm/client/fe-protocol.c b/src/gtm/client/fe-protocol.c index a873b2a26f..d56496fcfc 100644 --- a/src/gtm/client/fe-protocol.c +++ b/src/gtm/client/fe-protocol.c @@ -20,7 +20,10 @@ #include "gtm/libpq-fe.h" #include "gtm/libpq-int.h" +#include "gtm/gtm_seq.h" #include "gtm/gtm_client.h" +#include "gtm/gtm_serialize.h" +#include "gtm/register.h" #include <unistd.h> #include <netinet/in.h> @@ -126,7 +129,7 @@ pqParseInput(GTM_Conn *conn) case 'E': /* error return */ if (gtmpqGetError(conn, result)) return NULL; - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; default: printfGTMPQExpBuffer(&conn->errorMessage, @@ -211,7 +214,7 @@ gtmpqGetError(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)result->gr_proxy_data, result->gr_msglen, conn)) { - result->gr_status = 1; + result->gr_status = GTM_RESULT_UNKNOWN; return 1; } @@ -286,13 +289,18 @@ GTMPQgetResult(GTM_Conn *conn) return res; } +/* + * return 0 if parsing command is totally completed. + * return 1 if it needs to be read continuously. + */ static int gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) { int xcnt, xsize; + int i; GlobalTransactionId *xip = NULL; - result->gr_status = 0; + result->gr_status = GTM_RESULT_OK; if (gtmpqGetInt((int *)&result->gr_type, 4, conn)) return 1; @@ -332,40 +340,46 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)result->gr_proxy_data, result->gr_msglen, conn)) { - result->gr_status = 1; + result->gr_status = GTM_RESULT_UNKNOWN; return 1; } return result->gr_status; } - result->gr_status = 0; + result->gr_status = GTM_RESULT_OK; switch (result->gr_type) { + case NODE_BEGIN_REPLICATION_INIT_RESULT: + break; + + case NODE_END_REPLICATION_INIT_RESULT: + break; + case TXN_BEGIN_RESULT: if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txnhandle, sizeof (GTM_TransactionHandle), conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; case TXN_BEGIN_GETGXID_RESULT: if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid_tp.gxid, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid_tp.timestamp, sizeof (GTM_Timestamp), conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; case TXN_BEGIN_GETGXID_AUTOVACUUM_RESULT: case TXN_PREPARE_RESULT: case TXN_START_PREPARED_RESULT: if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid, sizeof (GlobalTransactionId), conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; case TXN_COMMIT_RESULT: @@ -373,38 +387,47 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) case TXN_ROLLBACK_RESULT: if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid, sizeof (GlobalTransactionId), conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; case TXN_GET_GXID_RESULT: if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn.txnhandle, sizeof (GTM_TransactionHandle), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn.gxid, sizeof (GlobalTransactionId), conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; + break; + + case TXN_GET_NEXT_GXID_RESULT: + if (gtmpqGetInt((int *)&result->gr_resdata.grd_next_gxid, + sizeof (int32), conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } break; case TXN_BEGIN_GETGXID_MULTI_RESULT: if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_multi.txn_count, sizeof (int), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_multi.start_gxid, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_multi.timestamp, sizeof (GTM_Timestamp), conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; case TXN_COMMIT_MULTI_RESULT: @@ -412,13 +435,13 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_rc_multi.txn_count, sizeof (int), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_rc_multi.status, sizeof (int) * result->gr_resdata.grd_txn_rc_multi.txn_count, conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } break; @@ -427,7 +450,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_snap_multi.txnhandle, sizeof (GTM_TransactionHandle), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } /* Fall through */ @@ -435,7 +458,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_snap_multi.gxid, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } /* Fall through */ @@ -443,42 +466,42 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_snap_multi.txn_count, sizeof (int), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_snap_multi.status, sizeof (int) * result->gr_resdata.grd_txn_snap_multi.txn_count, conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_xmin, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_xmax, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_recent_global_xmin, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } - if (gtmpqGetInt(&result->gr_snapshot.sn_xcnt, + if (gtmpqGetInt((int *)&result->gr_snapshot.sn_xcnt, sizeof (int32), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } @@ -495,7 +518,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)xip, sizeof (GlobalTransactionId) * xcnt, conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } @@ -508,7 +531,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) case SEQUENCE_ALTER_RESULT: case SEQUENCE_SET_VAL_RESULT: if (gtmpqReadSeqKey(&result->gr_resdata.grd_seqkey, conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; case SEQUENCE_GET_CURRENT_RESULT: @@ -516,12 +539,50 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) case SEQUENCE_GET_LAST_RESULT: if (gtmpqReadSeqKey(&result->gr_resdata.grd_seq.seqkey, conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_resdata.grd_seq.seqval, sizeof (GTM_Sequence), conn)) - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; + break; + + case SEQUENCE_LIST_RESULT: + if (gtmpqGetInt(&result->gr_resdata.grd_seq_list.seq_count, + sizeof (int32), conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + + result->gr_resdata.grd_seq_list.seq = + (GTM_SeqInfo **)malloc(sizeof(GTM_SeqInfo *) * + result->gr_resdata.grd_seq_list.seq_count); + + for (i = 0 ; i < result->gr_resdata.grd_seq_list.seq_count; i++) + { + int buflen; + char *buf; + + /* a length of the next serialized sequence */ + if (gtmpqGetInt(&buflen, sizeof (int32), conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + + /* a data body of the serialized sequence */ + buf = (char *)malloc(buflen); + if (gtmpqGetnchar(buf, buflen, conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + + result->gr_resdata.grd_seq_list.seq[i] = gtm_deserialize_sequence(buf, buflen); + + free(buf); + } break; case TXN_GET_STATUS_RESULT: @@ -534,19 +595,19 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_gid_data.gxid, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_gid_data.prepared_gxid, sizeof (GlobalTransactionId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetInt(&result->gr_resdata.grd_txn_get_gid_data.datanodecnt, sizeof (int32), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (result->gr_resdata.grd_txn_get_gid_data.datanodecnt != 0) @@ -554,20 +615,20 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if ((result->gr_resdata.grd_txn_get_gid_data.datanodes = (PGXC_NodeId *) malloc(sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.datanodecnt)) == NULL) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_get_gid_data.datanodes, sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.datanodecnt, conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } } if (gtmpqGetInt(&result->gr_resdata.grd_txn_get_gid_data.coordcnt, sizeof (int32), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (result->gr_resdata.grd_txn_get_gid_data.coordcnt != 0) @@ -575,38 +636,101 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result) if ((result->gr_resdata.grd_txn_get_gid_data.coordinators = (PGXC_NodeId *) malloc(sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.coordcnt)) == NULL) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_get_gid_data.coordinators, sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.coordcnt, conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } } break; + case TXN_GXID_LIST_RESULT: + result->gr_resdata.grd_txn_gid_list.len = 0; + result->gr_resdata.grd_txn_gid_list.ptr = NULL; + + if (gtmpqGetInt((int *)&result->gr_resdata.grd_txn_gid_list.len, + sizeof(int32), conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + + result->gr_resdata.grd_txn_gid_list.ptr = + (char *)malloc(result->gr_resdata.grd_txn_gid_list.len); + + if (result->gr_resdata.grd_txn_gid_list.ptr==NULL) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + + if (gtmpqGetnchar(result->gr_resdata.grd_txn_gid_list.ptr, + result->gr_resdata.grd_txn_gid_list.len, + conn)) /* serialized GTM_Transactions */ + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + break; + case NODE_UNREGISTER_RESULT: case NODE_REGISTER_RESULT: if (gtmpqGetnchar((char *)&result->gr_resdata.grd_node.type, sizeof (GTM_PGXCNodeType), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } if (gtmpqGetnchar((char *)&result->gr_resdata.grd_node.nodenum, sizeof (GTM_PGXCNodeId), conn)) { - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; } break; + case NODE_LIST_RESULT: + { + int i; + + if (gtmpqGetInt(&result->gr_resdata.grd_node_list.num_node, sizeof(int32), conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + + for (i = 0 ; i < result->gr_resdata.grd_node_list.num_node; i++) + { + int size; + char buf[1024]; + GTM_PGXCNodeInfo *data = (GTM_PGXCNodeInfo *)malloc(sizeof(GTM_PGXCNodeInfo)); + + if (gtmpqGetInt(&size, sizeof(int32), conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + + if (gtmpqGetnchar((char *)&buf, size, conn)) + { + result->gr_status = GTM_RESULT_ERROR; + break; + } + gtm_deserialize_pgxcnodeinfo(data, buf, size); + + result->gr_resdata.grd_node_list.nodeinfo[i] = data; + } + + break; + } default: printfGTMPQExpBuffer(&conn->errorMessage, "unexpected result type from server; result typr was \"%d\"\n", result->gr_type); - result->gr_status = -1; + result->gr_status = GTM_RESULT_ERROR; break; } @@ -619,7 +743,7 @@ gtmpqReadSeqKey(GTM_SequenceKey seqkey, GTM_Conn *conn) /* * Read keylength */ - if (gtmpqGetInt(&seqkey->gsk_keylen, 4, conn)) + if (gtmpqGetInt((int *)&seqkey->gsk_keylen, 4, conn)) return EINVAL; /* diff --git a/src/gtm/client/gtm_client.c b/src/gtm/client/gtm_client.c index f5cafd9af2..454ebe240a 100644 --- a/src/gtm/client/gtm_client.c +++ b/src/gtm/client/gtm_client.c @@ -18,17 +18,42 @@ #include <time.h> +#include "gtm/elog.h" #include "gtm/gtm_c.h" +#include "gtm/gtm_ip.h" #include "gtm/libpq-fe.h" #include "gtm/libpq-int.h" #include "gtm/gtm_client.h" #include "gtm/gtm_msg.h" +#include "gtm/gtm_serialize.h" +#include "gtm/register.h" #include "gtm/assert.h" void GTM_FreeResult(GTM_Result *result, GTM_PGXCNodeType remote_type); +static GTM_Result *makeEmptyResultIfIsNull(GTM_Result *oldres); + +/* + * Make an empty result if old one is null. + */ +static GTM_Result * +makeEmptyResultIfIsNull(GTM_Result *oldres) +{ + GTM_Result *res = NULL; + + if (oldres == NULL) + { + res = (GTM_Result *) malloc(sizeof(GTM_Result)); + memset(res, 0, sizeof(GTM_Result)); + } + else + return oldres; + + return res; +} + /* * Connection Management API */ @@ -45,6 +70,311 @@ disconnect_gtm(GTM_Conn *conn) } /* + * begin_replication_initial_sync() acquires several locks to prepare + * for copying internal transaction, xid and sequence information + * to the standby node at its startup. + * + * returns 1 on success, 0 on failure. + */ +int +begin_replication_initial_sync(GTM_Conn *conn) +{ + GTM_Result *res = NULL; + time_t finish_time; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn) || + gtmpqPutInt(MSG_NODE_BEGIN_REPLICATION_INIT, sizeof (GTM_MessageType), conn)) + goto send_failed; + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + Assert(res->gr_type == NODE_BEGIN_REPLICATION_INIT_RESULT); + else + return 0; + + return 1; + +receive_failed: +send_failed: + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return 0; +} + +/* + * end_replication_initial_sync() releases several locks + * after copying internal transaction, xid and sequence information + * to the standby node at its startup. + * + * returns 1 on success, 0 on failure. + */ +int +end_replication_initial_sync(GTM_Conn *conn) +{ + GTM_Result *res = NULL; + time_t finish_time; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn) || + gtmpqPutInt(MSG_NODE_END_REPLICATION_INIT, sizeof (GTM_MessageType), conn)) + goto send_failed; + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + Assert(res->gr_type == NODE_END_REPLICATION_INIT_RESULT); + + return 1; + +receive_failed: +send_failed: + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return 0; +} + +/* + * get_node_list() + * + * returns a number of nodes on success, -1 on failure. + */ +size_t +get_node_list(GTM_Conn *conn, GTM_PGXCNodeInfo *data, size_t maxlen) +{ + GTM_Result *res = NULL; + time_t finish_time; + size_t num_node; + int i; + + for (i = 0; i < maxlen; i++) + data[i].nodenum = i; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn) || + gtmpqPutInt(MSG_NODE_LIST, sizeof (GTM_MessageType), conn)) + goto send_failed; + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + /* + * Do something here. + */ + num_node = res->gr_resdata.grd_node_list.num_node; + + fprintf(stderr, "get_node_list: num_node=%ld\n", num_node); + + for (i = 0; i < num_node; i++) + { + memcpy(&data[i], res->gr_resdata.grd_node_list.nodeinfo[i], sizeof(GTM_PGXCNodeInfo)); + + fprintf(stderr, "get_node_list: nodetype=%d, nodenum=%d, datafolder=%s\n", + data[i].type, data[i].nodenum, data[i].datafolder); + } + + if (res->gr_status == GTM_RESULT_OK) + Assert(res->gr_type == NODE_LIST_RESULT); + + return num_node; + +receive_failed: +send_failed: + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} + +/* + * get_next_gxid() + * + * returns the next gxid on success, InvalidGlobalTransactionId on failure. + */ +GlobalTransactionId +get_next_gxid(GTM_Conn *conn) +{ + GTM_Result *res = NULL; + GlobalTransactionId next_gxid; + time_t finish_time; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn) || + gtmpqPutInt(MSG_TXN_GET_NEXT_GXID, sizeof (GTM_MessageType), conn)) + goto send_failed; + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + fprintf(stderr, "GTMPQgetResult() done.\n"); + fflush(stderr); + + next_gxid = res->gr_resdata.grd_next_gxid; + + if (res->gr_status == GTM_RESULT_OK) + Assert(res->gr_type == TXN_GET_NEXT_GXID_RESULT); + + /* FIXME: should be a number of gxids */ + return next_gxid; + +receive_failed: +send_failed: + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return InvalidGlobalTransactionId; +} + +/* + * get_txn_gxid_list() + * + * returns a number of gxid on success, -1 on failure. + */ +uint32 +get_txn_gxid_list(GTM_Conn *conn, GTM_Transactions *txn) +{ + GTM_Result *res = NULL; + time_t finish_time; + int txn_count; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn) || + gtmpqPutInt(MSG_TXN_GXID_LIST, sizeof (GTM_MessageType), conn)) + goto send_failed; + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + Assert(res->gr_type == TXN_GXID_LIST_RESULT); + + txn_count = gtm_deserialize_transactions(txn, + res->gr_resdata.grd_txn_gid_list.ptr, + res->gr_resdata.grd_txn_gid_list.len); + + return txn_count; + +receive_failed: +send_failed: + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} + +/* + * get_sequence_list() + * + * returns a number of sequences on success, -1 on failure. + */ +size_t +get_sequence_list(GTM_Conn *conn, GTM_SeqInfo **seq_list, size_t seq_max) +{ + GTM_Result *res = NULL; + time_t finish_time; + int i; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn) || + gtmpqPutInt(MSG_SEQUENCE_LIST, sizeof (GTM_MessageType), conn)) + goto send_failed; + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + Assert(res->gr_type == SEQUENCE_LIST_RESULT); + + for (i = 0; i < res->gr_resdata.grd_seq_list.seq_count; i++) + { + seq_list[i] = res->gr_resdata.grd_seq_list.seq[i]; + + if ( i >= seq_max ) + break; + } + + return i; + +receive_failed: +send_failed: + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} + +/* * Transaction Management API */ GlobalTransactionId @@ -77,7 +407,7 @@ begin_transaction(GTM_Conn *conn, GTM_IsolationLevel isolevel, GTM_Timestamp *ti if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { if (timestamp) *timestamp = res->gr_resdata.grd_gxid_tp.timestamp; @@ -89,6 +419,8 @@ begin_transaction(GTM_Conn *conn, GTM_IsolationLevel isolevel, GTM_Timestamp *ti receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return InvalidGlobalTransactionId; } @@ -126,13 +458,15 @@ begin_transaction_autovacuum(GTM_Conn *conn, GTM_IsolationLevel isolevel) if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) return res->gr_resdata.grd_gxid; else return InvalidGlobalTransactionId; receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return InvalidGlobalTransactionId; } @@ -165,7 +499,7 @@ commit_transaction(GTM_Conn *conn, GlobalTransactionId gxid) if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_type == TXN_COMMIT_RESULT); Assert(res->gr_resdata.grd_gxid == gxid); @@ -175,6 +509,8 @@ commit_transaction(GTM_Conn *conn, GlobalTransactionId gxid) receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -209,7 +545,7 @@ commit_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, GlobalTran if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_type == TXN_COMMIT_PREPARED_RESULT); Assert(res->gr_resdata.grd_gxid == gxid); @@ -219,6 +555,8 @@ commit_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, GlobalTran send_failed: receive_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -251,7 +589,7 @@ abort_transaction(GTM_Conn *conn, GlobalTransactionId gxid) if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_type == TXN_ROLLBACK_RESULT); Assert(res->gr_resdata.grd_gxid == gxid); @@ -261,6 +599,8 @@ abort_transaction(GTM_Conn *conn, GlobalTransactionId gxid) receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -309,7 +649,7 @@ start_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, char *gid, if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_type == TXN_START_PREPARED_RESULT); Assert(res->gr_resdata.grd_gxid == gxid); @@ -319,6 +659,8 @@ start_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, char *gid, receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -352,7 +694,7 @@ prepare_transaction(GTM_Conn *conn, GlobalTransactionId gxid) if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_type == TXN_PREPARE_RESULT); Assert(res->gr_resdata.grd_gxid == gxid); @@ -362,6 +704,8 @@ prepare_transaction(GTM_Conn *conn, GlobalTransactionId gxid) receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -406,7 +750,7 @@ get_gid_data(GTM_Conn *conn, if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { *gxid = res->gr_resdata.grd_txn_get_gid_data.gxid; *prepared_gxid = res->gr_resdata.grd_txn_get_gid_data.prepared_gxid; @@ -422,6 +766,8 @@ get_gid_data(GTM_Conn *conn, receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -458,7 +804,7 @@ get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid, bool canbe_grouped) if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_type == SNAPSHOT_GET_RESULT); Assert(res->gr_resdata.grd_txn_snap_multi.gxid == gxid); @@ -470,6 +816,8 @@ get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid, bool canbe_grouped) receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return NULL; } @@ -516,6 +864,8 @@ open_sequence(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence increment, receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -561,7 +911,9 @@ alter_sequence(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence increment, receive_failed: send_failed: - return -1; + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; } int @@ -598,6 +950,8 @@ close_sequence(GTM_Conn *conn, GTM_SequenceKey key) receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -634,8 +988,10 @@ rename_sequence(GTM_Conn *conn, GTM_SequenceKey key, GTM_SequenceKey newkey) return res->gr_status; - receive_failed: - send_failed: +receive_failed: +send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -668,13 +1024,15 @@ get_current(GTM_Conn *conn, GTM_SequenceKey key) if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) return res->gr_resdata.grd_seq.seqval; else return InvalidSequenceValue; receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -713,6 +1071,8 @@ set_val(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence nextval, bool iscalled receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -745,13 +1105,15 @@ get_next(GTM_Conn *conn, GTM_SequenceKey key) if ((res = GTMPQgetResult(conn)) == NULL) goto receive_failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) return res->gr_resdata.grd_seq.seqval; else return InvalidSequenceValue; receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -788,30 +1150,109 @@ reset_sequence(GTM_Conn *conn, GTM_SequenceKey key) receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } /* + * rc would be 0 on success, non-zero on gtm_getnameinfo_all() failure. + */ +char * +node_get_local_addr(GTM_Conn *conn, char *buf, size_t buflen, int *rc) +{ + char local_host[NI_MAXHOST]; + char local_port[NI_MAXSERV]; + + *rc = 0; + + memset(local_host, 0, sizeof(local_host)); + memset(local_port, 0, sizeof(local_port)); + memset(buf, 0, buflen); + + if (conn->remote_type != PGXC_NODE_GTM_PROXY) + { + if (gtm_getnameinfo_all(&conn->laddr.addr, conn->laddr.salen, + local_host, sizeof(local_host), + local_port, sizeof(local_port), + NI_NUMERICSERV)) + { + *rc = gtm_getnameinfo_all(&conn->laddr.addr, conn->laddr.salen, + local_host, sizeof(local_host), + local_port, sizeof(local_port), + NI_NUMERICHOST | NI_NUMERICSERV); + } + } + + if (local_host[0] != '\0') + strncpy(buf, local_host, buflen); + + return buf; +} + +/* * Register a Node on GTM * Seen from a Node viewpoint, we do not know if we are directly connected to GTM * or go through a proxy, so register 0 as proxy number. * This number is modified at proxy level automatically. + * + * node_register() returns 0 on success, -1 on failure. */ -int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodePort port, GTM_PGXCNodeId nodenum, +int node_register(GTM_Conn *conn, + GTM_PGXCNodeType type, + GTM_PGXCNodePort port, + GTM_PGXCNodeId nodenum, char *datafolder) { + char host[1024]; + int rc; + + node_get_local_addr(conn, host, sizeof(host), &rc); + if (rc != 0) + return -1; + + return node_register_internal(conn, type, host, port, nodenum, datafolder, NODE_CONNECTED); +} + +int node_register_internal(GTM_Conn *conn, + GTM_PGXCNodeType type, + const char *host, + GTM_PGXCNodePort port, + GTM_PGXCNodeId nodenum, + char *datafolder, + GTM_PGXCNodeStatus status) +{ GTM_Result *res = NULL; time_t finish_time; GTM_PGXCNodeId proxynum = 0; + /* + * We should be very careful about the format of the message. + * Host name and its length is needed only when registering + * GTM Proxy. + * In other case, they must not be included in the message. + */ if (gtmpqPutMsgStart('C', true, conn) || + /* Message Type */ gtmpqPutInt(MSG_NODE_REGISTER, sizeof (GTM_MessageType), conn) || + /* Node Type to Register */ gtmpqPutnchar((char *)&type, sizeof(GTM_PGXCNodeType), conn) || + /* Node Number to Register */ gtmpqPutnchar((char *)&nodenum, sizeof(GTM_PGXCNodeId), conn) || + /* Host name length */ + gtmpqPutInt(strlen(host), sizeof (GTM_StrLen), conn) || + /* Host name (var-len) */ + gtmpqPutnchar(host, strlen(host), conn) || + /* Port number */ gtmpqPutnchar((char *)&port, sizeof(GTM_PGXCNodePort), conn) || + /* Proxy ID (zero if connected to GTM directly) */ gtmpqPutnchar((char *)&proxynum, sizeof(GTM_PGXCNodeId), conn) || + /* Data Folder length */ gtmpqPutInt(strlen(datafolder), sizeof (GTM_StrLen), conn) || - gtmpqPutnchar(datafolder, strlen(datafolder), conn)) + /* Data Folder (var-len) */ + gtmpqPutnchar(datafolder, strlen(datafolder), conn) || + /* Node Status */ + gtmpqPutInt(status, sizeof(GTM_PGXCNodeStatus), conn)) goto send_failed; /* Finish the message. */ @@ -831,7 +1272,7 @@ int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodePort port, goto receive_failed; /* Check on node type and node number */ - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_resdata.grd_node.type == type); Assert(res->gr_resdata.grd_node.nodenum == nodenum); @@ -841,6 +1282,8 @@ int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodePort port, receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -872,7 +1315,7 @@ int node_unregister(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenu goto receive_failed; /* Check on node type and node number */ - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_resdata.grd_node.type == type); Assert(res->gr_resdata.grd_node.nodenum == nodenum); @@ -882,6 +1325,8 @@ int node_unregister(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenu receive_failed: send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; return -1; } @@ -893,3 +1338,264 @@ GTM_FreeResult(GTM_Result *result, GTM_PGXCNodeType remote_type) gtmpqFreeResultData(result, remote_type); free(result); } + +int +backend_disconnect(GTM_Conn *conn, bool is_postmaster, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum) +{ + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn) || + gtmpqPutInt(MSG_BACKEND_DISCONNECT, sizeof (GTM_MessageType), conn) || + gtmpqPutc(is_postmaster, conn)) + goto send_failed; + + /* + * Then send node type and node number if backend is a postmaster to + * disconnect the correct node. + */ + if (is_postmaster) + { + if (gtmpqPutnchar((char *)&type, + sizeof(GTM_PGXCNodeType), conn) || + gtmpqPutnchar((char *)&nodenum, + sizeof(GTM_PGXCNodeId), conn)) + goto send_failed; + } + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + return 1; + +send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} + +int +begin_transaction_multi(GTM_Conn *conn, int txn_count, GTM_IsolationLevel *txn_isolation_level, + bool *txn_read_only, GTMProxy_ConnID *txn_connid, + int *txn_count_out, GlobalTransactionId *gxid_out, GTM_Timestamp *ts_out) +{ + GTM_Result *res = NULL; + time_t finish_time; + int i; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */ + goto send_failed; + + if (gtmpqPutInt(MSG_TXN_BEGIN_GETGXID_MULTI, sizeof (GTM_MessageType), conn) || + gtmpqPutInt(txn_count, sizeof(int), conn)) + goto send_failed; + + for (i = 0; i < txn_count; i++) + { + gtmpqPutInt(txn_isolation_level[i], sizeof(int), conn); + gtmpqPutc(txn_read_only[i], conn); + gtmpqPutInt(txn_connid[i], sizeof(int), conn); + } + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + { + memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int)); + memcpy(gxid_out, &res->gr_resdata.grd_txn_get_multi.start_gxid, sizeof(GlobalTransactionId)); + memcpy(ts_out, &res->gr_resdata.grd_txn_get_multi.timestamp, sizeof(GTM_Timestamp)); + } + + return res->gr_status; + +receive_failed: +send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} + +int +commit_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid, + int *txn_count_out, int *status_out) +{ + GTM_Result *res = NULL; + time_t finish_time; + int i; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */ + goto send_failed; + + if (gtmpqPutInt(MSG_TXN_COMMIT_MULTI, sizeof (GTM_MessageType), conn) || + gtmpqPutInt(txn_count, sizeof(int), conn)) + goto send_failed; + + for (i = 0; i < txn_count; i++) + { + if (gtmpqPutc(true, conn) || + gtmpqPutnchar((char *)&gxid[i], + sizeof (GlobalTransactionId), conn)) + goto send_failed; + } + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + { + memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int)); + memcpy(status_out, &res->gr_resdata.grd_txn_rc_multi.status, sizeof(int) * (*txn_count_out)); + } + + return res->gr_status; + +receive_failed: +send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} + +int +abort_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid, + int *txn_count_out, int *status_out) +{ + GTM_Result *res = NULL; + time_t finish_time; + int i; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */ + goto send_failed; + + if (gtmpqPutInt(MSG_TXN_ROLLBACK_MULTI, sizeof (GTM_MessageType), conn) || + gtmpqPutInt(txn_count, sizeof(int), conn)) + goto send_failed; + + for (i = 0; i < txn_count; i++) + { + if (gtmpqPutc(true, conn) || + gtmpqPutnchar((char *)&gxid[i], + sizeof (GlobalTransactionId), conn)) + goto send_failed; + } + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + { + memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int)); + memcpy(status_out, &res->gr_resdata.grd_txn_rc_multi.status, sizeof(int) * (*txn_count_out)); + } + + return res->gr_status; + +receive_failed: +send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} + +int +snapshot_get_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid, + int *txn_count_out, int *status_out, + GlobalTransactionId *xmin_out, GlobalTransactionId *xmax_out, + GlobalTransactionId *recent_global_xmin_out, int32 *xcnt_out) +{ + GTM_Result *res = NULL; + time_t finish_time; + int i; + + /* Start the message. */ + if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */ + goto send_failed; + + if (gtmpqPutInt(MSG_SNAPSHOT_GET_MULTI, sizeof (GTM_MessageType), conn) || + gtmpqPutInt(txn_count, sizeof(int), conn)) + goto send_failed; + + for (i = 0; i < txn_count; i++) + { + if (gtmpqPutc(true, conn) || + gtmpqPutnchar((char *)&gxid[i], + sizeof (GlobalTransactionId), conn)) + goto send_failed; + } + + /* Finish the message. */ + if (gtmpqPutMsgEnd(conn)) + goto send_failed; + + /* Flush to ensure backend gets it. */ + if (gtmpqFlush(conn)) + goto send_failed; + + finish_time = time(NULL) + CLIENT_GTM_TIMEOUT; + if (gtmpqWaitTimed(true, false, conn, finish_time) || + gtmpqReadData(conn) < 0) + goto receive_failed; + + if ((res = GTMPQgetResult(conn)) == NULL) + goto receive_failed; + + if (res->gr_status == GTM_RESULT_OK) + { + memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int)); + memcpy(status_out, &res->gr_resdata.grd_txn_rc_multi.status, sizeof(int) * (*txn_count_out)); + memcpy(xmin_out, &res->gr_snapshot.sn_xmin, sizeof(GlobalTransactionId)); + memcpy(xmax_out, &res->gr_snapshot.sn_xmax, sizeof(GlobalTransactionId)); + memcpy(recent_global_xmin_out, &res->gr_snapshot.sn_recent_global_xmin, sizeof(GlobalTransactionId)); + memcpy(xcnt_out, &res->gr_snapshot.sn_xcnt, sizeof(int32)); + } + + return res->gr_status; + +receive_failed: +send_failed: + conn->result = makeEmptyResultIfIsNull(conn->result); + conn->result->gr_status = GTM_RESULT_COMM_ERROR; + return -1; +} diff --git a/src/gtm/common/Makefile b/src/gtm/common/Makefile index 5ee1eb8bbf..1b94a9e933 100644 --- a/src/gtm/common/Makefile +++ b/src/gtm/common/Makefile @@ -9,8 +9,10 @@ NAME=gtm SO_MAJOR_VERSION= 1 SO_MINOR_VERSION= 0 +LDFLAGS=-L$(top_build_dir)/common -L$(top_build_dir)/libpq +LIBS=-lpthread -OBJS=aset.o mcxt.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o +OBJS=aset.o mcxt.o gtm_utils.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o gtm_serialize.o gtm_serialize_debug.o all:all-lib diff --git a/src/gtm/common/gtm_list.c b/src/gtm/common/gtm_list.c index 234483cf55..46245f1951 100644 --- a/src/gtm/common/gtm_list.c +++ b/src/gtm/common/gtm_list.c @@ -20,17 +20,17 @@ #include "gtm/memutils.h" #include "gtm/assert.h" -#define equal(a, b) ((a) == (b)) +#define gtm_equal(a, b) ((a) == (b)) #ifdef USE_ASSERT_CHECKING /* - * Check that the specified List is valid (so far as we can tell). + * Check that the specified gtm_List is valid (so far as we can tell). */ static void -check_list_invariants(List *list) +check_list_invariants(gtm_List *list) { - if (list == NIL) + if (list == gtm_NIL) return; Assert(list->length > 0); @@ -48,21 +48,21 @@ check_list_invariants(List *list) #endif /* USE_ASSERT_CHECKING */ /* - * Return a freshly allocated List. Since empty non-NIL lists are + * Return a freshly allocated gtm_List. Since empty non-gtm_NIL lists are * invalid, new_list() also allocates the head cell of the new list: * the caller should be sure to fill in that cell's data. */ -static List * +static gtm_List * new_list() { - List *new_list; - ListCell *new_head; + gtm_List *new_list; + gtm_ListCell *new_head; - new_head = (ListCell *) palloc(sizeof(*new_head)); + new_head = (gtm_ListCell *) palloc(sizeof(*new_head)); new_head->next = NULL; /* new_head->data is left undefined! */ - new_list = (List *) palloc(sizeof(*new_list)); + new_list = (gtm_List *) palloc(sizeof(*new_list)); new_list->length = 1; new_list->head = new_head; new_list->tail = new_head; @@ -72,17 +72,17 @@ new_list() /* * Allocate a new cell and make it the head of the specified - * list. Assumes the list it is passed is non-NIL. + * list. Assumes the list it is passed is non-gtm_NIL. * * The data in the new head cell is undefined; the caller should be * sure to fill it in */ static void -new_head_cell(List *list) +new_head_cell(gtm_List *list) { - ListCell *new_head; + gtm_ListCell *new_head; - new_head = (ListCell *) palloc(sizeof(*new_head)); + new_head = (gtm_ListCell *) palloc(sizeof(*new_head)); new_head->next = list->head; list->head = new_head; @@ -91,17 +91,17 @@ new_head_cell(List *list) /* * Allocate a new cell and make it the tail of the specified - * list. Assumes the list it is passed is non-NIL. + * list. Assumes the list it is passed is non-gtm_NIL. * * The data in the new tail cell is undefined; the caller should be * sure to fill it in */ static void -new_tail_cell(List *list) +new_tail_cell(gtm_List *list) { - ListCell *new_tail; + gtm_ListCell *new_tail; - new_tail = (ListCell *) palloc(sizeof(*new_tail)); + new_tail = (gtm_ListCell *) palloc(sizeof(*new_tail)); new_tail->next = NULL; list->tail->next = new_tail; @@ -116,15 +116,15 @@ new_tail_cell(List *list) * value, rather than continuing to use the pointer passed as the * first argument. */ -List * -lappend(List *list, void *datum) +gtm_List * +gtm_lappend(gtm_List *list, void *datum) { - if (list == NIL) + if (list == gtm_NIL) list = new_list(); else new_tail_cell(list); - lfirst(list->tail) = datum; + gtm_lfirst(list->tail) = datum; check_list_invariants(list); return list; } @@ -132,15 +132,15 @@ lappend(List *list, void *datum) /* * Add a new cell to the list, in the position after 'prev_cell'. The * data in the cell is left undefined, and must be filled in by the - * caller. 'list' is assumed to be non-NIL, and 'prev_cell' is assumed - * to be non-NULL and a member of 'list'. + * caller. 'list' is assumed to be non-gtm_NIL, and 'prev_cell' is assumed + * to be non-NULL and a gtm_member of 'list'. */ -static ListCell * -add_new_cell(List *list, ListCell *prev_cell) +static gtm_ListCell * +add_new_cell(gtm_List *list, gtm_ListCell *prev_cell) { - ListCell *new_cell; + gtm_ListCell *new_cell; - new_cell = (ListCell *) palloc(sizeof(*new_cell)); + new_cell = (gtm_ListCell *) palloc(sizeof(*new_cell)); /* new_cell->data is left undefined! */ new_cell->next = prev_cell->next; prev_cell->next = new_cell; @@ -154,18 +154,18 @@ add_new_cell(List *list, ListCell *prev_cell) } /* - * Add a new cell to the specified list (which must be non-NIL); + * Add a new cell to the specified list (which must be non-gtm_NIL); * it will be placed after the list cell 'prev' (which must be - * non-NULL and a member of 'list'). The data placed in the new cell + * non-NULL and a gtm_member of 'list'). The data placed in the new cell * is 'datum'. The newly-constructed cell is returned. */ -ListCell * -lappend_cell(List *list, ListCell *prev, void *datum) +gtm_ListCell * +gtm_lappend_cell(gtm_List *list, gtm_ListCell *prev, void *datum) { - ListCell *new_cell; + gtm_ListCell *new_cell; new_cell = add_new_cell(list, prev); - lfirst(new_cell) = datum; + gtm_lfirst(new_cell) = datum; check_list_invariants(list); return new_cell; } @@ -177,15 +177,15 @@ lappend_cell(List *list, ListCell *prev, void *datum) * value, rather than continuing to use the pointer passed as the * second argument. */ -List * -lcons(void *datum, List *list) +gtm_List * +gtm_lcons(void *datum, gtm_List *list) { - if (list == NIL) + if (list == gtm_NIL) list = new_list(); else new_head_cell(list); - lfirst(list->head) = datum; + gtm_lfirst(list->head) = datum; check_list_invariants(list); return list; } @@ -198,18 +198,18 @@ lcons(void *datum, List *list) * * The nodes in list2 are merely appended to the end of list1 in-place * (i.e. they aren't copied; the two lists will share some of the same - * storage). Therefore, invoking list_free() on list2 will also + * storage). Therefore, invoking gtm_list_free() on list2 will also * invalidate a portion of list1. */ -List * -list_concat(List *list1, List *list2) +gtm_List * +gtm_list_concat(gtm_List *list1, gtm_List *list2) { - if (list1 == NIL) + if (list1 == gtm_NIL) return list2; - if (list2 == NIL) + if (list2 == gtm_NIL) return list1; if (list1 == list2) - elog(ERROR, "cannot list_concat() a list to itself"); + elog(ERROR, "cannot gtm_list_concat() a list to itself"); list1->length += list2->length; @@ -227,23 +227,23 @@ list_concat(List *list1, List *list2) * list -- it may or may not be the same as the pointer that was * passed. * - * Note that any cells removed by list_truncate() are NOT pfree'd. + * Note that any cells removed by gtm_list_truncate() are NOT pfree'd. */ -List * -list_truncate(List *list, int new_size) +gtm_List * +gtm_list_truncate(gtm_List *list, int new_size) { - ListCell *cell; + gtm_ListCell *cell; int n; if (new_size <= 0) - return NIL; /* truncate to zero length */ + return gtm_NIL; /* truncate to zero length */ /* If asked to effectively extend the list, do nothing */ - if (new_size >= list_length(list)) + if (new_size >= gtm_list_length(list)) return list; n = 1; - foreach(cell, list) + gtm_foreach(cell, list) { if (n == new_size) { @@ -265,12 +265,12 @@ list_truncate(List *list, int new_size) * Locate the n'th cell (counting from 0) of the list. It is an assertion * failure if there is no such cell. */ -static ListCell * -list_nth_cell(List *list, int n) +static gtm_ListCell * +list_nth_cell(gtm_List *list, int n) { - ListCell *match; + gtm_ListCell *match; - Assert(list != NIL); + Assert(list != gtm_NIL); Assert(n >= 0); Assert(n < list->length); check_list_invariants(list); @@ -287,29 +287,29 @@ list_nth_cell(List *list, int n) /* * Return the data value contained in the n'th element of the - * specified list. (List elements begin at 0.) + * specified list. (gtm_List elements begin at 0.) */ void * -list_nth(List *list, int n) +gtm_list_nth(gtm_List *list, int n) { - return lfirst(list_nth_cell(list, n)); + return gtm_lfirst(list_nth_cell(list, n)); } /* - * Return true iff 'datum' is a member of the list. Equality is - * determined via equal(), so callers should ensure that they pass a + * Return true if 'datum' is a gtm_member of the list. Equality is + * determined via gtm_equal(), so callers should ensure that they pass a * Node as 'datum'. */ bool -list_member(List *list, void *datum) +gtm_list_member(gtm_List *list, void *datum) { - ListCell *cell; + gtm_ListCell *cell; check_list_invariants(list); - foreach(cell, list) + gtm_foreach(cell, list) { - if (equal(lfirst(cell), datum)) + if (gtm_equal(gtm_lfirst(cell), datum)) return true; } @@ -317,19 +317,19 @@ list_member(List *list, void *datum) } /* - * Return true iff 'datum' is a member of the list. Equality is + * Return true if 'datum' is a gtm_member of the list. Equality is * determined by using simple pointer comparison. */ bool -list_member_ptr(List *list, void *datum) +gtm_list_member_ptr(gtm_List *list, void *datum) { - ListCell *cell; + gtm_ListCell *cell; check_list_invariants(list); - foreach(cell, list) + gtm_foreach(cell, list) { - if (lfirst(cell) == datum) + if (gtm_lfirst(cell) == datum) return true; } @@ -340,23 +340,23 @@ list_member_ptr(List *list, void *datum) * Delete 'cell' from 'list'; 'prev' is the previous element to 'cell' * in 'list', if any (i.e. prev == NULL iff list->head == cell) * - * The cell is pfree'd, as is the List header if this was the last member. + * The cell is pfree'd, as is the gtm_List header if this was the last gtm_member. */ -List * -list_delete_cell(List *list, ListCell *cell, ListCell *prev) +gtm_List * +gtm_list_delete_cell(gtm_List *list, gtm_ListCell *cell, gtm_ListCell *prev) { check_list_invariants(list); - Assert(prev != NULL ? lnext(prev) == cell : list_head(list) == cell); + Assert(prev != NULL ? gtm_lnext(prev) == cell : gtm_list_head(list) == cell); /* * If we're about to delete the last node from the list, free the whole - * list instead and return NIL, which is the only valid representation of + * list instead and return gtm_NIL, which is the only valid representation of * a zero-length list. */ if (list->length == 1) { - list_free(list); - return NIL; + gtm_list_free(list); + return gtm_NIL; } /* @@ -379,21 +379,21 @@ list_delete_cell(List *list, ListCell *cell, ListCell *prev) /* * Delete the first cell in list that matches datum, if any. - * Equality is determined via equal(). + * Equality is determined via gtm_equal(). */ -List * -list_delete(List *list, void *datum) +gtm_List * +gtm_list_delete(gtm_List *list, void *datum) { - ListCell *cell; - ListCell *prev; + gtm_ListCell *cell; + gtm_ListCell *prev; check_list_invariants(list); prev = NULL; - foreach(cell, list) + gtm_foreach(cell, list) { - if (equal(lfirst(cell), datum)) - return list_delete_cell(list, cell, prev); + if (gtm_equal(gtm_lfirst(cell), datum)) + return gtm_list_delete_cell(list, cell, prev); prev = cell; } @@ -403,19 +403,19 @@ list_delete(List *list, void *datum) } /* As above, but use simple pointer equality */ -List * -list_delete_ptr(List *list, void *datum) +gtm_List * +gtm_list_delete_ptr(gtm_List *list, void *datum) { - ListCell *cell; - ListCell *prev; + gtm_ListCell *cell; + gtm_ListCell *prev; check_list_invariants(list); prev = NULL; - foreach(cell, list) + gtm_foreach(cell, list) { - if (lfirst(cell) == datum) - return list_delete_cell(list, cell, prev); + if (gtm_lfirst(cell) == datum) + return gtm_list_delete_cell(list, cell, prev); prev = cell; } @@ -428,53 +428,53 @@ list_delete_ptr(List *list, void *datum) /* * Delete the first element of the list. * - * This is useful to replace the Lisp-y code "list = lnext(list);" in cases + * This is useful to replace the Lisp-y code "list = gtm_lnext(list);" in cases * where the intent is to alter the list rather than just traverse it. - * Beware that the removed cell is freed, whereas the lnext() coding leaves + * Beware that the removed cell is freed, whereas the gtm_lnext() coding leaves * the original list head intact if there's another pointer to it. */ -List * -list_delete_first(List *list) +gtm_List * +gtm_list_delete_first(gtm_List *list) { check_list_invariants(list); - if (list == NIL) - return NIL; /* would an error be better? */ + if (list == gtm_NIL) + return gtm_NIL; /* would an error be better? */ - return list_delete_cell(list, list_head(list), NULL); + return gtm_list_delete_cell(list, gtm_list_head(list), NULL); } /* * Generate the union of two lists. This is calculated by copying - * list1 via list_copy(), then adding to it all the members of list2 + * list1 via gtm_list_copy(), then adding to it all the members of list2 * that aren't already in list1. * * Whether an element is already a member of the list is determined - * via equal(). + * via gtm_equal(). * * The returned list is newly-allocated, although the content of the * cells is the same (i.e. any pointed-to objects are not copied). * * NB: this function will NOT remove any duplicates that are present * in list1 (so it only performs a "union" if list1 is known unique to - * start with). Also, if you are about to write "x = list_union(x, y)" - * you probably want to use list_concat_unique() instead to avoid wasting + * start with). Also, if you are about to write "x = gtm_list_union(x, y)" + * you probably want to use gtm_list_concat_unique() instead to avoid wasting * the list cells of the old x list. * * This function could probably be implemented a lot faster if it is a * performance bottleneck. */ -List * -list_union(List *list1, List *list2) +gtm_List * +gtm_list_union(gtm_List *list1, gtm_List *list2) { - List *result; - ListCell *cell; + gtm_List *result; + gtm_ListCell *cell; - result = list_copy(list1); - foreach(cell, list2) + result = gtm_list_copy(list1); + gtm_foreach(cell, list2) { - if (!list_member(result, lfirst(cell))) - result = lappend(result, lfirst(cell)); + if (!gtm_list_member(result, gtm_lfirst(cell))) + result = gtm_lappend(result, gtm_lfirst(cell)); } check_list_invariants(result); @@ -482,21 +482,21 @@ list_union(List *list1, List *list2) } /* - * This variant of list_union() determines duplicates via simple + * This variant of gtm_list_union() determines duplicates via simple * pointer comparison. */ -List * -list_union_ptr(List *list1, List *list2) +gtm_List * +gtm_list_union_ptr(gtm_List *list1, gtm_List *list2) { - List *result; - ListCell *cell; + gtm_List *result; + gtm_ListCell *cell; - result = list_copy(list1); - foreach(cell, list2) + result = gtm_list_copy(list1); + gtm_foreach(cell, list2) { - if (!list_member_ptr(result, lfirst(cell))) - result = lappend(result, lfirst(cell)); + if (!gtm_list_member_ptr(result, gtm_lfirst(cell))) + result = gtm_lappend(result, gtm_lfirst(cell)); } check_list_invariants(result); @@ -513,23 +513,23 @@ list_union_ptr(List *list1, List *list2) * "intersection" if list1 is known unique beforehand. * * This variant works on lists of pointers, and determines list - * membership via equal(). Note that the list1 member will be pointed + * membership via gtm_equal(). Note that the list1 gtm_member will be pointed * to in the result. */ -List * -list_intersection(List *list1, List *list2) +gtm_List * +gtm_list_intersection(gtm_List *list1, gtm_List *list2) { - List *result; - ListCell *cell; + gtm_List *result; + gtm_ListCell *cell; - if (list1 == NIL || list2 == NIL) - return NIL; + if (list1 == gtm_NIL || list2 == gtm_NIL) + return gtm_NIL; - result = NIL; - foreach(cell, list1) + result = gtm_NIL; + gtm_foreach(cell, list1) { - if (list_member(list2, lfirst(cell))) - result = lappend(result, lfirst(cell)); + if (gtm_list_member(list2, gtm_lfirst(cell))) + result = gtm_lappend(result, gtm_lfirst(cell)); } check_list_invariants(result); @@ -543,21 +543,21 @@ list_intersection(List *list1, List *list2) * input lists. * * This variant works on lists of pointers, and determines list - * membership via equal() + * membership via gtm_equal() */ -List * -list_difference(List *list1, List *list2) +gtm_List * +gtm_list_difference(gtm_List *list1, gtm_List *list2) { - ListCell *cell; - List *result = NIL; + gtm_ListCell *cell; + gtm_List *result = gtm_NIL; - if (list2 == NIL) - return list_copy(list1); + if (list2 == gtm_NIL) + return gtm_list_copy(list1); - foreach(cell, list1) + gtm_foreach(cell, list1) { - if (!list_member(list2, lfirst(cell))) - result = lappend(result, lfirst(cell)); + if (!gtm_list_member(list2, gtm_lfirst(cell))) + result = gtm_lappend(result, gtm_lfirst(cell)); } check_list_invariants(result); @@ -565,22 +565,22 @@ list_difference(List *list1, List *list2) } /* - * This variant of list_difference() determines list membership via + * This variant of gtm_list_difference() determines list membership via * simple pointer equality. */ -List * -list_difference_ptr(List *list1, List *list2) +gtm_List * +gtm_list_difference_ptr(gtm_List *list1, gtm_List *list2) { - ListCell *cell; - List *result = NIL; + gtm_ListCell *cell; + gtm_List *result = gtm_NIL; - if (list2 == NIL) - return list_copy(list1); + if (list2 == gtm_NIL) + return gtm_list_copy(list1); - foreach(cell, list1) + gtm_foreach(cell, list1) { - if (!list_member_ptr(list2, lfirst(cell))) - result = lappend(result, lfirst(cell)); + if (!gtm_list_member_ptr(list2, gtm_lfirst(cell))) + result = gtm_lappend(result, gtm_lfirst(cell)); } check_list_invariants(result); @@ -591,49 +591,49 @@ list_difference_ptr(List *list1, List *list2) * Append datum to list, but only if it isn't already in the list. * * Whether an element is already a member of the list is determined - * via equal(). + * via gtm_equal(). */ -List * -list_append_unique(List *list, void *datum) +gtm_List * +gtm_list_append_unique(gtm_List *list, void *datum) { - if (list_member(list, datum)) + if (gtm_list_member(list, datum)) return list; else - return lappend(list, datum); + return gtm_lappend(list, datum); } /* - * This variant of list_append_unique() determines list membership via + * This variant of gtm_list_append_unique() determines list membership via * simple pointer equality. */ -List * -list_append_unique_ptr(List *list, void *datum) +gtm_List * +gtm_list_append_unique_ptr(gtm_List *list, void *datum) { - if (list_member_ptr(list, datum)) + if (gtm_list_member_ptr(list, datum)) return list; else - return lappend(list, datum); + return gtm_lappend(list, datum); } /* * Append to list1 each member of list2 that isn't already in list1. * * Whether an element is already a member of the list is determined - * via equal(). + * via gtm_equal(). * - * This is almost the same functionality as list_union(), but list1 is + * This is almost the same functionality as gtm_list_union(), but list1 is * modified in-place rather than being copied. Note also that list2's cells - * are not inserted in list1, so the analogy to list_concat() isn't perfect. + * are not inserted in list1, so the analogy to gtm_list_concat() isn't perfect. */ -List * -list_concat_unique(List *list1, List *list2) +gtm_List * +gtm_list_concat_unique(gtm_List *list1, gtm_List *list2) { - ListCell *cell; + gtm_ListCell *cell; - foreach(cell, list2) + gtm_foreach(cell, list2) { - if (!list_member(list1, lfirst(cell))) - list1 = lappend(list1, lfirst(cell)); + if (!gtm_list_member(list1, gtm_lfirst(cell))) + list1 = gtm_lappend(list1, gtm_lfirst(cell)); } check_list_invariants(list1); @@ -641,18 +641,18 @@ list_concat_unique(List *list1, List *list2) } /* - * This variant of list_concat_unique() determines list membership via + * This variant of gtm_list_concat_unique() determines list membership via * simple pointer equality. */ -List * -list_concat_unique_ptr(List *list1, List *list2) +gtm_List * +gtm_list_concat_unique_ptr(gtm_List *list1, gtm_List *list2) { - ListCell *cell; + gtm_ListCell *cell; - foreach(cell, list2) + gtm_foreach(cell, list2) { - if (!list_member_ptr(list1, lfirst(cell))) - list1 = lappend(list1, lfirst(cell)); + if (!gtm_list_member_ptr(list1, gtm_lfirst(cell))) + list1 = gtm_lappend(list1, gtm_lfirst(cell)); } check_list_invariants(list1); @@ -663,20 +663,20 @@ list_concat_unique_ptr(List *list1, List *list2) * Free all storage in a list, and optionally the pointed-to elements */ static void -list_free_private(List *list, bool deep) +list_free_private(gtm_List *list, bool deep) { - ListCell *cell; + gtm_ListCell *cell; check_list_invariants(list); - cell = list_head(list); + cell = gtm_list_head(list); while (cell != NULL) { - ListCell *tmp = cell; + gtm_ListCell *tmp = cell; - cell = lnext(cell); + cell = gtm_lnext(cell); if (deep) - pfree(lfirst(tmp)); + pfree(gtm_lfirst(tmp)); pfree(tmp); } @@ -690,10 +690,10 @@ list_free_private(List *list, bool deep) * free'd. * * On return, the argument to this function has been freed, so the - * caller would be wise to set it to NIL for safety's sake. + * caller would be wise to set it to gtm_NIL for safety's sake. */ void -list_free(List *list) +gtm_list_free(gtm_List *list) { list_free_private(list, false); } @@ -704,10 +704,10 @@ list_free(List *list) * list must contain a pointer to a palloc()'d region of memory!) * * On return, the argument to this function has been freed, so the - * caller would be wise to set it to NIL for safety's sake. + * caller would be wise to set it to gtm_NIL for safety's sake. */ void -list_free_deep(List *list) +gtm_list_free_deep(gtm_List *list) { /* * A "deep" free operation only makes sense on a list of pointers. @@ -718,15 +718,15 @@ list_free_deep(List *list) /* * Return a shallow copy of the specified list. */ -List * -list_copy(List *oldlist) +gtm_List * +gtm_list_copy(gtm_List *oldlist) { - List *newlist; - ListCell *newlist_prev; - ListCell *oldlist_cur; + gtm_List *newlist; + gtm_ListCell *newlist_prev; + gtm_ListCell *oldlist_cur; - if (oldlist == NIL) - return NIL; + if (oldlist == gtm_NIL) + return gtm_NIL; newlist = new_list(); newlist->length = oldlist->length; @@ -741,9 +741,9 @@ list_copy(List *oldlist) oldlist_cur = oldlist->head->next; while (oldlist_cur) { - ListCell *newlist_cur; + gtm_ListCell *newlist_cur; - newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur)); + newlist_cur = (gtm_ListCell *) palloc(sizeof(*newlist_cur)); newlist_cur->data = oldlist_cur->data; newlist_prev->next = newlist_cur; @@ -761,18 +761,18 @@ list_copy(List *oldlist) /* * Return a shallow copy of the specified list, without the first N elements. */ -List * -list_copy_tail(List *oldlist, int nskip) +gtm_List * +gtm_list_copy_tail(gtm_List *oldlist, int nskip) { - List *newlist; - ListCell *newlist_prev; - ListCell *oldlist_cur; + gtm_List *newlist; + gtm_ListCell *newlist_prev; + gtm_ListCell *oldlist_cur; if (nskip < 0) nskip = 0; /* would it be better to elog? */ - if (oldlist == NIL || nskip >= oldlist->length) - return NIL; + if (oldlist == gtm_NIL || nskip >= oldlist->length) + return gtm_NIL; newlist = new_list(); newlist->length = oldlist->length - nskip; @@ -794,9 +794,9 @@ list_copy_tail(List *oldlist, int nskip) oldlist_cur = oldlist_cur->next; while (oldlist_cur) { - ListCell *newlist_cur; + gtm_ListCell *newlist_cur; - newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur)); + newlist_cur = (gtm_ListCell *) palloc(sizeof(*newlist_cur)); newlist_cur->data = oldlist_cur->data; newlist_prev->next = newlist_cur; @@ -819,20 +819,20 @@ list_copy_tail(List *oldlist, int nskip) */ #ifndef __GNUC__ -ListCell * -list_head(List *l) +gtm_ListCell * +gtm_list_head(gtm_List *l) { return l ? l->head : NULL; } -ListCell * -list_tail(List *l) +gtm_ListCell * +gtm_list_tail(gtm_List *l) { return l ? l->tail : NULL; } int -list_length(List *l) +gtm_list_length(gtm_List *l) { return l ? l->length : 0; } @@ -851,13 +851,13 @@ list_length(List *l) * Given a list, return its length. This is merely defined for the * sake of backward compatibility: we can't afford to define a macro * called "length", so it must be a function. New code should use the - * list_length() macro in order to avoid the overhead of a function + * gtm_list_length() macro in order to avoid the overhead of a function * call. */ -int length(List *list); +int gtm_length(gtm_List *list); int -length(List *list) +gtm_length(gtm_List *list) { - return list_length(list); + return gtm_list_length(list); } diff --git a/src/gtm/common/gtm_serialize.c b/src/gtm/common/gtm_serialize.c new file mode 100644 index 0000000000..e2b8624f13 --- /dev/null +++ b/src/gtm/common/gtm_serialize.c @@ -0,0 +1,974 @@ +/*------------------------------------------------------------------------- + * + * gtm_serialize.c + * Serialization management of GTM data + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * + * IDENTIFICATION + * src/gtm/common/gtm_serialize.c + * + *------------------------------------------------------------------------- + */ + +#include "gtm/gtm_c.h" +#include "gtm/elog.h" +#include "gtm/gtm.h" +#include "gtm/gtm_txn.h" +#include "gtm/gtm_seq.h" +#include "gtm/assert.h" +#include "gtm/register.h" +#include "gtm/stringinfo.h" +#include "gtm/libpq.h" +#include "gtm/pqformat.h" +#include "gtm/gtm_msg.h" + +#include "gen_alloc.h" + +#include "gtm/gtm_serialize.h" + +/* + * gtm_get_snapshotdata_size + * Get a serialized size of GTM_SnapshotData structure + * Corrected snapshort serialize data calculation. + * May 3rd, 2011, K.Suzuki + * + * Serialize of snapshot_data + * + * sn_xmin ---> sn_xmax ---> sn_recent_global_xmin + * ---> sn_xcnt ---> GXID * sn_xcnt + * |<--- sn_xip -->| + */ +size_t +gtm_get_snapshotdata_size(GTM_SnapshotData *data) +{ + size_t len = 0; + uint32 snapshot_elements; + + snapshot_elements = data->sn_xcnt; + len += sizeof(GlobalTransactionId); + len += sizeof(GlobalTransactionId); + len += sizeof(GlobalTransactionId); + len += sizeof(uint32); + len += sizeof(GlobalTransactionId) * snapshot_elements; + + return len; +} + +/* + * gtm_serialize_snapshotdata + * Serialize a GTM_SnapshotData structure + */ +size_t +gtm_serialize_snapshotdata(GTM_SnapshotData *data, char *buf, size_t buflen) +{ + int len = 0; + + memset(buf, 0, buflen); + + /* size check */ + if (gtm_get_snapshotdata_size(data) > buflen) + return 0; + + /* GTM_SnapshotData.sn_xmin */ + memcpy(buf + len, &(data->sn_xmin), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_SnapshotData.sn_xmax */ + memcpy(buf + len, &(data->sn_xmax), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_SnapshotData.sn_recent_global_xmin */ + memcpy(buf + len, &(data->sn_recent_global_xmin), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_SnapshotData.sn_xcnt */ + memcpy(buf + len, &(data->sn_xcnt), sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_SnapshotData.sn_xip */ +#if 0 + /* + * This block of code seems to be wrong. data->sn_xip is an array of GlobalTransacionIDs + * and the number of elements are indicated by sn_xcnt. + */ + memcpy(buf + len, &(data->sn_xip), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); +#else + if(data->sn_xcnt > 0) + { + memcpy(buf + len, data->sn_xip, sizeof(GlobalTransactionId) * data->sn_xcnt); + len += sizeof(GlobalTransactionId) * data->sn_xcnt; + } +#endif + + return len; +} + +/* ----------------------------------------------------- + * Deserialize a GTM_SnapshotData structure + * ----------------------------------------------------- + */ +size_t +gtm_deserialize_snapshotdata(GTM_SnapshotData *data, const char *buf, size_t buflen) +{ + size_t len = 0; + + /* GTM_SnapshotData.sn_xmin */ + memcpy(&(data->sn_xmin), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_SnapshotData.sn_xmax */ + memcpy(&(data->sn_xmax), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_SnapshotData.sn_recent_global_xmin */ + memcpy(&(data->sn_recent_global_xmin), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_SnapshotData.sn_xcnt */ + memcpy(&(data->sn_xcnt), buf + len, sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_SnapshotData.sn_xip */ + if (data->sn_xcnt > 0) + { + /* + * Please note that this function runs with TopMemoryContext. So we must + * free this area manually later. + */ + data->sn_xip = genAlloc(sizeof(GlobalTransactionId) * data->sn_xcnt); + memcpy(data->sn_xip, buf + len, sizeof(GlobalTransactionId) * data->sn_xcnt); + len += sizeof(GlobalTransactionId) * data->sn_xcnt; + } + else + { + data->sn_xip = NULL; + } + + return len; +} + + +/* + * gtm_get_transactioninfo_size + * Get a serialized size of GTM_TransactionInfo structure + * + * Original gti_gid serialization was just "null-terminated string". + * This should be prefixed with the length of the string. + */ +size_t +gtm_get_transactioninfo_size(GTM_TransactionInfo *data) +{ + size_t len = 0; + + if (data == NULL) + return len; + + len += sizeof(GTM_TransactionHandle); /* gti_handle */ + len += sizeof(GTM_ThreadID); /* gti_thread_id */ + len += sizeof(bool); /* gti_in_use */ + len += sizeof(GlobalTransactionId);/* gti_gxid */ + len += sizeof(GTM_TransactionStates); /* gti_state */ + len += sizeof(PGXC_NodeId);/* gti_coordid */ + len += sizeof(GlobalTransactionId);/* gti_xmin */ + len += sizeof(GTM_IsolationLevel); /* gti_isolevel */ + len += sizeof(bool); /* gti_readonly */ + len += sizeof(GTMProxy_ConnID);/* gti_backend_id */ + len += sizeof(uint32); /* gti_datanodecount */ + len += sizeof(PGXC_NodeId) * data->gti_datanodecount; + /* gti_datanodes */ + len += sizeof(uint32); /* gti_coordcount */ + len += sizeof(PGXC_NodeId) * data->gti_coordcount; + /* gti_coordinators */ + len += sizeof(uint32); + if (data->gti_gid != NULL) + len += strlen(data->gti_gid); /* gti_gid */ + + len += gtm_get_snapshotdata_size(&(data->gti_current_snapshot)); + /* gti_current_snapshot */ + len += sizeof(bool); /* gti_snapshot_set */ + /* NOTE: nothing to be done for gti_lock */ + len += sizeof(bool); /* gti_vacuum */ + + return len; +} + + +/* ----------------------------------------------------- + * Serialize a GTM_TransactionInfo structure + * ----------------------------------------------------- + */ +size_t +gtm_serialize_transactioninfo(GTM_TransactionInfo *data, char *buf, size_t buflen) +{ + int len = 0; + char *buf2; + int i; + + /* size check */ + if (gtm_get_transactioninfo_size(data) > buflen) + return 0; + + memset(buf, 0, buflen); + + /* GTM_TransactionInfo.gti_handle */ + memcpy(buf + len, &(data->gti_handle), sizeof(GTM_TransactionHandle)); + len += sizeof(GTM_TransactionHandle); + + /* GTM_TransactionInfo.gti_thread_id */ + memcpy(buf + len, &(data->gti_thread_id), sizeof(GTM_ThreadID)); + len += sizeof(GTM_ThreadID); + + /* GTM_TransactionInfo.gti_in_use */ + memcpy(buf + len, &(data->gti_in_use), sizeof(bool)); + len += sizeof(bool); + + /* GTM_TransactionInfo.gti_gxid */ + memcpy(buf + len, &(data->gti_gxid), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_TransactionInfo.gti_state */ + memcpy(buf + len, &(data->gti_state), sizeof(GTM_TransactionStates)); + len += sizeof(GTM_TransactionStates); + + /* GTM_TransactionInfo.gti_coordid */ + memcpy(buf + len, &(data->gti_coordid), sizeof(PGXC_NodeId)); + len += sizeof(PGXC_NodeId); + + /* GTM_TransactionInfo.gti_xmin */ + memcpy(buf + len, &(data->gti_xmin), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_TransactionInfo.gti_isolevel */ + memcpy(buf + len, &(data->gti_isolevel), sizeof(GTM_IsolationLevel)); + len += sizeof(GTM_IsolationLevel); + + /* GTM_TransactionInfo.gti_readonly */ + memcpy(buf + len, &(data->gti_readonly), sizeof(bool)); + len += sizeof(bool); + + /* GTM_TransactionInfo.gti_backend_id */ + memcpy(buf + len, &(data->gti_backend_id), sizeof(GTMProxy_ConnID)); + len += sizeof(GTMProxy_ConnID); + + /* GTM_TransactionInfo.gti_datanodecount */ + memcpy(buf + len, &(data->gti_datanodecount), sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_TransactionInfo.gti_datanodes */ + for (i = 0; i < data->gti_datanodecount; i++) + { + memcpy(buf + len, &(data->gti_datanodes[i]), sizeof(PGXC_NodeId)); + len += sizeof(PGXC_NodeId); + } + + /* GTM_TransactionInfo.gti_coordcount */ + memcpy(buf + len, &(data->gti_coordcount), sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_TransactionInfo.gti_coordinators */ + for (i = 0; i < data->gti_coordcount; i++) + { + memcpy(buf + len, &(data->gti_coordinators[i]), sizeof(PGXC_NodeId)); + len += sizeof(PGXC_NodeId); + } + + /* GTM_TransactionInfo.gti_gid */ + if (data->gti_gid != NULL) + { + uint32 gidlen; + + gidlen = (uint32)strlen(data->gti_gid); + memcpy(buf + len, &gidlen, sizeof(uint32)); + len += sizeof(uint32); + memcpy(buf + len, data->gti_gid, gidlen); + len += gidlen; + } + else + { + uint32 gidlen = 0; + + memcpy(buf + len, &gidlen, sizeof(uint32)); + len += sizeof(uint32); + } + + /* GTM_TransactionInfo.gti_current_snapshot */ + buf2 = malloc(gtm_get_snapshotdata_size(&(data->gti_current_snapshot))); + i = gtm_serialize_snapshotdata(&(data->gti_current_snapshot), + buf2, + gtm_get_snapshotdata_size(&(data->gti_current_snapshot))); + memcpy(buf + len, buf2, i); + free(buf2); + len += i; + + /* GTM_TransactionInfo.gti_snapshot_set */ + memcpy(buf + len, &(data->gti_snapshot_set), sizeof(bool)); + len += sizeof(bool); + + /* GTM_TransactionInfo.gti_lock would not be serialized. */ + + /* GTM_TransactionInfo.gti_vacuum */ + memcpy(buf + len, &(data->gti_vacuum), sizeof(bool)); + len += sizeof(bool); + + return len; +} + +/* ----------------------------------------------------- + * Deserialize a GTM_TransactionInfo structure + * ----------------------------------------------------- + */ +size_t +gtm_deserialize_transactioninfo(GTM_TransactionInfo *data, const char *buf, size_t maxlen) +{ + int len = 0; + int i; + + memset(data, 0, sizeof(GTM_TransactionInfo)); + + /* GTM_TransactionInfo.gti_handle */ + memcpy(&(data->gti_handle), buf + len, sizeof(GTM_TransactionHandle)); + len += sizeof(GTM_TransactionHandle); + + /* GTM_TransactionInfo.gti_thread_id */ + memcpy(&(data->gti_thread_id), buf + len, sizeof(GTM_ThreadID)); + len += sizeof(GTM_ThreadID); + + /* GTM_TransactionInfo.gti_in_use */ + memcpy(&(data->gti_in_use), buf + len, sizeof(bool)); + len += sizeof(bool); + + /* GTM_TransactionInfo.gti_gxid */ + memcpy(&(data->gti_gxid), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_TransactionInfo.gti_state */ + memcpy(&(data->gti_state), buf + len, sizeof(GTM_TransactionStates)); + len += sizeof(GTM_TransactionStates); + + /* GTM_TransactionInfo.gti_coordid */ + memcpy(&(data->gti_coordid), buf + len, sizeof(PGXC_NodeId)); + len += sizeof(PGXC_NodeId); + + /* GTM_TransactionInfo.gti_xmin */ + memcpy(&(data->gti_xmin), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_TransactionInfo.gti_isolevel */ + memcpy(&(data->gti_isolevel), buf + len, sizeof(GTM_IsolationLevel)); + len += sizeof(GTM_IsolationLevel); + + /* GTM_TransactionInfo.gti_readonly */ + memcpy(&(data->gti_readonly), buf + len, sizeof(bool)); + len += sizeof(bool); + + /* GTM_TransactionInfo.gti_backend_id */ + memcpy(&(data->gti_backend_id), buf + len, sizeof(GTMProxy_ConnID)); + len += sizeof(GTMProxy_ConnID); + + /* GTM_TransactionInfo.gti_datanodecount */ + memcpy(&(data->gti_datanodecount), buf + len, sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_TransactionInfo.gti_datanodes */ + if (data->gti_datanodes > 0) + data->gti_datanodes = (PGXC_NodeId *)genAlloc(sizeof(PGXC_NodeId) * data->gti_datanodecount); + else + data->gti_datanodes = NULL; + + for (i = 0; i < data->gti_datanodecount; i++) + { + memcpy(&(data->gti_datanodes[i]), buf + len, sizeof(PGXC_NodeId)); + len += sizeof(PGXC_NodeId); + } + + /* GTM_TransactionInfo.gti_coordcount */ + memcpy(&(data->gti_coordcount), buf + len, sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_TransactionInfo.gti_coordinators */ + if (data->gti_coordinators > 0) + data->gti_coordinators = (PGXC_NodeId *)genAlloc(sizeof(PGXC_NodeId) * data->gti_coordcount); + else + data->gti_coordinators = NULL; + + for (i = 0; i < data->gti_coordcount; i++) + { + PGXC_NodeId *cur = data->gti_coordinators; + + memcpy(cur, buf + len, sizeof(PGXC_NodeId)); + + len += sizeof(PGXC_NodeId); + cur++; + } + + /* GTM_TransactionInfo.gti_gid */ + { + uint32 gti_len; + + memcpy(>i_len, buf + len, sizeof(uint32)); + len += sizeof(uint32); + if (gti_len > 0) + { + data->gti_gid = (char *)genAlloc(gti_len+1); + memcpy(data->gti_gid, buf + len, gti_len); + data->gti_gid[gti_len] = 0; /* null-terminated */ + len += gti_len; + } + else + { + data->gti_gid = NULL; + } + } + + /* GTM_TransactionInfo.gti_current_snapshot */ + i = gtm_deserialize_snapshotdata(&(data->gti_current_snapshot), + buf + len, + sizeof(GTM_SnapshotData)); + len += i; + + /* GTM_TransactionInfo.gti_snapshot_set */ + memcpy(&(data->gti_snapshot_set), buf + len, sizeof(bool)); + len += sizeof(bool); + + /* GTM_TransactionInfo.gti_lock would not be serialized. */ + + /* GTM_TransactionInfo.gti_vacuum */ + memcpy(&(data->gti_vacuum), buf + len, sizeof(bool)); + len += sizeof(bool); + + return len; +} + + +size_t +gtm_get_transactions_size(GTM_Transactions *data) +{ + size_t len = 0; + int i; + + len += sizeof(uint32);/* gt_txn_count */ + len += sizeof(GTM_States);/* gt_gtm_state */ + + /* NOTE: nothing to be done for gt_XidGenLock */ + + len += sizeof(GlobalTransactionId); /* gt_nextXid */ + len += sizeof(GlobalTransactionId); /* gt_oldestXid */ + len += sizeof(GlobalTransactionId); /* gt_xidVacLimit */ + len += sizeof(GlobalTransactionId); /* gt_xidWarnLimit */ + len += sizeof(GlobalTransactionId); /* gt_xidStopLimit */ + len += sizeof(GlobalTransactionId); /* gt_xidWrapLimit */ + + len += sizeof(GlobalTransactionId); /* gt_latestCompletedXid */ + len += sizeof(GlobalTransactionId); /* gt_recent_global_xmin */ + + len += sizeof(int32); /* gt_lastslot */ + + len += sizeof(int32); /* txn_count */ + + for (i = 0; i < GTM_MAX_GLOBAL_TRANSACTIONS; i++) + { + len += sizeof(size_t); /* length */ + len += gtm_get_transactioninfo_size(&data->gt_transactions_array[i]); + } + + /* NOTE: nothing to be done for gt_open_transactions */ + /* NOTE: nothing to be done for gt_TransArrayLock */ + + return len; +} + +/* + * Return a number of serialized transactions. + */ +size_t +gtm_serialize_transactions(GTM_Transactions *data, char *buf, size_t buflen) +{ + int len = 0; + int i; + uint32 txn_count; + + /* size check */ + if (gtm_get_transactions_size(data) > buflen) + return 0; + + memset(buf, 0, buflen); + + /* GTM_Transactions.gt_txn_count */ + memcpy(buf + len, &(data->gt_txn_count), sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_Transactions.gt_gtm_state */ + memcpy(buf + len, &(data->gt_gtm_state), sizeof(GTM_States)); + len += sizeof(GTM_States); + + /* NOTE: nothing to be done for gt_XidGenLock */ + + /* GTM_Transactions.gt_nextXid */ + memcpy(buf + len, &(data->gt_nextXid), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_oldestXid */ + memcpy(buf + len, &(data->gt_oldestXid), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidVacLimit */ + memcpy(buf + len, &(data->gt_xidVacLimit), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidWarnLimit */ + memcpy(buf + len, &(data->gt_xidWarnLimit), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidStopLimit */ + memcpy(buf + len, &(data->gt_xidStopLimit), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidWrapLimit */ + memcpy(buf + len, &(data->gt_xidWrapLimit), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_latestCompletedXid */ + memcpy(buf + len, &(data->gt_latestCompletedXid), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_recent_global_xmin */ + memcpy(buf + len, &(data->gt_recent_global_xmin), sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_lastslot */ + memcpy(buf + len, &(data->gt_lastslot), sizeof(int32)); + len += sizeof(int32); + + /* Count up for valid transactions. */ + txn_count = 0; + + for (i = 0; i < GTM_MAX_GLOBAL_TRANSACTIONS; i++) + { + /* Select a used slot with the transaction array. */ + if (data->gt_transactions_array[i].gti_in_use == TRUE) + txn_count++; + } + + memcpy(buf + len, &txn_count, sizeof(int32)); + len += sizeof(int32); + + /* + * GTM_Transactions.gt_transactions_array + */ + for (i = 0; i < GTM_MAX_GLOBAL_TRANSACTIONS; i++) + { + char *buf2; + size_t buflen2, len2; + + /* + * Not to include invalid global transactions. + */ + if (data->gt_transactions_array[i].gti_gxid == InvalidGlobalTransactionId) + continue; + + buflen2 = gtm_get_transactioninfo_size(&data->gt_transactions_array[i]); + + /* store a length of following data. */ + memcpy(buf + len, &buflen2, sizeof(size_t)); + len += sizeof(size_t); + + buf2 = (char *)malloc(buflen2); + + len2 = gtm_serialize_transactioninfo(&data->gt_transactions_array[i], + buf2, + buflen2); + + /* store a serialized GTM_TransactionInfo structure. */ + memcpy(buf + len, buf2, len2); + len += len2; + + free(buf2); + } + + /* NOTE: nothing to be done for gt_TransArrayLock */ + return len; +} + + +/* + * Return a number of deserialized transactions. + */ +size_t +gtm_deserialize_transactions(GTM_Transactions *data, const char *buf, size_t maxlen) +{ + int len = 0; + int i; + uint32 txn_count; + + /* GTM_Transactions.gt_txn_count */ + memcpy(&(data->gt_txn_count), buf + len, sizeof(uint32)); + len += sizeof(uint32); + + /* GTM_Transactions.gt_gtm_state */ + memcpy(&(data->gt_gtm_state), buf + len, sizeof(GTM_States)); + len += sizeof(GTM_States); + + /* NOTE: nothing to be done for gt_XidGenLock */ + + /* GTM_Transactions.gt_nextXid */ + memcpy(&(data->gt_nextXid), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_oldestXid */ + memcpy(&(data->gt_oldestXid), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidVacLimit */ + memcpy(&(data->gt_xidVacLimit), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidWarnLimit */ + memcpy(&(data->gt_xidWarnLimit), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidStopLimit */ + memcpy(&(data->gt_xidStopLimit), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_xidWrapLimit */ + memcpy(&(data->gt_xidWrapLimit), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_latestCompletedXid */ + memcpy(&(data->gt_latestCompletedXid), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_recent_global_xmin */ + memcpy(&(data->gt_recent_global_xmin), buf + len, sizeof(GlobalTransactionId)); + len += sizeof(GlobalTransactionId); + + /* GTM_Transactions.gt_lastslot */ + memcpy(&(data->gt_lastslot), buf + len, sizeof(int32)); + len += sizeof(int32); + + /* A number of valid transactions */ + memcpy(&txn_count, buf + len, sizeof(int32)); + len += sizeof(int32); + + /* GTM_Transactions.gt_transactions_array */ + for (i = 0; i < txn_count; i++) + { + size_t buflen2, len2; + + /* read a length of following data. */ + memcpy(&buflen2, buf + len, sizeof(size_t)); + len += sizeof(size_t); + + /* reada serialized GTM_TransactionInfo structure. */ + len2 = gtm_deserialize_transactioninfo(&(data->gt_transactions_array[i]), + buf + len, + buflen2); + + len += len2; + } + + /* NOTE: nothing to be done for gt_TransArrayLock */ + + return txn_count; +} + + +/* + * Return size of PGXC node information + */ +size_t +gtm_get_pgxcnodeinfo_size(GTM_PGXCNodeInfo *data) +{ + size_t len = 0; + + len += sizeof(GTM_PGXCNodeType); /* type */ + len += sizeof(GTM_PGXCNodeId); /* nodenum */ + len += sizeof(GTM_PGXCNodeId); /* proxynum */ + len += sizeof(GTM_PGXCNodePort); /* port */ + + len += sizeof(uint32); /* ipaddress length */ + if (data->ipaddress != NULL) /* ipaddress */ + len += strlen(data->ipaddress); + + len += sizeof(uint32); /* datafolder length */ + if (data->datafolder != NULL) /* datafolder */ + len += strlen(data->datafolder); + + len += sizeof(GTM_PGXCNodeStatus); /* status */ + + return len; +} + +/* + * Return a serialize number of PGXC node information + */ +size_t +gtm_serialize_pgxcnodeinfo(GTM_PGXCNodeInfo *data, char *buf, size_t buflen) +{ + size_t len = 0; + uint32 len_wk; + + /* size check */ + if (gtm_get_pgxcnodeinfo_size(data) > buflen) + return 0; + + memset(buf, 0, buflen); + + /* GTM_PGXCNodeInfo.type */ + memcpy(buf + len, &(data->type), sizeof(GTM_PGXCNodeType)); + len += sizeof(GTM_PGXCNodeType); + + /* GTM_PGXCNodeInfo.nodenum */ + memcpy(buf + len, &(data->nodenum), sizeof(GTM_PGXCNodeId)); + len += sizeof(GTM_PGXCNodeId); + + /* GTM_PGXCNodeInfo.proxynum */ + memcpy(buf + len, &(data->proxynum), sizeof(GTM_PGXCNodeId)); + len += sizeof(GTM_PGXCNodeId); + + /* GTM_PGXCNodeInfo.port */ + memcpy(buf + len, &(data->port), sizeof(GTM_PGXCNodePort)); + len += sizeof(GTM_PGXCNodePort); + + /* GTM_PGXCNodeInfo.ipaddress */ + if (data->ipaddress == NULL) + len_wk = 0; + else + len_wk = (uint32)strlen(data->ipaddress); + + memcpy(buf + len, &len_wk, sizeof(uint32)); + len += sizeof(uint32); + if (len_wk > 0) + { + memcpy(buf + len, data->ipaddress, len_wk); + len += len_wk; + } + + /* GTM_PGXCNodeInfo.datafolder */ + if (data->datafolder == NULL) + len_wk = 0; + else + len_wk = (uint32)strlen(data->datafolder); + + memcpy(buf + len, &len_wk, sizeof(uint32)); + len += sizeof(uint32); + if (len_wk > 0) + { + memcpy(buf + len, data->datafolder, len_wk); + len += len_wk; + } + + /* GTM_PGXCNodeInfo.status */ + memcpy(buf + len, &(data->status), sizeof(GTM_PGXCNodeStatus)); + len += sizeof(GTM_PGXCNodeStatus); + + /* NOTE: nothing to be done for node_lock */ + return len; +} + + +/* + * Return a deserialize number of PGXC node information + */ +size_t +gtm_deserialize_pgxcnodeinfo(GTM_PGXCNodeInfo *data, const char *buf, size_t buflen) +{ + size_t len = 0; + uint32 len_wk; + + /* GTM_PGXCNodeInfo.type */ + memcpy(&(data->type), buf + len, sizeof(GTM_PGXCNodeType)); + len += sizeof(GTM_PGXCNodeType); + + /* GTM_PGXCNodeInfo.nodenum */ + memcpy(&(data->nodenum), buf + len, sizeof(GTM_PGXCNodeId)); + len += sizeof(GTM_PGXCNodeId); + + /* GTM_PGXCNodeInfo.proxynum */ + memcpy(&(data->proxynum), buf + len, sizeof(GTM_PGXCNodeId)); + len += sizeof(GTM_PGXCNodeId); + + /* GTM_PGXCNodeInfo.port */ + memcpy(&(data->port), buf + len, sizeof(GTM_PGXCNodePort)); + len += sizeof(GTM_PGXCNodePort); + + /* GTM_PGXCNodeInfo.ipaddress */ + memcpy(&len_wk, buf + len, sizeof(uint32)); + len += sizeof(uint32); + if (len_wk == 0) + { + data->ipaddress = NULL; + } + else + { + data->ipaddress = (char *)genAlloc(len_wk + 1); + memcpy(data->ipaddress, buf + len, (size_t)len_wk); + data->ipaddress[len_wk] = 0; /* null_terminate */ + len += len_wk; + } + + /* GTM_PGXCNodeInfo.datafolder */ + memcpy(&len_wk, buf + len, sizeof(uint32)); + len += sizeof(uint32); + if (len_wk == 0) + { + data->datafolder = NULL; + } + else + { + data->datafolder = (char *)genAlloc(len_wk + 1); + memcpy(data->datafolder, buf + len, (size_t)len_wk); + data->datafolder[len_wk] = 0; /* null_terminate */ + len += len_wk; + } + + /* GTM_PGXCNodeInfo.status */ + memcpy(&(data->status), buf + len, sizeof(GTM_PGXCNodeStatus)); + len += sizeof(GTM_PGXCNodeStatus); + + /* NOTE: nothing to be done for node_lock */ + + return len; +} + + +/* + * Return size of sequence information + */ +size_t +gtm_get_sequence_size(GTM_SeqInfo *seq) +{ + size_t len = 0; + + len += sizeof(uint32);/* gs_key.gsk_keylen */ + len += seq->gs_key->gsk_keylen; /* gs_key.gsk_key */ + len += sizeof(GTM_SequenceKeyType); /* gs_key.gsk_type */ + len += sizeof(GTM_Sequence); /* gs_value */ + len += sizeof(GTM_Sequence); /* gs_init_value */ + len += sizeof(GTM_Sequence); /* gs_last_value */ + len += sizeof(GTM_Sequence); /* gs_increment_by */ + len += sizeof(GTM_Sequence); /* gs_min_value */ + len += sizeof(GTM_Sequence); /* gs_max_value */ + len += sizeof(bool); /* gs_cycle */ + len += sizeof(bool); /* gs_called */ + len += sizeof(uint32); /* gs_ref_count */ + len += sizeof(uint32); /* ge_state */ + + return len; +} + +/* + * Return number of serialized sequence information + */ +size_t +gtm_serialize_sequence(GTM_SeqInfo *s, char *buf, size_t buflen) +{ + size_t len = 0; + + /* size check */ + if (gtm_get_sequence_size(s) > buflen) + return 0; + + memset(buf, 0, buflen); + + memcpy(buf + len, &s->gs_key->gsk_keylen, sizeof(uint32)); + len += sizeof(uint32);/* gs_key.gsk_keylen */ + + memcpy(buf + len, s->gs_key->gsk_key, s->gs_key->gsk_keylen); + len += s->gs_key->gsk_keylen; /* gs_key.gsk_key */ + + memcpy(buf + len, &s->gs_key->gsk_type, sizeof(GTM_SequenceKeyType)); + len += sizeof(GTM_SequenceKeyType); /* gs_key.gsk_type */ + + memcpy(buf + len, &s->gs_value, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_value */ + + memcpy(buf + len, &s->gs_init_value, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_init_value */ + + memcpy(buf + len, &s->gs_last_value, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_last_value */ + + memcpy(buf + len, &s->gs_increment_by, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_increment_by */ + + memcpy(buf + len, &s->gs_min_value, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_min_value */ + + memcpy(buf + len, &s->gs_max_value, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_max_value */ + + memcpy(buf + len, &s->gs_cycle, sizeof(bool)); + len += sizeof(bool); /* gs_cycle */ + + memcpy(buf + len, &s->gs_called, sizeof(bool)); + len += sizeof(bool); /* gs_called */ + + memcpy(buf + len, &s->gs_ref_count, sizeof(uint32)); + len += sizeof(uint32); /* gs_ref_count */ + + memcpy(buf + len, &s->gs_state, sizeof(uint32)); + len += sizeof(uint32); /* gs_state */ + + return len; +} + +/* + * Return number of deserialized sequence information + */ +GTM_SeqInfo * +gtm_deserialize_sequence(const char *buf, size_t buflen) +{ + size_t len = 0; + GTM_SeqInfo *seq; + + seq = (GTM_SeqInfo *)genAlloc0(sizeof(GTM_SeqInfo)); + seq->gs_key = (GTM_SequenceKeyData *)genAlloc0(sizeof(GTM_SequenceKeyData)); + + memcpy(&seq->gs_key->gsk_keylen, buf + len, sizeof(uint32)); + len += sizeof(uint32);/* gs_key.gsk_keylen */ + + seq->gs_key->gsk_key = (char *)genAlloc0(seq->gs_key->gsk_keylen+1); + memcpy(seq->gs_key->gsk_key, buf + len, seq->gs_key->gsk_keylen); + len += seq->gs_key->gsk_keylen;/* gs_key.gsk_key */ + + memcpy(&seq->gs_key->gsk_type, buf + len, sizeof(GTM_SequenceKeyType)); + len += sizeof(GTM_SequenceKeyType); /* gs_key.gsk_type */ + + memcpy(&seq->gs_value, buf + len, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_value */ + + memcpy(&seq->gs_init_value, buf + len, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_init_value */ + + memcpy(&seq->gs_last_value, buf + len, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_last_value */ + + memcpy(&seq->gs_increment_by, buf + len, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_increment_by */ + + memcpy(&seq->gs_min_value, buf + len, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_min_value */ + + memcpy(&seq->gs_max_value, buf + len, sizeof(GTM_Sequence)); + len += sizeof(GTM_Sequence); /* gs_max_value */ + + memcpy(&seq->gs_cycle, buf + len, sizeof(bool)); + len += sizeof(bool); /* gs_cycle */ + + memcpy(&seq->gs_called, buf + len, sizeof(bool)); + len += sizeof(bool); /* gs_called */ + + memcpy(&seq->gs_ref_count, buf + len, sizeof(uint32)); + len += sizeof(uint32); + + memcpy(&seq->gs_state, buf + len, sizeof(uint32)); + len += sizeof(uint32); + + return seq; +} diff --git a/src/gtm/common/gtm_serialize_debug.c b/src/gtm/common/gtm_serialize_debug.c new file mode 100644 index 0000000000..5af6403132 --- /dev/null +++ b/src/gtm/common/gtm_serialize_debug.c @@ -0,0 +1,94 @@ +/*------------------------------------------------------------------------- + * + * gtm_serialize_debug.c + * Debug functionalities of serialization management + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * + * IDENTIFICATION + * src/gtm/common/gtm_serialize_debug.c + * + *------------------------------------------------------------------------- + */ + +#include "gtm/gtm_c.h" +#include "gtm/elog.h" +#include "gtm/palloc.h" +#include "gtm/gtm.h" +#include "gtm/gtm_txn.h" +#include "gtm/gtm_seq.h" +#include "gtm/assert.h" +#include "gtm/register.h" +#include "gtm/stringinfo.h" +#include "gtm/libpq.h" +#include "gtm/pqformat.h" +#include "gtm/gtm_msg.h" + +#include "gtm/gtm_serialize.h" + +void +dump_transactioninfo_elog(GTM_TransactionInfo *txn) +{ + int ii; + + elog(LOG, " ========= GTM_TransactionInfo ========="); + elog(LOG, "gti_handle: %d", txn->gti_handle); + elog(LOG, "gti_thread_id: %ld", txn->gti_thread_id); + elog(LOG, "gti_in_use: %d", txn->gti_in_use); + elog(LOG, "gti_gxid: %d", txn->gti_gxid); + elog(LOG, "gti_state: %d", txn->gti_state); + elog(LOG, "gti_coordid: %d", txn->gti_coordid); + elog(LOG, "gti_xmin: %d", txn->gti_xmin); + elog(LOG, "gti_isolevel: %d", txn->gti_isolevel); + elog(LOG, "gti_readonly: %d", txn->gti_readonly); + elog(LOG, "gti_backend_id: %d", txn->gti_backend_id); + elog(LOG, "gti_datanodecount: %d", txn->gti_datanodecount); + elog(LOG, "gti_coordcount: %d", txn->gti_coordcount); + elog(LOG, "gti_gid: %s", txn->gti_gid); + + elog(LOG, " sn_xmin: %d", txn->gti_current_snapshot.sn_xmin); + elog(LOG, " sn_xmax: %d", txn->gti_current_snapshot.sn_xmax); + elog(LOG, " sn_recent_global_xmin: %d", txn->gti_current_snapshot.sn_recent_global_xmin); + elog(LOG, " sn_xcnt: %d", txn->gti_current_snapshot.sn_xcnt); + + /* Print all the GXIDs in snapshot */ + for (ii = 0; ii < txn->gti_current_snapshot.sn_xcnt; ii++) + { + elog (LOG, " sn_xip[%d]: %d", ii, txn->gti_current_snapshot.sn_xip[ii]); + } + + elog(LOG, "gti_snapshot_set: %d", txn->gti_snapshot_set); + elog(LOG, "gti_vacuum: %d", txn->gti_vacuum); + elog(LOG, " ========================================"); +} + +void +dump_transactions_elog(GTM_Transactions *txn, int num_txn) +{ + int i; + + elog(LOG, "============ GTM_Transactions ============"); + elog(LOG, " gt_txn_count: %d", txn->gt_txn_count); + elog(LOG, " gt_XidGenLock: %p", &txn->gt_XidGenLock); + elog(LOG, " gt_nextXid: %d", txn->gt_nextXid); + elog(LOG, " gt_oldestXid: %d", txn->gt_oldestXid); + elog(LOG, " gt_xidVacLimit: %d", txn->gt_xidVacLimit); + elog(LOG, " gt_xidWarnLimit: %d", txn->gt_xidWarnLimit); + elog(LOG, " gt_xidStopLimit: %d", txn->gt_xidStopLimit); + elog(LOG, " gt_xidWrapLimit: %d", txn->gt_xidWrapLimit); + elog(LOG, " gt_latestCompletedXid: %d", txn->gt_latestCompletedXid); + elog(LOG, " gt_recent_global_xmin: %d", txn->gt_recent_global_xmin); + elog(LOG, " gt_lastslot: %d", txn->gt_lastslot); + + for (i = 0; i < num_txn; i++) + { + if (txn->gt_transactions_array[i].gti_gxid != InvalidGlobalTransactionId) + dump_transactioninfo_elog(&txn->gt_transactions_array[i]); + } + + elog(LOG, " gt_TransArrayLock: %p", &txn->gt_TransArrayLock); + elog(LOG, "=========================================="); +} diff --git a/src/gtm/common/gtm_utils.c b/src/gtm/common/gtm_utils.c new file mode 100644 index 0000000000..bce9bcb8a2 --- /dev/null +++ b/src/gtm/common/gtm_utils.c @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * gtm_utils.c + * Utililies of GTM + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * + * IDENTIFICATION + * src/gtm/common/gtm_utils.c + * + *------------------------------------------------------------------------- + */ +#include "gtm/gtm_utils.h" +#include "gtm/elog.h" +#include "gtm/gtm.h" + +/* + * gtm_report_failure() is an utility function to report fatal failure + * which occureed inside GTM to XCM, especially communication errors. + * + * failed_conn is null-able when failed to establish a connection + * with other node. + */ +#if 0 +/* + * PGXCTODO: This portion of code needs XCM support + * to be able to report GTM failures to XC watcher and + * enable a GTM reconnection kick. + */ +void +gtm_report_failure(GTM_Conn *failed_conn) +{ + elog(LOG, "Calling report_xcwatch_gtm_failure()..."); + return; +} +#endif diff --git a/src/gtm/common/mcxt.c b/src/gtm/common/mcxt.c index 1cb007982d..90c63f08f1 100644 --- a/src/gtm/common/mcxt.c +++ b/src/gtm/common/mcxt.c @@ -760,4 +760,13 @@ pgport_pfree(void *pointer) pfree(pointer); } + #endif + +#include "gen_alloc.h" +void *current_memcontext(void); +void *current_memcontext(void) +{ + return((void *)CurrentMemoryContext); +} +Gen_Alloc genAlloc_class = {MemoryContextAlloc, MemoryContextAllocZero, repalloc, pfree, current_memcontext}; diff --git a/src/gtm/common/stringinfo.c b/src/gtm/common/stringinfo.c index 35e4cd87f1..821ca67640 100644 --- a/src/gtm/common/stringinfo.c +++ b/src/gtm/common/stringinfo.c @@ -41,6 +41,41 @@ makeStringInfo(void) } /* + * dupStringInfo + * + * Get new StringInfo and copy the original to it. + */ +StringInfo +dupStringInfo(StringInfo orig) +{ + StringInfo new; + + new = makeStringInfo(); + if (!new) + return(new); + + if (orig->len > 0) + { + appendBinaryStringInfo(new, orig->data, orig->len); + new->cursor = orig->cursor; + } + return(new); +} + +/* + * copyStringInfo + * Deep copy: Data part is copied too. Cursor of the destination is + * initialized to zero. + */ +void +copyStringInfo(StringInfo to, StringInfo from) +{ + resetStringInfo(to); + appendBinaryStringInfo(to, from->data, from->len); + return; +} + +/* * initStringInfo * * Initialize a StringInfoData struct (with previously undefined contents) diff --git a/src/gtm/gtm_ctl/gtm_ctl.c b/src/gtm/gtm_ctl/gtm_ctl.c index 6a87b88011..18fc3f9240 100644 --- a/src/gtm/gtm_ctl/gtm_ctl.c +++ b/src/gtm/gtm_ctl/gtm_ctl.c @@ -42,7 +42,9 @@ typedef enum NO_COMMAND = 0, START_COMMAND, STOP_COMMAND, + PROMOTE_COMMAND, RESTART_COMMAND, + STATUS_COMMAND } CtlCommand; #define DEFAULT_WAIT 60 @@ -92,7 +94,7 @@ static char gtmopts_file[MAXPGPATH]; static char pid_file[MAXPGPATH]; /* - * Write errors to stderr (or by equal means when stderr is + * Write errors to stderr (or by gtm_equal means when stderr is * not available). */ static void @@ -299,7 +301,6 @@ start_gtm(void) } - /* * Find the pgport and try a connection */ @@ -540,6 +541,36 @@ do_stop(void) } } +static void +do_promote(void) +{ + pgpid_t pid; + + pid = get_pgpid(); + + if (pid == 0) /* no pid file */ + { + write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file); + write_stderr(_("Is server running?\n")); + exit(1); + } + else if (pid < 0) /* standalone backend, not gtm */ + { + pid = -pid; + write_stderr(_("%s: cannot promote server; " + "single-user server is running (PID: %ld)\n"), + progname, pid); + exit(1); + } + + if (kill((pid_t) pid, SIGUSR1) != 0) + { + write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"), progname, pid, + strerror(errno)); + exit(1); + } +} + /* * restart/reload routines @@ -621,6 +652,97 @@ do_restart(void) } +static void +do_status(void) +{ + pgpid_t pid; + char datpath[MAXPGPATH]; + int mode; + FILE *pidf; + + /* + * Read a PID file to get GTM server status instead of attaching shared memory. + */ + pidf = fopen(pid_file, "r"); + if (pidf == NULL) + { + write_stderr(_("%s: could not open PID file \"%s\": %s\n"), + progname, pid_file, strerror(errno)); + exit(1); + } + + if (fscanf(pidf, "%ld", &pid) != 1) + { + write_stderr(_("%s: invalid data in PID file \"%s\"\n"), + progname, pid_file); + exit(1); + } + + if (fscanf(pidf, "%s", datpath) != 1) + { + write_stderr(_("%s: invalid data in PID file \"%s\"\n"), + progname, pid_file); + exit(1); + } + + if (fscanf(pidf, "%d", &mode) != 1) + { + write_stderr(_("%s: invalid data in PID file \"%s\"\n"), + progname, pid_file); + exit(1); + } + + fclose(pidf); + + printf("pid: %ld\n", pid); + printf("data: %s\n", datpath); + printf("active: %d\n", mode); + +#ifdef NOT_USED + pid = get_pgpid(); + + if (pid == 0) /* no pid file */ + { + write_stderr(_("%s: PID file \"%s\" does not exist\n"), + progname, pid_file); + write_stderr(_("Is server running?\n")); + exit(1); + } + else if (pid < 0) /* standalone backend, not gtm */ + { + pid = -pid; + if (gtm_is_alive((pid_t) pid)) + { + write_stderr(_("%s: cannot get server status; " + "single-user server is running (PID: %ld)\n"), + progname, pid); + write_stderr(_("Please terminate the single-user server and try again.\n")); + exit(1); + } + } + + if (!gtm_is_alive((pid_t) pid)) + { + write_stderr(_("%s: old server process (PID: %ld) seems to be gone\n"), + progname, pid); + exit(1); + } + + /* + * status check stuffs. + */ + exitcode = check_gtm(); + if (exitcode != 0) + { + write_stderr(_("%s: could not get server status: exit code was %d\n"), + progname, exitcode); + exit(1); + } +#endif /* NOT_USED */ +} + + + /* * utility routines */ @@ -660,19 +782,19 @@ static void do_help(void) { printf(_("%s is a utility to start, stop or restart,\n" - "a GTM server or GTM proxy.\n\n"), progname); + "a GTM server, a GTM standby or GTM proxy.\n\n"), progname); printf(_("Usage:\n")); - printf(_(" %s start -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname); - printf(_(" %s stop -S STARTUP_MODE [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname); - printf(_(" %s restart -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n" + printf(_(" %s start -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname); + printf(_(" %s stop -S STARTUP_MODE [-W] [-t SECS] [-D DATADIR] [-m SHUTDOWN-MODE]\n"), progname); + printf(_(" %s promote -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR]\n"), progname); + printf(_(" %s restart -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-m SHUTDOWN-MODE]\n" " [-o \"OPTIONS\"]\n"), progname); + printf(_(" %s status -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR]\n"), progname); printf(_("\nCommon options:\n")); printf(_(" -D DATADIR location of the database storage area\n")); printf(_(" -i NODE_ID set gtm_proxy ID registered on GTM\n")); printf(_(" (option ignored if used with GTM)\n")); - printf(_(" -S set gtm or gtm_proxy to launch one of them\n")); - printf(_(" -s, only print errors, no informational messages\n")); printf(_(" -t SECS seconds to wait when using -w option\n")); printf(_(" -w wait until operation completes\n")); printf(_(" -W do not wait until operation completes\n")); @@ -680,7 +802,7 @@ do_help(void) printf(_("(The default is to wait for shutdown, but not for start or restart.)\n\n")); printf(_("\nOptions for start or restart:\n")); - printf(_(" -S STARTUP-MODE can be \"gtm\" or \"gtm_proxy\"\n")); + printf(_(" -S STARTUP-MODE can be \"gtm\", \"gtm_standby\" or \"gtm_proxy\"\n")); printf(_(" -l FILENAME write (or append) server log to FILENAME\n")); printf(_(" -o OPTIONS command line options to pass to gtm\n" " (GTM server executable)\n")); @@ -816,6 +938,14 @@ main(int argc, char **argv) break; case 'S': gtm_app = xstrdup(optarg); + if (strcmp(gtm_app,"gtm_proxy") != 0 + && strcmp(gtm_app,"gtm_standby") != 0 + && strcmp(gtm_app,"gtm") != 0) + { + write_stderr(_("%s: %s launch name set not correct\n"), progname, gtm_app); + do_advice(); + exit(1); + } break; case 't': wait_seconds = atoi(optarg); @@ -849,11 +979,16 @@ main(int argc, char **argv) ctl_command = START_COMMAND; else if (strcmp(argv[optind], "stop") == 0) ctl_command = STOP_COMMAND; + else if (strcmp(argv[optind], "promote") == 0) + ctl_command = PROMOTE_COMMAND; else if (strcmp(argv[optind], "restart") == 0) ctl_command = RESTART_COMMAND; + else if (strcmp(argv[optind], "status") == 0) + ctl_command = STATUS_COMMAND; else { - write_stderr(_("%s: unrecognized operation mode \"%s\"\n"), progname, argv[optind]); + write_stderr(_("%s: unrecognized operation mode \"%s\"\n"), + progname, argv[optind]); do_advice(); exit(1); } @@ -931,6 +1066,8 @@ main(int argc, char **argv) { case RESTART_COMMAND: case START_COMMAND: + case PROMOTE_COMMAND: + case STATUS_COMMAND: do_wait = false; break; case STOP_COMMAND: @@ -941,6 +1078,26 @@ main(int argc, char **argv) } } +#if 0 + if (gtm_data) + { + if (strcmp(gtm_app,"gtm_proxy") == 0) + { + snprintf(pid_file, MAXPGPATH, "%s/gtm_proxy.pid", gtm_data); + snprintf(gtmopts_file, MAXPGPATH, "%s/gtm_proxy.opts", gtm_data); + } + else if (strcmp(gtm_app,"gtm") == 0) + { + snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data); + snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data); + } + else if (strcmp(gtm_app,"gtm_standby") == 0) + { + snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data); + snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data); + } + } +#else /* Build strings for pid file and option file */ if (strcmp(gtm_app,"gtm_proxy") == 0) { @@ -952,6 +1109,15 @@ main(int argc, char **argv) snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data); snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data); } + else if (strcmp(gtm_app,"gtm_standby") == 0) + { + snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data); + snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data); + } +#endif + + if (ctl_command==STATUS_COMMAND) + gtm_opts = xstrdup("-c"); switch (ctl_command) { @@ -961,9 +1127,15 @@ main(int argc, char **argv) case STOP_COMMAND: do_stop(); break; + case PROMOTE_COMMAND: + do_promote(); + break; case RESTART_COMMAND: do_restart(); break; + case STATUS_COMMAND: + do_status(); + break; default: break; } diff --git a/src/gtm/libpq/pqcomm.c b/src/gtm/libpq/pqcomm.c index d7c3081864..9647b8488c 100644 --- a/src/gtm/libpq/pqcomm.c +++ b/src/gtm/libpq/pqcomm.c @@ -65,6 +65,8 @@ *------------------------ */ +#include "pg_config.h" + #include <signal.h> #include <fcntl.h> #include <grp.h> @@ -73,6 +75,7 @@ #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> +#include <sys/types.h> #include <netdb.h> #include <netinet/in.h> #ifdef HAVE_NETINET_TCP_H @@ -344,7 +347,7 @@ StreamConnection(int server_fd, Port *port) port->raddr.salen = sizeof(port->raddr.addr); if ((port->sock = accept(server_fd, (struct sockaddr *) & port->raddr.addr, - &port->raddr.salen)) < 0) + (socklen_t *)&port->raddr.salen)) < 0) { ereport(LOG, (EACCES, @@ -375,7 +378,7 @@ StreamConnection(int server_fd, Port *port) port->laddr.salen = sizeof(port->laddr.addr); if (getsockname(port->sock, (struct sockaddr *) & port->laddr.addr, - &port->laddr.salen) < 0) + (socklen_t *)&port->laddr.salen) < 0) { elog(LOG, "getsockname() failed: %m"); return STATUS_ERROR; @@ -597,6 +600,7 @@ pq_getbytes(Port *myport, char *s, size_t len) return 0; } +#ifdef NOT_USED /* -------------------------------- * pq_discardbytes - throw away a known number of bytes * @@ -626,6 +630,7 @@ pq_discardbytes(Port *myport, size_t len) } return 0; } +#endif /* NOT_USED */ /* -------------------------------- * pq_getstring - get a null terminated string from connection diff --git a/src/gtm/main/Makefile b/src/gtm/main/Makefile index bcdaf89061..d3afcca563 100644 --- a/src/gtm/main/Makefile +++ b/src/gtm/main/Makefile @@ -3,7 +3,7 @@ top_build_dir=../.. include $(top_build_dir)/gtm/Makefile.global -OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o ../common/libgtm.a ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a +OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o gtm_standby.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a LDFLAGS=-L$(top_build_dir)/common -L$(top_build_dir)/libpq LIBS=-lpthread diff --git a/src/gtm/main/gtm_seq.c b/src/gtm/main/gtm_seq.c index 35dc023ea0..2c2c7d6423 100644 --- a/src/gtm/main/gtm_seq.c +++ b/src/gtm/main/gtm_seq.c @@ -13,19 +13,22 @@ * *------------------------------------------------------------------------- */ -#include "gtm/gtm_c.h" +#include <unistd.h> + +#include "gtm/assert.h" +#include "gtm/elog.h" #include "gtm/gtm.h" +#include "gtm/gtm_client.h" #include "gtm/gtm_seq.h" -#include "gtm/assert.h" -#include "gtm/gtm_list.h" +#include "gtm/gtm_serialize.h" +#include "gtm/gtm_standby.h" #include "gtm/libpq.h" +#include "gtm/libpq-int.h" #include "gtm/pqformat.h" -#include "gtm/gtm_msg.h" -#include <unistd.h> typedef struct GTM_SeqInfoHashBucket { - List *shb_list; + gtm_List *shb_list; GTM_RWLock shb_lock; } GTM_SeqInfoHashBucket; @@ -86,16 +89,16 @@ seq_find_seqinfo(GTM_SequenceKey seqkey) { uint32 hash = seq_gethash(seqkey); GTM_SeqInfoHashBucket *bucket; - ListCell *elem; + gtm_ListCell *elem; GTM_SeqInfo *curr_seqinfo = NULL; bucket = >MSequences[hash]; GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ); - foreach(elem, bucket->shb_list) + gtm_foreach(elem, bucket->shb_list) { - curr_seqinfo = (GTM_SeqInfo *) lfirst(elem); + curr_seqinfo = (GTM_SeqInfo *) gtm_lfirst(elem); if (seq_keys_equal(curr_seqinfo->gs_key, seqkey)) break; curr_seqinfo = NULL; @@ -152,16 +155,16 @@ seq_add_seqinfo(GTM_SeqInfo *seqinfo) { uint32 hash = seq_gethash(seqinfo->gs_key); GTM_SeqInfoHashBucket *bucket; - ListCell *elem; + gtm_ListCell *elem; bucket = >MSequences[hash]; GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_WRITE); - foreach(elem, bucket->shb_list) + gtm_foreach(elem, bucket->shb_list) { GTM_SeqInfo *curr_seqinfo = NULL; - curr_seqinfo = (GTM_SeqInfo *) lfirst(elem); + curr_seqinfo = (GTM_SeqInfo *) gtm_lfirst(elem); if (seq_keys_equal(curr_seqinfo->gs_key, seqinfo->gs_key)) { @@ -176,7 +179,7 @@ seq_add_seqinfo(GTM_SeqInfo *seqinfo) /* * Safe to add the structure to the list */ - bucket->shb_list = lappend(bucket->shb_list, seqinfo); + bucket->shb_list = gtm_lappend(bucket->shb_list, seqinfo); GTM_RWLockRelease(&bucket->shb_lock); return 0; @@ -206,7 +209,7 @@ seq_remove_seqinfo(GTM_SeqInfo *seqinfo) return EBUSY; } - bucket->shb_list = list_delete(bucket->shb_list, seqinfo); + bucket->shb_list = gtm_list_delete(bucket->shb_list, seqinfo); GTM_RWLockRelease(&seqinfo->gs_lock); GTM_RWLockRelease(&bucket->shb_lock); @@ -399,16 +402,16 @@ int GTM_SeqAlter(GTM_SequenceKey seqkey, /* * Restore a sequence. */ -static int +int GTM_SeqRestore(GTM_SequenceKey seqkey, - GTM_Sequence increment_by, - GTM_Sequence minval, - GTM_Sequence maxval, - GTM_Sequence startval, - GTM_Sequence curval, - int32 state, - bool cycle, - bool called) + GTM_Sequence increment_by, + GTM_Sequence minval, + GTM_Sequence maxval, + GTM_Sequence startval, + GTM_Sequence curval, + int32 state, + bool cycle, + bool called) { GTM_SeqInfo *seqinfo = NULL; int errcode = 0; @@ -515,7 +518,7 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp) { int ii = 0; GTM_SeqInfoHashBucket *bucket; - ListCell *cell, *prev; + gtm_ListCell *cell, *prev; GTM_SeqInfo *curr_seqinfo = NULL; int res = 0; bool deleted; @@ -527,10 +530,10 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp) GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ); prev = NULL; - cell = list_head(bucket->shb_list); + cell = gtm_list_head(bucket->shb_list); while (cell != NULL) { - curr_seqinfo = (GTM_SeqInfo *) lfirst(cell); + curr_seqinfo = (GTM_SeqInfo *) gtm_lfirst(cell); deleted = false; if (seq_key_dbname_equal(nsp, curr_seqinfo->gs_key)) @@ -555,7 +558,7 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp) { /* Sequence is not is busy state, it can be deleted safely */ - bucket->shb_list = list_delete_cell(bucket->shb_list, cell, prev); + bucket->shb_list = gtm_list_delete_cell(bucket->shb_list, cell, prev); elog(LOG, "Sequence %s was deleted from GTM", curr_seqinfo->gs_key->gsk_key); @@ -566,14 +569,14 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp) if (deleted) { if (prev) - cell = lnext(prev); + cell = gtm_lnext(prev); else - cell = list_head(bucket->shb_list); + cell = gtm_list_head(bucket->shb_list); } else { prev = cell; - cell = lnext(cell); + cell = gtm_lnext(cell); } } GTM_RWLockRelease(&bucket->shb_lock); @@ -636,7 +639,7 @@ GTM_SeqRename(GTM_SequenceKey seqkey, GTM_SequenceKey newseqkey) /* Release first the structure as it has been taken previously */ seq_release_seqinfo(seqinfo); - /* Close sequence properly, full name is here */ + /* Close sequence properly, full name is here */ seqkey->gsk_type = GTM_SEQ_FULL_NAME; /* Then close properly the old sequence */ GTM_SeqClose(seqkey); @@ -682,7 +685,7 @@ GTM_SeqSetVal(GTM_SequenceKey seqkey, GTM_Sequence nextval, bool iscalled) ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist"))); - return EINVAL; + return InvalidSequenceValue; } GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE); @@ -820,7 +823,7 @@ GTM_InitSeqManager(void) for (ii = 0; ii < SEQ_HASH_TABLE_SIZE; ii++) { - GTMSequences[ii].shb_list = NIL; + GTMSequences[ii].shb_list = gtm_NIL; GTM_RWLockInit(>MSequences[ii].shb_lock); } } @@ -866,13 +869,15 @@ ProcessSequenceInitCommand(Port *myport, StringInfo message) */ oldContext = MemoryContextSwitchTo(TopMostMemoryContext); - if (GTM_SeqOpen(&seqkey, increment, minval, maxval, startval, cycle)) + if ((errcode = GTM_SeqOpen(&seqkey, increment, minval, maxval, startval, cycle))) ereport(ERROR, (errcode, errmsg("Failed to open a new sequence"))); MemoryContextSwitchTo(oldContext); + elog(LOG, "Opening sequence %s", seqkey.gsk_key); + pq_getmsgend(message); /* @@ -892,6 +897,31 @@ ProcessSequenceInitCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling open_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + + retry: + rc = open_sequence(GetMyThreadInfo->thr_conn->standby, + &seqkey, + increment, + minval, + maxval, + startval, + cycle); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "open_sequence() returns rc %d.", rc); + } + + /* FIXME: need to check errors */ } /* @@ -937,7 +967,9 @@ ProcessSequenceAlterCommand(Port *myport, StringInfo message) */ oldContext = MemoryContextSwitchTo(TopMostMemoryContext); - if (GTM_SeqAlter(&seqkey, increment, minval, maxval, startval, lastval, cycle, is_restart)) + elog(LOG, "Altering sequence key %s", seqkey.gsk_key); + + if ((errcode = GTM_SeqAlter(&seqkey, increment, minval, maxval, startval, lastval, cycle, is_restart))) ereport(ERROR, (errcode, errmsg("Failed to open a new sequence"))); @@ -960,6 +992,126 @@ ProcessSequenceAlterCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if ( GetMyThreadInfo->thr_conn->standby ) + { + int rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling alter_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + + retry: + rc = alter_sequence(GetMyThreadInfo->thr_conn->standby, + &seqkey, + increment, + minval, + maxval, + startval, + lastval, + cycle, + is_restart); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "alter_sequence() returns rc %d.", rc); + } + + /* FIXME: need to check errors */ +} + + +/* + * Process MSG_SEQUENCE_LIST message + */ +void +ProcessSequenceListCommand(Port *myport, StringInfo message) +{ + StringInfoData buf; + int seq_count = 0; + MemoryContext oldContext; + GTM_SeqInfo *seq_list[1024]; /* FIXME: make it expandable. */ + int i; + + if (Recovery_IsStandby()) + ereport(ERROR, + (EPERM, + errmsg("Operation not permitted under the standby mode."))); + + memset(seq_list, 0, sizeof(GTM_SeqInfo *) * 1024); + + /* + * We must use the TopMostMemoryContext because the sequence information is + * not bound to a thread and can outlive any of the thread specific + * contextes. + */ + oldContext = MemoryContextSwitchTo(TopMostMemoryContext); + + /* + * Store pointers to all GTM_SeqInfo in the hash buckets into an array. + */ + { + GTM_SeqInfoHashBucket *b; + gtm_ListCell *elem; + + for (i = 0 ; i < SEQ_HASH_TABLE_SIZE ; i++) + { + b = >MSequences[i]; + + GTM_RWLockAcquire(&b->shb_lock, GTM_LOCKMODE_READ); + + gtm_foreach(elem, b->shb_list) + { + seq_list[seq_count] = (GTM_SeqInfo *) gtm_lfirst(elem); + seq_count++; + } + + GTM_RWLockRelease(&b->shb_lock); + } + } + + MemoryContextSwitchTo(oldContext); + + pq_getmsgend(message); + + pq_beginmessage(&buf, 'S'); + pq_sendint(&buf, SEQUENCE_LIST_RESULT, 4); + + if (myport->remote_type == PGXC_NODE_GTM_PROXY) + { + GTM_ProxyMsgHeader proxyhdr; + proxyhdr.ph_conid = myport->conn_id; + pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader)); + } + + /* Send a number of sequences */ + pq_sendint(&buf, seq_count, 4); + + for (i = 0 ; i < seq_count ; i++) + { + char *seq_buf; + size_t seq_buflen; + + seq_buflen = gtm_get_sequence_size(seq_list[i]); + seq_buf = (char *)malloc(seq_buflen); + + gtm_serialize_sequence(seq_list[i], seq_buf, seq_buflen); + + elog(LOG, "seq_buflen = %ld", seq_buflen); + + pq_sendint(&buf, seq_buflen, 4); + pq_sendbytes(&buf, seq_buf, seq_buflen); + + free(seq_buf); + } + + pq_endmessage(myport, &buf); + + elog(LOG, "ProcessSequenceListCommand() done."); + + if (myport->remote_type != PGXC_NODE_GTM_PROXY) + pq_flush(myport); } @@ -982,6 +1134,8 @@ ProcessSequenceGetCurrentCommand(Port *myport, StringInfo message) (ERANGE, errmsg("Can not get current value of the sequence"))); + elog(LOG, "Getting current value %ld for sequence %s", seqval, seqkey.gsk_key); + pq_beginmessage(&buf, 'S'); pq_sendint(&buf, SEQUENCE_GET_CURRENT_RESULT, 4); if (myport->remote_type == PGXC_NODE_GTM_PROXY) @@ -997,6 +1151,25 @@ ProcessSequenceGetCurrentCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + GTM_Sequence loc_seq; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling get_current() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + loc_seq = get_current(GetMyThreadInfo->thr_conn->standby, &seqkey); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "get_current() returns GTM_Sequence %ld.", loc_seq); + } + + /* FIXME: need to check errors */ } /* @@ -1018,6 +1191,8 @@ ProcessSequenceGetNextCommand(Port *myport, StringInfo message) (ERANGE, errmsg("Can not get current value of the sequence"))); + elog(LOG, "Getting next value %ld for sequence %s", seqval, seqkey.gsk_key); + pq_beginmessage(&buf, 'S'); pq_sendint(&buf, SEQUENCE_GET_NEXT_RESULT, 4); if (myport->remote_type == PGXC_NODE_GTM_PROXY) @@ -1033,6 +1208,24 @@ ProcessSequenceGetNextCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + GTM_Sequence loc_seq; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling get_next() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + + retry: + loc_seq = get_next(GetMyThreadInfo->thr_conn->standby, &seqkey); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "get_next() returns GTM_Sequence %ld.", loc_seq); + } + /* FIXME: need to check errors */ } /* @@ -1067,7 +1260,9 @@ ProcessSequenceSetValCommand(Port *myport, StringInfo message) */ oldContext = MemoryContextSwitchTo(TopMostMemoryContext); - if (GTM_SeqSetVal(&seqkey, nextval, iscalled)) + elog(LOG, "Setting new value %ld for sequence %s", nextval, seqkey.gsk_key); + + if ((errcode = GTM_SeqSetVal(&seqkey, nextval, iscalled))) ereport(ERROR, (errcode, errmsg("Failed to set values of sequence"))); @@ -1090,6 +1285,27 @@ ProcessSequenceSetValCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling set_val() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + rc = set_val(GetMyThreadInfo->thr_conn->standby, + &seqkey, + nextval, + iscalled); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "set_val() returns rc %d.", rc); + } + /* FIXME: need to check errors */ } /* @@ -1105,6 +1321,8 @@ ProcessSequenceResetCommand(Port *myport, StringInfo message) seqkey.gsk_keylen = pq_getmsgint(message, sizeof (seqkey.gsk_keylen)); seqkey.gsk_key = (char *)pq_getmsgbytes(message, seqkey.gsk_keylen); + elog(LOG, "Resetting sequence %s", seqkey.gsk_key); + if ((errcode = GTM_SeqReset(&seqkey))) ereport(ERROR, (errcode, @@ -1124,6 +1342,24 @@ ProcessSequenceResetCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling reset_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + rc = reset_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "reset_sequence() returns rc %d.", rc); + } + /* FIXME: need to check errors */ } /* @@ -1141,6 +1377,8 @@ ProcessSequenceCloseCommand(Port *myport, StringInfo message) memcpy(&seqkey.gsk_type, pq_getmsgbytes(message, sizeof (GTM_SequenceKeyType)), sizeof (GTM_SequenceKeyType)); + elog(LOG, "Closing sequence %s", seqkey.gsk_key); + if ((errcode = GTM_SeqClose(&seqkey))) ereport(ERROR, (errcode, @@ -1160,6 +1398,24 @@ ProcessSequenceCloseCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling close_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + rc = close_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "close_sequence() returns rc %d.", rc); + } + /* FIXME: need to check errors */ } /* @@ -1188,6 +1444,8 @@ ProcessSequenceRenameCommand(Port *myport, StringInfo message) */ oldContext = MemoryContextSwitchTo(TopMostMemoryContext); + elog(LOG, "Renaming sequence %s to %s", seqkey.gsk_key, newseqkey.gsk_key); + if ((errcode = GTM_SeqRename(&seqkey, &newseqkey))) ereport(ERROR, (errcode, @@ -1212,13 +1470,32 @@ ProcessSequenceRenameCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling rename_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + rc = rename_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey, &newseqkey); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "rename_sequence() returns rc %d.", rc); + } + + /* FIXME: need to check errors */ } void GTM_SaveSeqInfo(int ctlfd) { GTM_SeqInfoHashBucket *bucket; - ListCell *elem; + gtm_ListCell *elem; GTM_SeqInfo *seqinfo = NULL; int hash; @@ -1228,9 +1505,9 @@ GTM_SaveSeqInfo(int ctlfd) GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ); - foreach(elem, bucket->shb_list) + gtm_foreach(elem, bucket->shb_list) { - seqinfo = (GTM_SeqInfo *) lfirst(elem); + seqinfo = (GTM_SeqInfo *) gtm_lfirst(elem); if (seqinfo == NULL) break; @@ -1312,6 +1589,6 @@ GTM_RestoreSeqInfo(int ctlfd) } GTM_SeqRestore(&seqkey, increment_by, minval, maxval, startval, curval, - state, cycle, called); + state, cycle, called); } } diff --git a/src/gtm/main/gtm_snap.c b/src/gtm/main/gtm_snap.c index 1308765dbc..5e8904fa5f 100644 --- a/src/gtm/main/gtm_snap.c +++ b/src/gtm/main/gtm_snap.c @@ -13,17 +13,15 @@ * *------------------------------------------------------------------------- */ -#include "gtm/gtm_c.h" +#include "gtm/assert.h" #include "gtm/elog.h" -#include "gtm/palloc.h" #include "gtm/gtm.h" -#include "gtm/gtm_txn.h" -#include "gtm/assert.h" +#include "gtm/gtm_client.h" +#include "gtm/gtm_standby.h" #include "gtm/stringinfo.h" #include "gtm/libpq.h" +#include "gtm/libpq-int.h" #include "gtm/pqformat.h" -#include "gtm/gtm_msg.h" - /* * Get snapshot for the given transactions. If this is the first call in the @@ -58,7 +56,7 @@ GTM_GetTransactionSnapshot(GTM_TransactionHandle handle[], int txn_count, int *s GlobalTransactionId xmax; GlobalTransactionId globalxmin; int count = 0; - ListCell *elem = NULL; + gtm_ListCell *elem = NULL; int ii; /* @@ -126,9 +124,9 @@ GTM_GetTransactionSnapshot(GTM_TransactionHandle handle[], int txn_count, int *s * Spin over transaction list checking xid, xmin, and subxids. The goal is to * gather all active xids and find the lowest xmin */ - foreach(elem, GTMTransactions.gt_open_transactions) + gtm_foreach(elem, GTMTransactions.gt_open_transactions) { - volatile GTM_TransactionInfo *gtm_txninfo = (GTM_TransactionInfo *)lfirst(elem); + volatile GTM_TransactionInfo *gtm_txninfo = (GTM_TransactionInfo *)gtm_lfirst(elem); GlobalTransactionId xid; /* Don't take into account LAZY VACUUMs */ @@ -445,6 +443,32 @@ ProcessGetSnapshotCommandMulti(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + int txn_count_out; + int status_out[GTM_MAX_GLOBAL_TRANSACTIONS]; + GlobalTransactionId xmin_out; + GlobalTransactionId xmax_out; + GlobalTransactionId recent_global_xmin_out; + int32 xcnt_out; + + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; +retry: + elog(LOG, "calling snapshot_get_multi() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + + _rc = snapshot_get_multi(GetMyThreadInfo->thr_conn->standby, + txn_count, gxid, &txn_count_out, status_out, + &xmin_out, &xmax_out, &recent_global_xmin_out, &xcnt_out); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "snapshot_get_multi() rc=%d done.", _rc); + } + return; } diff --git a/src/gtm/main/gtm_standby.c b/src/gtm/main/gtm_standby.c new file mode 100644 index 0000000000..5b9fa420ae --- /dev/null +++ b/src/gtm/main/gtm_standby.c @@ -0,0 +1,511 @@ +/*------------------------------------------------------------------------- + * + * gtm_standby.c + * Functionalities of GTM Standby + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * + * IDENTIFICATION + * src/gtm/common/gtm_standby.c + * + *------------------------------------------------------------------------- + */ +#include "gtm/gtm_standby.h" + +#include "gtm/elog.h" +#include "gtm/gtm.h" +#include "gtm/gtm_c.h" +#include "gtm/standby_utils.h" +#include "gtm/gtm_client.h" +#include "gtm/gtm_seq.h" +#include "gtm/gtm_serialize.h" +#include "gtm/gtm_utils.h" +#include "gtm/register.h" + +static GTM_Conn *GTM_ActiveConn = NULL; +static char standbyNodeName[NI_MAXHOST]; +static GTM_PGXCNodeId standbyNodeNum; +static int standbyPortNumber; +static char *standbyDataDir; + +static GTM_Conn *gtm_standby_connect_to_standby_int(int *); + +int +gtm_standby_start_startup(void) +{ + char connect_string[1024]; + int active_port = Recovery_StandbyGetActivePort(); + char *active_address = Recovery_StandbyGetActiveAddress(); + + elog(LOG, "Connecting the GTM active on %s:%d...", active_address, active_port); + + sprintf(connect_string, "host=%s port=%d pgxc_node_id=1 remote_type=%d", + active_address, active_port, PGXC_NODE_GTM); + + GTM_ActiveConn = PQconnectGTM(connect_string); + if (GTM_ActiveConn == NULL) + { + elog(DEBUG3, "Error in connection"); + return 0; + } + elog(LOG, "Connection established to the GTM active."); + + /* Initialize standby lock */ + Recovery_InitStandbyLock(); + + return 1; +} + +int +gtm_standby_finish_startup(void) +{ + elog(LOG, "Closing a startup connection..."); + + GTMPQfinish(GTM_ActiveConn); + + elog(LOG, "A startup connection closed."); + return 1; +} + +int +gtm_standby_restore_next_gxid(void) +{ + GlobalTransactionId next_gxid = InvalidGlobalTransactionId; + + next_gxid = get_next_gxid(GTM_ActiveConn); + GTM_RestoreTxnInfo(-1, next_gxid); + + elog(LOG, "Restoring the next GXID done."); + return 1; +} + +int +gtm_standby_restore_sequence(void) +{ + GTM_SeqInfo *seq_list[1024]; + int num_seq; + int i; + + /* + * Restore sequence data. + */ + num_seq = get_sequence_list(GTM_ActiveConn, seq_list, 1024); + + for (i = 0; i < num_seq; i++) + { + GTM_SeqRestore(seq_list[i]->gs_key, + seq_list[i]->gs_increment_by, + seq_list[i]->gs_min_value, + seq_list[i]->gs_max_value, + seq_list[i]->gs_init_value, + seq_list[i]->gs_value, + seq_list[i]->gs_state, + seq_list[i]->gs_cycle, + seq_list[i]->gs_called); + } + + elog(LOG, "Restoring sequences done."); + return 1; +} + +int +gtm_standby_restore_gxid(void) +{ + int num_txn; + GTM_Transactions txn; + int i; + + /* + * Restore gxid data. + */ + num_txn = get_txn_gxid_list(GTM_ActiveConn, &txn); + + GTM_RWLockAcquire(>MTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE); + + GTMTransactions.gt_txn_count = txn.gt_txn_count; + GTMTransactions.gt_gtm_state = txn.gt_gtm_state; + GTMTransactions.gt_nextXid = txn.gt_nextXid; + GTMTransactions.gt_oldestXid = txn.gt_oldestXid; + GTMTransactions.gt_xidVacLimit = txn.gt_xidVacLimit; + GTMTransactions.gt_xidWarnLimit = txn.gt_xidWarnLimit; + GTMTransactions.gt_xidStopLimit = txn.gt_xidStopLimit; + GTMTransactions.gt_xidWrapLimit = txn.gt_xidWrapLimit; + GTMTransactions.gt_latestCompletedXid = txn.gt_latestCompletedXid; + GTMTransactions.gt_recent_global_xmin = txn.gt_recent_global_xmin; + GTMTransactions.gt_lastslot = txn.gt_lastslot; + + for (i = 0; i < num_txn; i++) + { + GTMTransactions.gt_transactions_array[i].gti_handle = txn.gt_transactions_array[i].gti_handle; + GTMTransactions.gt_transactions_array[i].gti_thread_id = txn.gt_transactions_array[i].gti_thread_id; + GTMTransactions.gt_transactions_array[i].gti_in_use = txn.gt_transactions_array[i].gti_in_use; + GTMTransactions.gt_transactions_array[i].gti_gxid = txn.gt_transactions_array[i].gti_gxid; + GTMTransactions.gt_transactions_array[i].gti_state = txn.gt_transactions_array[i].gti_state; + GTMTransactions.gt_transactions_array[i].gti_coordid = txn.gt_transactions_array[i].gti_coordid; + GTMTransactions.gt_transactions_array[i].gti_xmin = txn.gt_transactions_array[i].gti_xmin; + GTMTransactions.gt_transactions_array[i].gti_isolevel = txn.gt_transactions_array[i].gti_isolevel; + GTMTransactions.gt_transactions_array[i].gti_readonly = txn.gt_transactions_array[i].gti_readonly; + GTMTransactions.gt_transactions_array[i].gti_backend_id = txn.gt_transactions_array[i].gti_backend_id; + + /* data node */ + GTMTransactions.gt_transactions_array[i].gti_datanodecount = txn.gt_transactions_array[i].gti_datanodecount; + if (GTMTransactions.gt_transactions_array[i].gti_datanodecount > 0) + { + GTMTransactions.gt_transactions_array[i].gti_datanodes + = txn.gt_transactions_array[i].gti_datanodes; + } + else + { + GTMTransactions.gt_transactions_array[i].gti_datanodes = NULL; + } + + /* coordinator node */ + GTMTransactions.gt_transactions_array[i].gti_coordcount = txn.gt_transactions_array[i].gti_coordcount; + if (GTMTransactions.gt_transactions_array[i].gti_coordcount > 0) + { + GTMTransactions.gt_transactions_array[i].gti_coordinators = txn.gt_transactions_array[i].gti_coordinators; + } + else + { + GTMTransactions.gt_transactions_array[i].gti_coordinators = NULL; + } + + if (txn.gt_transactions_array[i].gti_gid==NULL ) + GTMTransactions.gt_transactions_array[i].gti_gid = NULL; + else + { + GTMTransactions.gt_transactions_array[i].gti_gid = txn.gt_transactions_array[i].gti_gid; + } + + /* copy GTM_SnapshotData */ + GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xmin = + txn.gt_transactions_array[i].gti_current_snapshot.sn_xmin; + GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xmax = + txn.gt_transactions_array[i].gti_current_snapshot.sn_xmax; + GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_recent_global_xmin = + txn.gt_transactions_array[i].gti_current_snapshot.sn_recent_global_xmin; + GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xcnt = + txn.gt_transactions_array[i].gti_current_snapshot.sn_xcnt; + GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xip = + txn.gt_transactions_array[i].gti_current_snapshot.sn_xip; + /* end of copying GTM_SnapshotData */ + + GTMTransactions.gt_transactions_array[i].gti_snapshot_set = + txn.gt_transactions_array[i].gti_snapshot_set; + GTMTransactions.gt_transactions_array[i].gti_vacuum = + txn.gt_transactions_array[i].gti_vacuum; + + /* + * Is this correct? Is GTM_TXN_COMMITTED transaction categorized as "open"? + */ + if (GTMTransactions.gt_transactions_array[i].gti_state != GTM_TXN_ABORTED) + { + GTMTransactions.gt_open_transactions = + gtm_lappend(GTMTransactions.gt_open_transactions, + >MTransactions.gt_transactions_array[i]); + } + } + + dump_transactions_elog(>MTransactions, num_txn); + + GTM_RWLockRelease(>MTransactions.gt_XidGenLock); + + elog(LOG, "Restoring %d gxid(s) done.", num_txn); + return 1; +} + +int +gtm_standby_restore_node(void) +{ + GTM_PGXCNodeInfo *data; + int rc, i; + int num_node; + + elog(LOG, "Copying node information from the GTM active..."); + + data = (GTM_PGXCNodeInfo *) malloc(sizeof(GTM_PGXCNodeInfo) * 128); + memset(data, 0, sizeof(GTM_PGXCNodeInfo) * 128); + + rc = get_node_list(GTM_ActiveConn, data, 128); + if (rc < 0) + { + elog(DEBUG3, "get_node_list() failed."); + rc = 0; + goto finished; + } + + num_node = rc; + + for (i = 0; i < num_node; i++) + { + elog(LOG, "get_node_list: nodetype=%d, nodenum=%d, datafolder=%s", + data[i].type, data[i].nodenum, data[i].datafolder); + if (Recovery_PGXCNodeRegister(data[i].type, data[i].nodenum, data[i].port, + data[i].proxynum, data[i].status, + data[i].ipaddress, data[i].datafolder, true, + -1 /* dummy socket */) != 0) + { + rc = 0; + goto finished; + } + } + + elog(LOG, "Copying node information from GTM active done."); + +finished: + free(data); + return rc; +} + +/* + * Register myself to the GTM (active) as a "disconnected" node. + * + * This status would be updated later after restoring completion. + * See gtm_standby_update_self(). + * + * Returns 1 on success, 0 on failure. + */ +int +gtm_standby_register_self(GTM_PGXCNodeId nodenum, int port, const char *datadir) +{ + int rc; + + elog(LOG, "Registering standby-GTM status..."); + + node_get_local_addr(GTM_ActiveConn, standbyNodeName, sizeof(standbyNodeName), &rc); + if (rc != 0) + return 0; + + standbyNodeNum = nodenum; + standbyPortNumber = port; + standbyDataDir= (char *)datadir; + + rc = node_register_internal(GTM_ActiveConn, PGXC_NODE_GTM, standbyNodeName, standbyPortNumber, + standbyNodeNum, standbyDataDir, NODE_DISCONNECTED); + if (rc < 0) + { + elog(LOG, "Failed to register a standby-GTM status."); + return 0; + } + + elog(LOG, "Registering standby-GTM done."); + + return 1; +} + +/* + * Update my node status from "disconnected" to "connected" in GTM by myself. + * + * Returns 1 on success, 0 on failure. + */ +int +gtm_standby_activate_self(void) +{ + int rc; + + elog(LOG, "Updating the standby-GTM status to \"CONNECTED\"..."); + + rc = node_unregister(GTM_ActiveConn, PGXC_NODE_GTM, standbyNodeNum); + if (rc < 0) + { + elog(LOG, "Failed to unregister old standby-GTM status."); + return 0; + } + + rc = node_register_internal(GTM_ActiveConn, PGXC_NODE_GTM, standbyNodeName, standbyPortNumber, + standbyNodeNum, standbyDataDir, NODE_CONNECTED); + + if (rc < 0) + { + elog(LOG, "Failed to register a new standby-GTM status."); + return 0; + } + + elog(LOG, "Updating the standby-GTM status done."); + + return 1; +} + + +/* + * Find "one" GTM standby node info. + * + * Returns a pointer to GTM_PGXCNodeInfo on success, + * or returns NULL on failure. + */ +static GTM_PGXCNodeInfo * +find_standby_node_info(void) +{ + GTM_PGXCNodeInfo *node[1024]; + size_t n; + int i; + + n = pgxcnode_find_by_type(PGXC_NODE_GTM, node, 1024); + + for (i = 0 ; i < n ; i++) + { + elog(LOG, "pgxcnode_find_by_type: nodenum=%d, type=%d, ipaddress=%s, port=%d, status=%d", + node[i]->nodenum, + node[i]->type, + node[i]->ipaddress, + node[i]->port, + node[i]->status); + + if (node[i]->nodenum != standbyNodeNum && + node[i]->status == NODE_CONNECTED) + return node[i]; + } + + return NULL; +} + + +/* + * Make a connection to the GTM standby node when getting connected + * from the client. + * + * Returns a pointer to a GTM_Conn object on success, or NULL on failure. + */ +GTM_Conn * +gtm_standby_connect_to_standby(void) +{ + GTM_Conn *conn; + int report; + + conn = gtm_standby_connect_to_standby_int(&report); + +#if 0 + /* + * PGXCTODO: This portion of code needs XCM support + * to be able to report GTM failures to XC watcher and + * enable a GTM reconnection kick. + */ + if (!conn && report) + gtm_report_failure(NULL); +#endif + + return conn; +} + +static GTM_Conn * +gtm_standby_connect_to_standby_int(int *report_needed) +{ + GTM_Conn *standby = NULL; + GTM_PGXCNodeInfo *n; + char conn_string[1024]; + + *report_needed = 0; + + if (Recovery_IsStandby()) + return NULL; + + n = find_standby_node_info(); + + if (!n) + { + elog(LOG, "Any GTM standby node not found in registered node(s)."); + return NULL; + } + + elog(LOG, "GTM standby is active. Going to connect."); + *report_needed = 1; + + snprintf(conn_string, sizeof(conn_string), + "host=%s port=%d pgxc_node_id=1 remote_type=4", + n->ipaddress, n->port); + + standby = PQconnectGTM(conn_string); + + if ( !standby ) + { + elog(LOG, "Failed to establish a connection with GTM standby. - %p", n); + return NULL; + } + + elog(LOG, "Connection established with GTM standby. - %p", n); + + return standby; +} + +void +gtm_standby_disconnect_from_standby(GTM_Conn *conn) +{ + if (Recovery_IsStandby()) + return; + + GTMPQfinish(conn); +} + + +GTM_Conn * +gtm_standby_reconnect_to_standby(GTM_Conn *old_conn, int retry_max) +{ + GTM_Conn *newconn; + int report; + int i; + + if (Recovery_IsStandby()) + return NULL; + + if (old_conn != NULL) + gtm_standby_disconnect_from_standby(old_conn); + + for (i = 0; i < retry_max; i++) + { + elog(LOG, "gtm_standby_reconnect_to_standby(): going to re-connect. retry=%d", i); + + newconn = gtm_standby_connect_to_standby_int(&report); + if (newconn != NULL) + break; + + elog(LOG, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=%d", i); + } + +#if 0 + /* + * PGXCTODO: This portion of code needs XCM support + * to be able to report GTM failures to XC watcher and + * enable a GTM reconnection kick. + */ + if (newconn) + gtm_report_failure(NULL); +#endif + + return newconn; +} + + +#define GTM_STANDBY_RETRY_MAX 3 + +bool +gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn) +{ + if (GetMyThreadInfo->thr_conn->standby->result->gr_status == GTM_RESULT_COMM_ERROR) + { + if (*retry_count == 0) + { + (*retry_count)++; + + GetMyThreadInfo->thr_conn->standby = + gtm_standby_reconnect_to_standby(GetMyThreadInfo->thr_conn->standby, + GTM_STANDBY_RETRY_MAX); + + if (GetMyThreadInfo->thr_conn->standby) + return true; + } + + elog(LOG, "communication error with standby."); +#if 0 + /* + * PGXCTODO: This portion of code needs XCM support + * to be able to report GTM failures to XC watcher and + * enable a GTM reconnection kick. + */ + gtm_report_failure(oldconn); +#endif + } + return false; +} diff --git a/src/gtm/main/gtm_thread.c b/src/gtm/main/gtm_thread.c index fd49e852dd..c041645521 100644 --- a/src/gtm/main/gtm_thread.c +++ b/src/gtm/main/gtm_thread.c @@ -265,6 +265,17 @@ GTM_ThreadCleanup(void *argp) elog(LOG, "Cleaning up thread state"); /* + * Close a connection to GTM standby. + */ + if (thrinfo->thr_conn->standby) + { + elog(LOG, "Closing a connection to the GTM standby."); + + GTMPQfinish(thrinfo->thr_conn->standby); + thrinfo->thr_conn->standby = NULL; + } + + /* * TODO Close the open connection. */ StreamClose(thrinfo->thr_conn->con_port->sock); diff --git a/src/gtm/main/gtm_txn.c b/src/gtm/main/gtm_txn.c index c16570a3c3..7d13076486 100644 --- a/src/gtm/main/gtm_txn.c +++ b/src/gtm/main/gtm_txn.c @@ -13,19 +13,19 @@ * *------------------------------------------------------------------------- */ -#include "gtm/gtm_c.h" +#include "gtm/gtm_txn.h" + +#include <unistd.h> +#include "gtm/assert.h" #include "gtm/elog.h" -#include "gtm/palloc.h" #include "gtm/gtm.h" -#include "gtm/gtm_txn.h" -#include "gtm/gtm_c.h" #include "gtm/gtm_time.h" -#include "gtm/assert.h" -#include "gtm/stringinfo.h" +#include "gtm/gtm_txn.h" +#include "gtm/gtm_serialize.h" +#include "gtm/gtm_standby.h" #include "gtm/libpq.h" +#include "gtm/libpq-int.h" #include "gtm/pqformat.h" -#include "gtm/gtm_msg.h" -#include <unistd.h> /* Local functions */ static XidStatus GlobalTransactionIdGetStatus(GlobalTransactionId transactionId); @@ -54,7 +54,7 @@ GTM_InitTxnManager(void) * permanent storage and read it back when it's restarted. It will get * trickier for GTM failures. * - * TODO We skip thia part for the prototype. + * TODO We skip this part for the prototype. */ GTMTransactions.gt_nextXid = FirstNormalGlobalTransactionId; @@ -65,7 +65,7 @@ GTM_InitTxnManager(void) /* * XXX Compute various xid limits to avoid wrap-around related database - * corruptions. Again, this is not implemeneted for the prototype + * corruptions. Again, this is not implemented for the prototype */ GTMTransactions.gt_xidVacLimit = InvalidGlobalTransactionId; GTMTransactions.gt_xidWarnLimit = InvalidGlobalTransactionId; @@ -87,7 +87,7 @@ GTM_InitTxnManager(void) /* * Initialize the list */ - GTMTransactions.gt_open_transactions = NIL; + GTMTransactions.gt_open_transactions = gtm_NIL; GTMTransactions.gt_lastslot = -1; GTMTransactions.gt_gtm_state = GTM_STARTING; @@ -101,7 +101,7 @@ GTM_InitTxnManager(void) static XidStatus GlobalTransactionIdGetStatus(GlobalTransactionId transactionId) { - XidStatus xidstatus; + XidStatus xidstatus = TRANSACTION_STATUS_IN_PROGRESS; /* * Also, check to see if the transaction ID is a permanent one. @@ -116,8 +116,10 @@ GlobalTransactionIdGetStatus(GlobalTransactionId transactionId) } /* - * TODO To be implemeneted + * TODO To be implemented + * This code is not completed yet and the latter code must not be reached. */ + Assert(0); return xidstatus; } @@ -127,14 +129,14 @@ GlobalTransactionIdGetStatus(GlobalTransactionId transactionId) GTM_TransactionHandle GTM_GXIDToHandle(GlobalTransactionId gxid) { - ListCell *elem = NULL; + gtm_ListCell *elem = NULL; GTM_TransactionInfo *gtm_txninfo = NULL; GTM_RWLockAcquire(>MTransactions.gt_TransArrayLock, GTM_LOCKMODE_READ); - foreach(elem, GTMTransactions.gt_open_transactions) + gtm_foreach(elem, GTMTransactions.gt_open_transactions) { - gtm_txninfo = (GTM_TransactionInfo *)lfirst(elem); + gtm_txninfo = (GTM_TransactionInfo *)gtm_lfirst(elem); if (GlobalTransactionIdEquals(gtm_txninfo->gti_gxid, gxid)) break; gtm_txninfo = NULL; @@ -155,14 +157,14 @@ GTM_GXIDToHandle(GlobalTransactionId gxid) GTM_TransactionHandle GTM_GIDToHandle(char *gid) { - ListCell *elem = NULL; + gtm_ListCell *elem = NULL; GTM_TransactionInfo *gtm_txninfo = NULL; GTM_RWLockAcquire(>MTransactions.gt_TransArrayLock, GTM_LOCKMODE_READ); - foreach(elem, GTMTransactions.gt_open_transactions) + gtm_foreach(elem, GTMTransactions.gt_open_transactions) { - gtm_txninfo = (GTM_TransactionInfo *)lfirst(elem); + gtm_txninfo = (GTM_TransactionInfo *)gtm_lfirst(elem); if (gtm_txninfo->gti_gid && strcmp(gid,gtm_txninfo->gti_gid) == 0) break; gtm_txninfo = NULL; @@ -236,16 +238,16 @@ GTM_RemoveTransInfoMulti(GTM_TransactionInfo *gtm_txninfo[], int txn_count) if (gtm_txninfo[ii] == NULL) continue; - GTMTransactions.gt_open_transactions = list_delete(GTMTransactions.gt_open_transactions, gtm_txninfo[ii]); + GTMTransactions.gt_open_transactions = gtm_list_delete(GTMTransactions.gt_open_transactions, gtm_txninfo[ii]); if (GlobalTransactionIdIsNormal(gtm_txninfo[ii]->gti_gxid) && GlobalTransactionIdFollowsOrEquals(gtm_txninfo[ii]->gti_gxid, GTMTransactions.gt_latestCompletedXid)) GTMTransactions.gt_latestCompletedXid = gtm_txninfo[ii]->gti_gxid; - elog(DEBUG1, "GTM_RemoveTransInfoMulti: removing transaction id %u, %lu", gtm_txninfo[ii]->gti_gxid, gtm_txninfo[ii]->gti_thread_id); + /* * Now mark the transaction as aborted and mark the structure as not-in-use */ @@ -286,7 +288,7 @@ GTM_RemoveTransInfoMulti(GTM_TransactionInfo *gtm_txninfo[], int txn_count) void GTM_RemoveAllTransInfos(int backend_id) { - ListCell *cell, *prev; + gtm_ListCell *cell, *prev; GTM_ThreadID thread_id; thread_id = pthread_self(); @@ -296,10 +298,10 @@ GTM_RemoveAllTransInfos(int backend_id) */ GTM_RWLockAcquire(>MTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE); prev = NULL; - cell = list_head(GTMTransactions.gt_open_transactions); + cell = gtm_list_head(GTMTransactions.gt_open_transactions); while (cell != NULL) { - GTM_TransactionInfo *gtm_txninfo = lfirst(cell); + GTM_TransactionInfo *gtm_txninfo = gtm_lfirst(cell); /* * Check if current entry is associated with the thread * A transaction in prepared state has to be kept alive in the structure. @@ -312,7 +314,7 @@ GTM_RemoveAllTransInfos(int backend_id) ((gtm_txninfo->gti_backend_id == backend_id) || (backend_id == -1))) { /* remove the entry */ - GTMTransactions.gt_open_transactions = list_delete_cell(GTMTransactions.gt_open_transactions, cell, prev); + GTMTransactions.gt_open_transactions = gtm_list_delete_cell(GTMTransactions.gt_open_transactions, cell, prev); /* update the latestCompletedXid */ if (GlobalTransactionIdIsNormal(gtm_txninfo->gti_gxid) && @@ -349,14 +351,14 @@ GTM_RemoveAllTransInfos(int backend_id) /* move to next cell in the list */ if (prev) - cell = lnext(prev); + cell = gtm_lnext(prev); else - cell = list_head(GTMTransactions.gt_open_transactions); + cell = gtm_list_head(GTMTransactions.gt_open_transactions); } else { prev = cell; - cell = lnext(cell); + cell = gtm_lnext(cell); } } @@ -717,7 +719,7 @@ GTM_BeginTransactionMulti(GTM_PGXCNodeId coord_id, * because the list is global and any memory allocation must outlive the * thread context */ - GTMTransactions.gt_open_transactions = lappend(GTMTransactions.gt_open_transactions, gtm_txninfo[kk]); + GTMTransactions.gt_open_transactions = gtm_lappend(GTMTransactions.gt_open_transactions, gtm_txninfo[kk]); } GTM_RWLockRelease(>MTransactions.gt_TransArrayLock); @@ -894,6 +896,12 @@ GTM_StartPreparedTransaction(GTM_TransactionHandle txn, return STATUS_ERROR; /* + * Check if given GID is already in use by another transaction. + */ + if (GTM_GIDToHandle(gid) != InvalidTransactionHandle) + return STATUS_ERROR; + + /* * Mark the transaction as being prepared */ GTM_RWLockAcquire(>m_txninfo->gti_lock, GTM_LOCKMODE_WRITE); @@ -1107,6 +1115,25 @@ ProcessBeginTransactionGetGXIDCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + GTM_Timestamp _ts; + GlobalTransactionId _gxid; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling begin_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + _gxid = begin_transaction(GetMyThreadInfo->thr_conn->standby, txn_isolation_level, &_ts); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "begin_transaction() returns GXID %d.", _gxid); + } + return; } @@ -1167,6 +1194,26 @@ ProcessBeginTransactionGetGXIDAutovacuumCommand(Port *myport, StringInfo message if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + GlobalTransactionId _gxid; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling begin_transaction_autovacuum() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + + retry: + _gxid = begin_transaction_autovacuum(GetMyThreadInfo->thr_conn->standby, + txn_isolation_level); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "begin_transaction_autovacuum() GXID=%d done.", _gxid); + } + return; } @@ -1246,6 +1293,35 @@ ProcessBeginTransactionGetGXIDCommandMulti(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + int txn_count_out; + GlobalTransactionId gxid_out; + GTM_Timestamp ts_out; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling begin_transaction_multi() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = begin_transaction_multi(GetMyThreadInfo->thr_conn->standby, + txn_count, + txn_isolation_level, + txn_read_only, + txn_connid, + &txn_count_out, + &gxid_out, + &ts_out); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "begin_transaction_multi() rc=%d done.", _rc); + } + return; } @@ -1287,6 +1363,8 @@ ProcessCommitTransactionCommand(Port *myport, StringInfo message) pq_getmsgend(message); oldContext = MemoryContextSwitchTo(TopMemoryContext); + + elog(LOG, "Committing transaction id %u", gxid); /* * Commit the transaction @@ -1309,6 +1387,24 @@ ProcessCommitTransactionCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling commit_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = commit_transaction(GetMyThreadInfo->thr_conn->standby, gxid); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "commit_transaction() rc=%d done.", _rc); + } + return; } @@ -1380,6 +1476,26 @@ ProcessCommitPreparedTransactionCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling commit_prepared_transaction() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = commit_prepared_transaction(GetMyThreadInfo->thr_conn->standby, + gxid[0], gxid[1] /* prepared GXID */); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "commit_prepared_transaction() rc=%d done.", _rc); + } + return; } @@ -1397,11 +1513,10 @@ void ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message) { StringInfoData buf; - char *gid; + char gid[1024]; int gidlen; GTM_IsolationLevel txn_isolation_level; bool txn_read_only; - MemoryContext oldContext; GTM_TransactionHandle txn, prepared_txn; /* Data to be sent back to client */ GlobalTransactionId gxid, prepared_gxid; @@ -1415,7 +1530,8 @@ ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message) /* receive GID */ gidlen = pq_getmsgint(message, sizeof (GTM_StrLen)); - gid = (char *)pq_getmsgbytes(message, gidlen); + memcpy(gid, (char *)pq_getmsgbytes(message, gidlen), gidlen); + gid[gidlen] = '\0'; pq_getmsgend(message); @@ -1476,9 +1592,101 @@ ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling get_gid_data() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = get_gid_data(GetMyThreadInfo->thr_conn->standby, + txn_isolation_level, + gid, + &gxid, + &prepared_gxid, + &datanodecnt, + &datanodes, + &coordcnt, + &coordinators); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "get_gid_data() rc=%d done.", _rc); + } + + return; +} + +/* + * Process MSG_TXN_GXID_LIST + */ +void +ProcessGXIDListCommand(Port *myport, StringInfo message) +{ + MemoryContext oldContext; + StringInfoData buf; + char *data; + size_t estlen, actlen; /* estimated length and actual length */ + + pq_getmsgend(message); + + if (Recovery_IsStandby()) + ereport(ERROR, + (EPERM, + errmsg("Operation not permitted under the standby mode."))); + + /* + * Do something here. + */ + oldContext = MemoryContextSwitchTo(TopMemoryContext); + + GTM_RWLockAcquire(>MTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE); + + estlen = gtm_get_transactions_size(>MTransactions); + data = malloc(estlen+1); + + actlen = gtm_serialize_transactions(>MTransactions, data, estlen); + + elog(LOG, "gtm_serialize_transactions: estlen=%ld, actlen=%ld", estlen, actlen); + + GTM_RWLockRelease(>MTransactions.gt_XidGenLock); + + MemoryContextSwitchTo(oldContext); + + /* + * Send a SUCCESS message back to the client + */ + pq_beginmessage(&buf, 'S'); + pq_sendint(&buf, TXN_GXID_LIST_RESULT, 4); + if (myport->remote_type == PGXC_NODE_GTM_PROXY) + { + GTM_ProxyMsgHeader proxyhdr; + proxyhdr.ph_conid = myport->conn_id; + pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader)); + } + + pq_sendint(&buf, actlen, sizeof(int32)); /* size of serialized GTM_Transactions */ + pq_sendbytes(&buf, data, actlen); /* serialized GTM_Transactions */ + pq_endmessage(myport, &buf); + + if (myport->remote_type != PGXC_NODE_GTM_PROXY) + { + pq_flush(myport); + elog(LOG, "pq_flush()"); + } + + elog(LOG, "ProcessGXIDListCommand() ok. %ld bytes sent. len=%d", actlen, buf.len); + free(data); + return; } + /* * Process MSG_TXN_ROLLBACK message */ @@ -1518,6 +1726,8 @@ ProcessRollbackTransactionCommand(Port *myport, StringInfo message) oldContext = MemoryContextSwitchTo(TopMemoryContext); + elog(LOG, "Cancelling transaction id %u", gxid); + /* * Commit the transaction */ @@ -1539,6 +1749,23 @@ ProcessRollbackTransactionCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling abort_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + +retry: + abort_transaction(GetMyThreadInfo->thr_conn->standby, gxid); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "abort_transaction() GXID=%d done.", gxid); + } + return; } @@ -1611,6 +1838,28 @@ ProcessCommitTransactionCommandMulti(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + int txn_count_out; + int status_out[GTM_MAX_GLOBAL_TRANSACTIONS]; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling commit_transaction_multi() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = commit_transaction_multi(GetMyThreadInfo->thr_conn->standby, + txn_count, gxid, &txn_count_out, status_out); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "commit_transaction_multi() rc=%d done.", _rc); + } + return; } @@ -1682,6 +1931,28 @@ ProcessRollbackTransactionCommandMulti(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + int txn_count_out; + int status_out[GTM_MAX_GLOBAL_TRANSACTIONS]; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling abort_transaction_multi() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = abort_transaction_multi(GetMyThreadInfo->thr_conn->standby, + txn_count, gxid, &txn_count_out, status_out); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "abort_transaction_multi() rc=%d done.", _rc); + } + return; } @@ -1700,7 +1971,7 @@ ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message) PGXC_NodeId *coordinators = NULL; PGXC_NodeId *datanodes = NULL; MemoryContext oldContext; - char *gid; + char gid[1024]; isgxid = pq_getmsgbyte(message); @@ -1726,7 +1997,8 @@ ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message) /* get GID */ gidlen = pq_getmsgint(message, sizeof (GTM_StrLen)); - gid = (char *)pq_getmsgbytes(message, gidlen); + memcpy(gid, (char *)pq_getmsgbytes(message, gidlen), gidlen); + gid[gidlen] = '\0'; /* Get Datanode Count Data */ datanodecnt = pq_getmsgint(message, 4); @@ -1780,6 +2052,28 @@ ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling start_prepared_transaction() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + + retry: + _rc = start_prepared_transaction(GetMyThreadInfo->thr_conn->standby, + gxid, gid, + datanodecnt, datanodes, + coordcnt, coordinators); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "start_prepared_transaction() rc=%d done.", _rc); + } + return; } @@ -1829,6 +2123,8 @@ ProcessPrepareTransactionCommand(Port *myport, StringInfo message) MemoryContextSwitchTo(oldContext); + elog(LOG, "Preparing transaction id %u", gxid); + pq_beginmessage(&buf, 'S'); pq_sendint(&buf, TXN_PREPARE_RESULT, 4); if (myport->remote_type == PGXC_NODE_GTM_PROXY) @@ -1842,6 +2138,23 @@ ProcessPrepareTransactionCommand(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling prepare_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby); + + retry: + prepare_transaction(GetMyThreadInfo->thr_conn->standby, gxid); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "prepare_transaction() GXID=%d done.", gxid); + } + return; } @@ -1903,6 +2216,50 @@ ProcessGetGXIDTransactionCommand(Port *myport, StringInfo message) /* + * Process MSG_TXN_GET_NEXT_GXID message + */ +void +ProcessGetNextGXIDTransactionCommand(Port *myport, StringInfo message) +{ + StringInfoData buf; + GlobalTransactionId next_gxid; + MemoryContext oldContext; + + elog(DEBUG3, "Inside ProcessGetNextGXIDTransactionCommand"); + + pq_getmsgend(message); + + oldContext = MemoryContextSwitchTo(TopMemoryContext); + + /* + * Get the next gxid. + */ + GTM_RWLockAcquire(>MTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE); + next_gxid = GTMTransactions.gt_nextXid; + GTM_RWLockRelease(>MTransactions.gt_XidGenLock); + + MemoryContextSwitchTo(oldContext); + + elog(DEBUG3, "Sending next gxid %d", next_gxid); + + pq_beginmessage(&buf, 'S'); + pq_sendint(&buf, TXN_GET_NEXT_GXID_RESULT, 4); + if (myport->remote_type == PGXC_NODE_GTM_PROXY) + { + GTM_ProxyMsgHeader proxyhdr; + proxyhdr.ph_conid = myport->conn_id; + pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader)); + } + pq_sendint(&buf, next_gxid, sizeof(GlobalTransactionId)); + pq_endmessage(myport, &buf); + + if (myport->remote_type != PGXC_NODE_GTM_PROXY) + pq_flush(myport); + return; +} + + +/* * Mark GTM as shutting down. This point onwards no new GXID are issued to * ensure that the last GXID recorded in the control file remains sane */ diff --git a/src/gtm/main/main.c b/src/gtm/main/main.c index 9bfa9da475..b71bec7e5b 100644 --- a/src/gtm/main/main.c +++ b/src/gtm/main/main.c @@ -25,17 +25,22 @@ #include <stdio.h> #include "gtm/gtm_c.h" -#include "gtm/gtm.h" #include "gtm/path.h" +#include "gtm/gtm.h" #include "gtm/elog.h" #include "gtm/memutils.h" #include "gtm/gtm_list.h" +#include "gtm/gtm_seq.h" +#include "gtm/standby_utils.h" +#include "gtm/gtm_standby.h" #include "gtm/libpq.h" +#include "gtm/libpq-fe.h" #include "gtm/libpq-be.h" #include "gtm/pqsignal.h" #include "gtm/pqformat.h" #include "gtm/assert.h" #include "gtm/register.h" +#include "gtm/replication.h" #include "gtm/gtm_txn.h" #include "gtm/gtm_seq.h" #include "gtm/gtm_msg.h" @@ -69,7 +74,7 @@ static Port *ConnCreate(int serverFd); static int ServerLoop(void); static int initMasks(fd_set *rmask); void *GTM_ThreadMain(void *argp); -static int GTMAddConnection(Port *port); +static int GTMAddConnection(Port *port, GTM_Conn *standby); static int ReadCommand(Port *myport, StringInfo inBuf); static void ProcessCommand(Port *myport, StringInfo input_message); @@ -80,7 +85,6 @@ static void ProcessSequenceCommand(Port *myport, GTM_MessageType mtype, StringIn static void ProcessQueryCommand(Port *myport, GTM_MessageType mtype, StringInfo message); static void GTM_RegisterPGXCNode(Port *myport, GTM_PGXCNodeId pgxc_node_id); -static void GTM_UnregisterPGXCNode(Port *myport, GTM_PGXCNodeId pgxc_node_id); static bool CreateOptsFile(int argc, char *argv[]); static void CreateDataDirLockFile(void); @@ -89,6 +93,7 @@ static void SetDataDir(void); static void ChangeToDataDir(void); static void checkDataDir(void); static void DeleteLockFile(const char *filename); +static void PromoteToActive(void); /* * One-time initialization. It's called immediately after the main process @@ -190,6 +195,10 @@ GTM_SigleHandler(int signal) case SIGHUP: break; + case SIGUSR1: + PromoteToActive(); + return; + default: fprintf(stderr, "Unknown signal %d\n", signal); return; @@ -220,12 +229,27 @@ help(const char *progname) printf(_("This is the GTM server.\n\n")); printf(_("Usage:\n %s [OPTION]...\n\n"), progname); printf(_("Options:\n")); - printf(_(" -h hostname GTM server hostname/IP\n")); - printf(_(" -p port GTM server port number\n")); - printf(_(" -x xid Starting GXID \n")); - printf(_(" -D directory GTM working directory\n")); - printf(_(" -l filename GTM server log file name \n")); + printf(_(" -h hostname GTM server hostname/IP to listen.\n")); + printf(_(" -p port GTM server port number to listen.\n")); + printf(_(" -n nodenum Node number for GTM server.\n")); + printf(_(" -x xid Starting GXID \n")); + printf(_(" -D directory GTM working directory\n")); + printf(_(" -l filename GTM server log file name \n")); + printf(_(" -c show server status, then exit\n")); printf(_(" --help show this help, then exit\n")); + printf(_("\n")); + printf(_("Options for Standby mode:\n")); + printf(_(" -s Start as a GTM standby server.\n")); + printf(_(" -i hostname Active GTM server hostname/IP to connect.\n")); + printf(_(" -q port Active GTM server port number to connect.\n")); + printf(_("\n")); +} + +static void +gtm_status() +{ + fprintf(stderr, "gtm_status(): must be implemented to scan the shmem.\n"); + exit(0); } int @@ -236,6 +260,9 @@ main(int argc, char *argv[]) int i; GlobalTransactionId next_gxid = InvalidGlobalTransactionId; int ctlfd; + char *active_addr; + int active_port; + GTM_PGXCNodeId node_num = 1001; /* * Catch standard options before doing much else @@ -255,20 +282,28 @@ main(int argc, char *argv[]) /* * Parse the command like options and set variables */ - while ((opt = getopt(argc, argv, "h:p:x:D:l:")) != -1) + while ((opt = getopt(argc, argv, "ch:n:p:x:D:l:si:q:")) != -1) { switch (opt) { + case 'c': + gtm_status(); + break; /* never reach here. */ + case 'h': ListenAddresses = strdup(optarg); break; + case 'n': + node_num = atoi(optarg); + break; + case 'p': GTMPortNumber = atoi(optarg); break; case 'x': - next_gxid = (GlobalTransactionId )atoll(optarg); + next_gxid = (GlobalTransactionId)atoll(optarg); break; case 'D': @@ -280,12 +315,37 @@ main(int argc, char *argv[]) GTMLogFile = strdup(optarg); break; + case 's': + Recovery_StandbySetStandby(true); + break; + + case 'i': + active_addr = strdup(optarg); + break; + + case 'q': + active_port = atoi(optarg); + break; + default: write_stderr("Try \"%s --help\" for more information.\n", progname); } } + /* + * Check options for the standby mode. + */ + if (Recovery_IsStandby()) + { + if (active_addr == NULL || active_port < 1) + { + help(argv[0]); + exit(1); + } + Recovery_StandbySetConnInfo(active_addr, active_port); + } + if (GTMDataDir == NULL) { write_stderr("GTM data directory must be specified\n"); @@ -311,21 +371,80 @@ main(int argc, char *argv[]) */ BaseInit(); + /* + * Establish a connection between the active and standby. + */ + if (Recovery_IsStandby()) + { + if (!gtm_standby_start_startup()) + { + elog(ERROR, "Failed to establish a connection to active-GTM."); + exit(1); + } + elog(LOG, "Startup connection established with active-GTM."); + } + elog(DEBUG3, "Starting GTM server at (%s:%d) -- control file %s", ListenAddresses, GTMPortNumber, GTMControlFile); /* * Read the last GXID and start from there */ + if (Recovery_IsStandby()) + { + if (!gtm_standby_restore_next_gxid()) + { + elog(ERROR, "Failed to restore next/last gxid from the active-GTM."); + exit(1); + } + elog(LOG, "Restoring next/last gxid from the active-GTM succeeded."); - ctlfd = open(GTMControlFile, O_RDONLY); + if (!gtm_standby_restore_gxid()) + { + elog(ERROR, "Failed to restore all of gxid(s) from the active-GTM."); + exit(1); + } + elog(LOG, "Restoring all of gxid(s) from the active-GTM succeeded."); - GTM_RestoreTxnInfo(ctlfd, next_gxid); - GTM_RestoreSeqInfo(ctlfd); + if (!gtm_standby_restore_sequence()) + { + elog(ERROR, "Failed to restore sequences from the active-GTM."); + exit(1); + } + elog(LOG, "Restoring sequences from the active-GTM succeeded."); + } + else + { + ctlfd = open(GTMControlFile, O_RDONLY); + GTM_RestoreTxnInfo(ctlfd, next_gxid); + GTM_RestoreSeqInfo(ctlfd); + close(ctlfd); + } - close(ctlfd); + if (Recovery_IsStandby()) + { + if (!gtm_standby_register_self(node_num, GTMPortNumber, GTMDataDir)) + { + elog(ERROR, "Failed to register myself on the active-GTM as a GTM node."); + exit(1); + } + elog(LOG, "Registering myself to the active-GTM as a GTM node succeeded."); + } /* Recover Data of Registered nodes. */ - Recovery_RestoreRegisterInfo(); + if (Recovery_IsStandby()) + { + if (!gtm_standby_restore_node()) + { + elog(ERROR, "Failed to restore node information from the active-GTM."); + exit(1); + } + elog(LOG, "Restoring node information from the active-GTM succeeded."); + } + else + { + /* Recover Data of Registered nodes. */ + Recovery_RestoreRegisterInfo(); + } /* * Establish input sockets. @@ -367,8 +486,28 @@ main(int argc, char *argv[]) pqsignal(SIGQUIT, GTM_SigleHandler); pqsignal(SIGTERM, GTM_SigleHandler); pqsignal(SIGINT, GTM_SigleHandler); + pqsignal(SIGUSR1, GTM_SigleHandler); pqinitmask(); + + /* + * Now, activating a standby GTM... + */ + if (Recovery_IsStandby()) + { + if (!gtm_standby_activate_self()) + { + elog(ERROR, "Failed to update the standby-GTM status as \"CONNECTED\"."); + exit(1); + } + elog(LOG, "Updating the standby-GTM status as \"CONNECTED\" succeeded."); + if (!gtm_standby_finish_startup()) + { + elog(ERROR, "Failed to close the initial connection to the active-GTM."); + exit(1); + } + elog(LOG, "Startup connection with the active-GTM closed."); + } /* * Accept any new connections. Fork a new thread for each incoming @@ -528,12 +667,20 @@ ServerLoop(void) port = ConnCreate(ListenSocket[i]); if (port) { - if (GTMAddConnection(port) != STATUS_OK) + GTM_Conn *standby = NULL; + + standby = gtm_standby_connect_to_standby(); + + if (GTMAddConnection(port, standby) != STATUS_OK) { elog(ERROR, "Too many connections"); + + gtm_standby_disconnect_from_standby(standby); + StreamClose(port->sock); ConnFree(port); } + } } } @@ -541,6 +688,7 @@ ServerLoop(void) } } + /* * Initialise the masks for select() for the ports we are listening on. * Return the number of sockets to listen on. @@ -791,9 +939,12 @@ ProcessCommand(Port *myport, StringInfo input_message) { case MSG_NODE_REGISTER: case MSG_NODE_UNREGISTER: + case MSG_NODE_LIST: ProcessPGXCNodeCommand(myport, mtype, input_message); break; + case MSG_NODE_BEGIN_REPLICATION_INIT: + case MSG_NODE_END_REPLICATION_INIT: case MSG_TXN_BEGIN: case MSG_TXN_BEGIN_GETGXID: case MSG_TXN_BEGIN_GETGXID_AUTOVACUUM: @@ -807,6 +958,8 @@ ProcessCommand(Port *myport, StringInfo input_message) case MSG_TXN_COMMIT_MULTI: case MSG_TXN_ROLLBACK_MULTI: case MSG_TXN_GET_GID_DATA: + case MSG_TXN_GET_NEXT_GXID: + case MSG_TXN_GXID_LIST: ProcessTransactionCommand(myport, mtype, input_message); break; @@ -825,6 +978,7 @@ ProcessCommand(Port *myport, StringInfo input_message) case MSG_SEQUENCE_CLOSE: case MSG_SEQUENCE_RENAME: case MSG_SEQUENCE_ALTER: + case MSG_SEQUENCE_LIST: ProcessSequenceCommand(myport, mtype, input_message); break; @@ -849,11 +1003,12 @@ ProcessCommand(Port *myport, StringInfo input_message) } static int -GTMAddConnection(Port *port) +GTMAddConnection(Port *port, GTM_Conn *standby) { GTM_ConnectionInfo *conninfo = NULL; conninfo = (GTM_ConnectionInfo *)palloc(sizeof (GTM_ConnectionInfo)); + memset(conninfo, 0, sizeof(GTM_ConnectionInfo)); if (conninfo == NULL) { @@ -867,6 +1022,12 @@ GTMAddConnection(Port *port) conninfo->con_port = port; /* + * Add a connection to the standby. + */ + if (standby != NULL) + conninfo->standby = standby; + + /* * XXX Start the thread */ if (GTM_ThreadCreate(conninfo, GTM_ThreadMain) == NULL) @@ -960,6 +1121,10 @@ ProcessPGXCNodeCommand(Port *myport, GTM_MessageType mtype, StringInfo message) ProcessPGXCNodeUnregister(myport, message); break; + case MSG_NODE_LIST: + ProcessPGXCNodeList(myport, message); + break; + default: Assert(0); /* Shouldn't come here.. keep compiler quite */ } @@ -972,6 +1137,14 @@ ProcessTransactionCommand(Port *myport, GTM_MessageType mtype, StringInfo messag switch (mtype) { + case MSG_NODE_BEGIN_REPLICATION_INIT: + ProcessBeginReplicationInitialSyncRequest(myport, message); + break; + + case MSG_NODE_END_REPLICATION_INIT: + ProcessEndReplicationInitialSyncRequest(myport, message); + break; + case MSG_TXN_BEGIN: ProcessBeginTransactionCommand(myport, message); break; @@ -1022,6 +1195,15 @@ ProcessTransactionCommand(Port *myport, GTM_MessageType mtype, StringInfo messag case MSG_TXN_GET_GID_DATA: ProcessGetGIDDataTransactionCommand(myport, message); + break; + + case MSG_TXN_GET_NEXT_GXID: + ProcessGetNextGXIDTransactionCommand(myport, message); + break; + + case MSG_TXN_GXID_LIST: + ProcessGXIDListCommand(myport, message); + break; default: Assert(0); /* Shouldn't come here.. keep compiler quite */ @@ -1088,6 +1270,10 @@ ProcessSequenceCommand(Port *myport, GTM_MessageType mtype, StringInfo message) ProcessSequenceRenameCommand(myport, message); break; + case MSG_SEQUENCE_LIST: + ProcessSequenceListCommand(myport, message); + break; + default: Assert(0); /* Shouldn't come here.. keep compiler quite */ } @@ -1116,16 +1302,6 @@ GTM_RegisterPGXCNode(Port *myport, GTM_PGXCNodeId cid) myport->pgxc_node_id = cid; } - -static void -GTM_UnregisterPGXCNode(Port *myport, GTM_PGXCNodeId cid) -{ - /* - * Do a clean shutdown - */ - return; -} - /* * Validate the proposed data directory */ @@ -1196,10 +1372,10 @@ SetDataDir() new = make_absolute_path(GTMDataDir); if (!new) ereport(FATAL, - (errno, - errmsg("failed to set the data directory \"%s\"", - GTMDataDir))); - + (errno, + errmsg("failed to set the data directory \"%s\"", + GTMDataDir))); + if (GTMDataDir) free(GTMDataDir); @@ -1382,8 +1558,8 @@ CreateLockFile(const char *filename, const char *refName) /* * Successfully created the file, now fill it. */ - snprintf(buffer, sizeof(buffer), "%d\n%s\n", - (int) my_pid, GTMDataDir); + snprintf(buffer, sizeof(buffer), "%d\n%s\n%d\n", + (int) my_pid, GTMDataDir, (Recovery_IsStandby() ? 0 : 1)); errno = 0; if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) { @@ -1452,3 +1628,17 @@ DeleteLockFile(const char *filename) "it could not be removed. Please remove the file " "by hand and try again."))); } + +static void +PromoteToActive(void) +{ + elog(LOG, "Promote signal received. Becoming an active..."); + + /* + * Do promoting things here. + */ + Recovery_StandbySetStandby(false); + CreateDataDirLockFile(); + + return; +} diff --git a/src/gtm/path/path.c b/src/gtm/path/path.c index 4a3298b3d7..a3604f3b92 100644 --- a/src/gtm/path/path.c +++ b/src/gtm/path/path.c @@ -176,6 +176,7 @@ trim_trailing_separator(char *path) for (p--; p > path && IS_DIR_SEP(*p); p--) *p = '\0'; } + /* * If the given pathname isn't already absolute, make it so, interpreting * it relative to the current working directory. @@ -186,7 +187,7 @@ trim_trailing_separator(char *path) char * make_absolute_path(const char *path) { - char *new; + char *new; /* Returning null for null input is convenient for some callers */ if (path == NULL) @@ -194,8 +195,8 @@ make_absolute_path(const char *path) if (!is_absolute_path(path)) { - char *buf; - size_t buflen; + char *buf; + size_t buflen; buflen = MAXPGPATH; for (;;) @@ -221,7 +222,7 @@ make_absolute_path(const char *path) new = malloc(strlen(buf) + strlen(path) + 2); if (!new) - return NULL; + return NULL; sprintf(new, "%s/%s", buf, path); free(buf); } @@ -229,7 +230,7 @@ make_absolute_path(const char *path) { new = strdup(path); if (!new) - return NULL; + return NULL; } /* Make sure punctuation is canonical, too */ diff --git a/src/gtm/proxy/Makefile b/src/gtm/proxy/Makefile index ebc97ea7c2..e81fd03908 100644 --- a/src/gtm/proxy/Makefile +++ b/src/gtm/proxy/Makefile @@ -3,7 +3,8 @@ top_build_dir=../.. include $(top_build_dir)/gtm/Makefile.global -OBJS=proxy_main.o proxy_thread.o ../common/libgtm.a ../libpq/libpqcomm.a ../client/libgtmclient.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a +OBJS=proxy_main.o proxy_thread.o proxy_utils.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a + LDFLAGS=-L$(top_build_dir)/common -L$(top_build_dir)/libpq LIBS=-lpthread diff --git a/src/gtm/proxy/proxy_main.c b/src/gtm/proxy/proxy_main.c index e7632e15d8..9ad7551759 100644 --- a/src/gtm/proxy/proxy_main.c +++ b/src/gtm/proxy/proxy_main.c @@ -40,6 +40,8 @@ #include "gtm/gtm_seq.h" #include "gtm/gtm_msg.h" #include "gtm/libpq-int.h" +#include "gtm/gtm_ip.h" +#include "gtm/gtm_standby.h" extern int optind; extern char *optarg; @@ -730,7 +732,7 @@ GTMProxy_ThreadMain(void *argp) for (;;) { - ListCell *elem = NULL; + gtm_ListCell *elem = NULL; GTM_Result *res = NULL; /* @@ -818,7 +820,7 @@ GTMProxy_ThreadMain(void *argp) /* * Initialize the lists */ - thrinfo->thr_processed_commands = NIL; + thrinfo->thr_processed_commands = gtm_NIL; memset(thrinfo->thr_pending_commands, 0, sizeof (thrinfo->thr_pending_commands)); /* @@ -907,9 +909,9 @@ GTMProxy_ThreadMain(void *argp) * Read back the responses and put them on to the right backend * connection. */ - foreach(elem, thrinfo->thr_processed_commands) + gtm_foreach(elem, thrinfo->thr_processed_commands) { - GTMProxy_CommandInfo *cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem); + GTMProxy_CommandInfo *cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem); /* * If this is a continuation of a multi-part command response, we @@ -925,8 +927,8 @@ GTMProxy_ThreadMain(void *argp) ProcessResponse(thrinfo, cmdinfo, res); } - list_free_deep(thrinfo->thr_processed_commands); - thrinfo->thr_processed_commands = NIL; + gtm_list_free_deep(thrinfo->thr_processed_commands); + thrinfo->thr_processed_commands = gtm_NIL; /* * Now clean up disconnected connections @@ -1050,7 +1052,7 @@ ProcessResponse(GTMProxy_ThreadInfo *thrinfo, GTMProxy_CommandInfo *cmdinfo, * derive our GXID from the start GXID and the our position in the * command queue */ - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { if (res->gr_type != TXN_BEGIN_GETGXID_MULTI_RESULT) elog(ERROR, "Wrong result"); @@ -1198,7 +1200,7 @@ ProcessResponse(GTMProxy_ThreadInfo *thrinfo, GTMProxy_CommandInfo *cmdinfo, */ switch (res->gr_status) { - case 0: + case GTM_RESULT_OK: pq_beginmessage(&buf, 'S'); pq_sendint(&buf, res->gr_type, 4); pq_sendbytes(&buf, res->gr_proxy_data, res->gr_msglen); @@ -1334,6 +1336,13 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn, sizeof (GTM_PGXCNodeType)); memcpy(&cmd_data.cd_reg.nodenum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)), sizeof (GTM_PGXCNodeId)); + /* + * Now we have to waste the following host information. It is taken from + * the address field in the conn. + */ + len = pq_getmsgint(message, sizeof(GTM_StrLen)); + pq_getmsgbytes(message, len); + memcpy(&cmd_data.cd_reg.port, pq_getmsgbytes(message, sizeof (GTM_PGXCNodePort)), sizeof (GTM_PGXCNodePort)); memcpy(&cmd_data.cd_reg.proxynum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)), @@ -1341,6 +1350,9 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn, len = pq_getmsgint(message, sizeof (int)); cmd_data.cd_reg.datafolder = (char *)pq_getmsgbytes(message, len); + + /* Now we have one more data to waste, "status" */ + cmd_data.cd_reg.status = pq_getmsgint(message, sizeof(GTM_PGXCNodeStatus)); pq_getmsgend(message); /* Copy also remote host address in data to be proxied */ @@ -1560,7 +1572,7 @@ GTMProxy_ProxyCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn, cmdinfo->ci_mtype = mtype; cmdinfo->ci_conn = conninfo; cmdinfo->ci_res_index = 0; - thrinfo->thr_processed_commands = lappend(thrinfo->thr_processed_commands, cmdinfo); + thrinfo->thr_processed_commands = gtm_lappend(thrinfo->thr_processed_commands, cmdinfo); /* Finish the message. */ if (gtmpqPutMsgEnd(gtm_conn)) @@ -1587,16 +1599,28 @@ static void GTMProxy_ProxyPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo,GTM_ case MSG_NODE_REGISTER: /* Rebuild the message */ if (gtmpqPutMsgStart('C', true, gtm_conn) || + /* GTM Proxy Header */ gtmpqPutnchar((char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader), gtm_conn) || + /* Message Type */ gtmpqPutInt(MSG_NODE_REGISTER, sizeof (GTM_MessageType), gtm_conn) || + /* Node Type to Register */ gtmpqPutnchar((char *)&cmd_data.cd_reg.type, sizeof(GTM_PGXCNodeType), gtm_conn) || + /* Node Number to Register */ gtmpqPutnchar((char *)&cmd_data.cd_reg.nodenum, sizeof(GTM_PGXCNodeId), gtm_conn) || - gtmpqPutnchar((char *)&cmd_data.cd_reg.port, sizeof(GTM_PGXCNodePort), gtm_conn) || - gtmpqPutnchar((char *)>MProxyID, sizeof(GTM_PGXCNodeId), gtm_conn) || + /* Host Name (length) */ gtmpqPutInt(strlen(cmd_data.cd_reg.ipaddress), sizeof (GTM_StrLen), gtm_conn) || + /* Host Name (var-len) */ gtmpqPutnchar(cmd_data.cd_reg.ipaddress, strlen(cmd_data.cd_reg.ipaddress), gtm_conn) || + /* Port Number */ + gtmpqPutnchar((char *)&cmd_data.cd_reg.port, sizeof(GTM_PGXCNodePort), gtm_conn) || + /* Proxy ID (zero if connected to GTM directly) */ + gtmpqPutnchar((char *)>MProxyID, sizeof(GTM_PGXCNodeId), gtm_conn) || + /* Data Folder length */ gtmpqPutInt(strlen(cmd_data.cd_reg.datafolder), 4, gtm_conn) || - gtmpqPutnchar(cmd_data.cd_reg.datafolder, strlen(cmd_data.cd_reg.datafolder), gtm_conn)) + /* Data folder name (var-len) */ + gtmpqPutnchar(cmd_data.cd_reg.datafolder, strlen(cmd_data.cd_reg.datafolder), gtm_conn) || + /* Node Status */ + gtmpqPutInt(cmd_data.cd_reg.status, sizeof(GTM_PGXCNodeStatus), gtm_conn)) elog(ERROR, "Error proxing data"); break; @@ -1620,7 +1644,7 @@ static void GTMProxy_ProxyPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo,GTM_ cmdinfo->ci_mtype = mtype; cmdinfo->ci_conn = conninfo; cmdinfo->ci_res_index = 0; - thrinfo->thr_processed_commands = lappend(thrinfo->thr_processed_commands, cmdinfo); + thrinfo->thr_processed_commands = gtm_lappend(thrinfo->thr_processed_commands, cmdinfo); /* Finish the message. */ if (gtmpqPutMsgEnd(gtm_conn)) @@ -1649,7 +1673,7 @@ GTMProxy_CommandPending(GTMProxy_ConnectionInfo *conninfo, GTM_MessageType mtype cmdinfo->ci_conn = conninfo; cmdinfo->ci_res_index = 0; cmdinfo->ci_data = cmd_data; - thrinfo->thr_pending_commands[mtype] = lappend(thrinfo->thr_pending_commands[mtype], cmdinfo); + thrinfo->thr_pending_commands[mtype] = gtm_lappend(thrinfo->thr_pending_commands[mtype], cmdinfo); return; } @@ -1776,13 +1800,13 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo) GTMProxy_CommandInfo *cmdinfo = NULL; GTM_ProxyMsgHeader proxyhdr; GTM_Conn *gtm_conn = thrinfo->thr_gtm_conn; - ListCell *elem = NULL; + gtm_ListCell *elem = NULL; for (ii = 0; ii < MSG_TYPE_COUNT; ii++) { int res_index = 0; - if (list_length(thrinfo->thr_pending_commands[ii]) == 0) + if (gtm_list_length(thrinfo->thr_pending_commands[ii]) == 0) continue; /* @@ -1797,15 +1821,15 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo) switch (ii) { case MSG_TXN_BEGIN_GETGXID: - if (list_length(thrinfo->thr_pending_commands[ii]) <=0 ) + if (gtm_list_length(thrinfo->thr_pending_commands[ii]) <=0 ) elog(PANIC, "No pending commands of type %d", ii); if (gtmpqPutInt(MSG_TXN_BEGIN_GETGXID_MULTI, sizeof (GTM_MessageType), gtm_conn) || - gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) + gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) elog(ERROR, "Error sending data"); - foreach (elem, thrinfo->thr_pending_commands[ii]) + gtm_foreach (elem, thrinfo->thr_pending_commands[ii]) { - cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem); + cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem); Assert(cmdinfo->ci_mtype == ii); cmdinfo->ci_res_index = res_index++; if (gtmpqPutInt(cmdinfo->ci_data.cd_beg.iso_level, @@ -1823,19 +1847,19 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo) /* * Move the entire list to the processed command */ - thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands, + thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands, thrinfo->thr_pending_commands[ii]); - thrinfo->thr_pending_commands[ii] = NIL; + thrinfo->thr_pending_commands[ii] = gtm_NIL; break; case MSG_TXN_COMMIT: if (gtmpqPutInt(MSG_TXN_COMMIT_MULTI, sizeof (GTM_MessageType), gtm_conn) || - gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) + gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) elog(ERROR, "Error sending data"); - foreach (elem, thrinfo->thr_pending_commands[ii]) + gtm_foreach (elem, thrinfo->thr_pending_commands[ii]) { - cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem); + cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem); Assert(cmdinfo->ci_mtype == ii); cmdinfo->ci_res_index = res_index++; if (cmdinfo->ci_data.cd_rc.isgxid) @@ -1861,21 +1885,21 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo) /* * Move the entire list to the processed command */ - thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands, + thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands, thrinfo->thr_pending_commands[ii]); - thrinfo->thr_pending_commands[ii] = NIL; + thrinfo->thr_pending_commands[ii] = gtm_NIL; break; break; case MSG_TXN_ROLLBACK: if (gtmpqPutInt(MSG_TXN_ROLLBACK_MULTI, sizeof (GTM_MessageType), gtm_conn) || - gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) + gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) elog(ERROR, "Error sending data"); - foreach (elem, thrinfo->thr_pending_commands[ii]) + gtm_foreach (elem, thrinfo->thr_pending_commands[ii]) { - cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem); + cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem); Assert(cmdinfo->ci_mtype == ii); cmdinfo->ci_res_index = res_index++; if (cmdinfo->ci_data.cd_rc.isgxid) @@ -1902,19 +1926,19 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo) /* * Move the entire list to the processed command */ - thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands, + thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands, thrinfo->thr_pending_commands[ii]); - thrinfo->thr_pending_commands[ii] = NIL; + thrinfo->thr_pending_commands[ii] = gtm_NIL; break; case MSG_SNAPSHOT_GET: if (gtmpqPutInt(MSG_SNAPSHOT_GET_MULTI, sizeof (GTM_MessageType), gtm_conn) || - gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) + gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn)) elog(ERROR, "Error sending data"); - foreach (elem, thrinfo->thr_pending_commands[ii]) + gtm_foreach (elem, thrinfo->thr_pending_commands[ii]) { - cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem); + cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem); Assert(cmdinfo->ci_mtype == ii); cmdinfo->ci_res_index = res_index++; if (cmdinfo->ci_data.cd_rc.isgxid) @@ -1940,9 +1964,9 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo) /* * Move the entire list to the processed command */ - thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands, + thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands, thrinfo->thr_pending_commands[ii]); - thrinfo->thr_pending_commands[ii] = NIL; + thrinfo->thr_pending_commands[ii] = gtm_NIL; break; @@ -2015,7 +2039,7 @@ retry: * never set DataDir directly. */ void -SetDataDir() +SetDataDir(void) { char *new; @@ -2319,7 +2343,7 @@ UnregisterProxy(void) goto failed; /* Check on node type and node number */ - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_resdata.grd_node.type == type); Assert(res->gr_resdata.grd_node.nodenum == GTMProxyID); @@ -2358,10 +2382,13 @@ RegisterProxy(void) gtmpqPutInt(MSG_NODE_REGISTER, sizeof (GTM_MessageType), master_conn) || gtmpqPutnchar((char *)&type, sizeof(GTM_PGXCNodeType), master_conn) || gtmpqPutnchar((char *)>MProxyID, sizeof(GTM_PGXCNodeId), master_conn) || /* nodenum */ + gtmpqPutInt((int)strlen(ListenAddresses), sizeof(int), master_conn) || + gtmpqPutnchar(ListenAddresses, (int)strlen(ListenAddresses), master_conn) || gtmpqPutnchar((char *)&port, sizeof(GTM_PGXCNodePort), master_conn) || gtmpqPutnchar((char *)&proxynum, sizeof(GTM_PGXCNodeId), master_conn) || - gtmpqPutInt(strlen(GTMProxyDataDir), 4, master_conn) || - gtmpqPutnchar(GTMProxyDataDir, strlen(GTMProxyDataDir), master_conn)) + gtmpqPutInt((int)strlen(GTMProxyDataDir), 4, master_conn) || + gtmpqPutnchar(GTMProxyDataDir, strlen(GTMProxyDataDir), master_conn)|| + gtmpqPutInt(NODE_CONNECTED, sizeof(GTM_PGXCNodeStatus), master_conn)) goto failed; /* Finish the message. */ @@ -2380,7 +2407,7 @@ RegisterProxy(void) if ((res = GTMPQgetResult(master_conn)) == NULL) goto failed; - if (res->gr_status == 0) + if (res->gr_status == GTM_RESULT_OK) { Assert(res->gr_resdata.grd_node.type == type); Assert(res->gr_resdata.grd_node.nodenum == GTMProxyID); diff --git a/src/gtm/proxy/proxy_utils.c b/src/gtm/proxy/proxy_utils.c new file mode 100644 index 0000000000..9b928ca4dd --- /dev/null +++ b/src/gtm/proxy/proxy_utils.c @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * proxy_utils.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/gtm/proxy/proxy_utils.c + * + *------------------------------------------------------------------------- + */ + +#include "gtm/proxy_utils.h" + +#include "gtm/elog.h" +#include "gtm/gtm.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" +#include "gtm/gtm_seq.h" +#include "gtm/register.h" + +/* + * This function is a dummy function of gtm_proxy module to avoid + * object link problem. + * + * Most of command processing functions are existing only in GTM main + * module, but a few are both in GTM main and GTM proxy modules, which + * consist of same binary objects. And all the command processing + * functions require calling gtm_standby_check_communication_error() + * for GTM main. + * + * Two options should be considered here: + * (1) Moving all command processing functions into the common modules, or + * (2) Creating a dummy function in GTM proxy module. + * + * (1) may cause another hard thing because of object and variable + * referencing issue. For example GetMyThreadInfo is specified in both + * gtm.h and gtm_proxy.h and depends on the context. + * + * This is the reason why this dummy function is needed here. + * + * The object and module structure of GTM/GTM Proxy needs review, and + * fix to remove this kind of tricks. + */ +bool +gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn) +{ + return false; +} diff --git a/src/gtm/recovery/Makefile b/src/gtm/recovery/Makefile index 3af7fd787a..b95b6b047b 100644 --- a/src/gtm/recovery/Makefile +++ b/src/gtm/recovery/Makefile @@ -8,7 +8,7 @@ NAME=gtmrecovery SO_MAJOR_VERSION= 1 SO_MINOR_VERSION= 0 -OBJS=register.o ../client/libgtmclient.a +OBJS=register.o replication.o standby_utils.o ../client/libgtmclient.a all:all-lib diff --git a/src/gtm/recovery/register.c b/src/gtm/recovery/register.c index 163a860621..0488122b59 100644 --- a/src/gtm/recovery/register.c +++ b/src/gtm/recovery/register.c @@ -15,18 +15,20 @@ */ #include <fcntl.h> +#include <stdio.h> #include <sys/stat.h> #include <unistd.h> -#include "gtm/gtm_c.h" +#include "gtm/elog.h" #include "gtm/gtm.h" -#include "gtm/register.h" -#include "gtm/assert.h" -#include <stdio.h> +#include "gtm/gtm_client.h" +#include "gtm/gtm_serialize.h" +#include "gtm/gtm_standby.h" #include "gtm/libpq.h" +#include "gtm/libpq-int.h" #include "gtm/pqformat.h" -#include "gtm/gtm_msg.h" #include "gtm/stringinfo.h" + #include "gtm/gtm_ip.h" #define GTM_NODE_FILE "register.node" @@ -35,7 +37,7 @@ typedef struct GTM_NodeInfoHashBucket { - List *nhb_list; + gtm_List *nhb_list; GTM_RWLock nhb_lock; } GTM_PGXCNodeInfoHashBucket; @@ -61,6 +63,78 @@ static char *pgxcnode_copy_char(const char *str); #define pgxcnode_nodenum_equal(num1,num2) (num1 == num2) #define pgxcnode_port_equal(port1,port2) (port1 == port2) +size_t +pgxcnode_get_all(GTM_PGXCNodeInfo **data, size_t maxlen) +{ + GTM_PGXCNodeInfoHashBucket *bucket; + gtm_ListCell *elem; + int node = 0; + int i; + + for (i = 0; i < NODE_HASH_TABLE_SIZE; i++) + { + bucket = >M_PGXCNodes[i]; + + GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ); + + gtm_foreach(elem, bucket->nhb_list) + { + GTM_PGXCNodeInfo *curr_nodeinfo = NULL; + + curr_nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem); + if (curr_nodeinfo != NULL) + { + data[node] = curr_nodeinfo; + node++; + } + + if (node == maxlen) + break; + } + + GTM_RWLockRelease(&bucket->nhb_lock); + } + + return node; +} + +size_t +pgxcnode_find_by_type(GTM_PGXCNodeType type, GTM_PGXCNodeInfo **data, size_t maxlen) +{ + GTM_PGXCNodeInfoHashBucket *bucket; + gtm_ListCell *elem; + int node = 0; + int i; + + for (i = 0; i < NODE_HASH_TABLE_SIZE; i++) + { + bucket = >M_PGXCNodes[i]; + + GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ); + + gtm_foreach(elem, bucket->nhb_list) + { + GTM_PGXCNodeInfo *cur = NULL; + + cur = (GTM_PGXCNodeInfo *) gtm_lfirst(elem); + + if (cur != NULL && cur->type == type) + { + data[node] = cur; + elog(LOG, "pgxcnode_find_by_type: cur=%p, ipaddress=%s", cur, cur->ipaddress); + node++; + } + + if (node == maxlen) + break; + } + + GTM_RWLockRelease(&bucket->nhb_lock); + } + + return node; +} + /* * Find the pgxcnode info structure for the given node type and number key. */ @@ -70,16 +144,16 @@ pgxcnode_find_info(GTM_PGXCNodeType type, { uint32 hash = pgxcnode_gethash(nodenum); GTM_PGXCNodeInfoHashBucket *bucket; - ListCell *elem; + gtm_ListCell *elem; GTM_PGXCNodeInfo *curr_nodeinfo = NULL; bucket = >M_PGXCNodes[hash]; GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ); - foreach(elem, bucket->nhb_list) + gtm_foreach(elem, bucket->nhb_list) { - curr_nodeinfo = (GTM_PGXCNodeInfo *) lfirst(elem); + curr_nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem); if (pgxcnode_type_equal(curr_nodeinfo->type, type) && pgxcnode_nodenum_equal(curr_nodeinfo->nodenum, nodenum)) break; @@ -121,7 +195,7 @@ pgxcnode_remove_info(GTM_PGXCNodeInfo *nodeinfo) GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_WRITE); GTM_RWLockAcquire(&nodeinfo->node_lock, GTM_LOCKMODE_WRITE); - bucket->nhb_list = list_delete(bucket->nhb_list, nodeinfo); + bucket->nhb_list = gtm_list_delete(bucket->nhb_list, nodeinfo); GTM_RWLockRelease(&nodeinfo->node_lock); GTM_RWLockRelease(&bucket->nhb_lock); @@ -137,16 +211,16 @@ pgxcnode_add_info(GTM_PGXCNodeInfo *nodeinfo) { uint32 hash = pgxcnode_gethash(nodeinfo->nodenum); GTM_PGXCNodeInfoHashBucket *bucket; - ListCell *elem; + gtm_ListCell *elem; bucket = >M_PGXCNodes[hash]; GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_WRITE); - foreach(elem, bucket->nhb_list) + gtm_foreach(elem, bucket->nhb_list) { GTM_PGXCNodeInfo *curr_nodeinfo = NULL; - curr_nodeinfo = (GTM_PGXCNodeInfo *) lfirst(elem); + curr_nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem); /* GTM Proxy are always registered as they do not have Identification numbers yet */ if (pgxcnode_type_equal(curr_nodeinfo->type, nodeinfo->type) && @@ -208,7 +282,7 @@ pgxcnode_add_info(GTM_PGXCNodeInfo *nodeinfo) /* * Safe to add the structure to the list */ - bucket->nhb_list = lappend(bucket->nhb_list, nodeinfo); + bucket->nhb_list = gtm_lappend(bucket->nhb_list, nodeinfo); GTM_RWLockRelease(&bucket->nhb_lock); return 0; @@ -228,12 +302,13 @@ pgxcnode_copy_char(const char *str) * contextes. */ retstr = (char *) MemoryContextAlloc(TopMostMemoryContext, - strlen(str)); + strlen(str) + 1); if (retstr == NULL) ereport(ERROR, (ENOMEM, errmsg("Out of memory"))); memcpy(retstr, str, strlen(str)); + retstr[strlen(str)] = '\0'; return retstr; } @@ -252,9 +327,6 @@ Recovery_PGXCNodeUnregister(GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum, bool * Unregistration has to be made by the same connection as the one used for registration * or the one that reconnected the node. */ - if (!in_recovery && socket != nodeinfo->socket) - return EINVAL; - pgxcnode_remove_info(nodeinfo); /* Add a record to file on disk saying that this node has been unregistered correctly */ @@ -302,6 +374,14 @@ Recovery_PGXCNodeRegister(GTM_PGXCNodeType type, nodeinfo->status = status; nodeinfo->socket = socket; + elog(LOG, "Recovery_PGXCNodeRegister Request info: type=%d, nodenum=%d, port=%d," \ + "datafolder=%s, ipaddress=%s, status=%d", + type, nodenum, port, datafolder, ipaddress, status); + elog(LOG, "Recovery_PGXCNodeRegister Node info: type=%d, nodenum=%d, port=%d, "\ + "datafolder=%s, ipaddress=%s, status=%d", + nodeinfo->type, nodeinfo->nodenum, nodeinfo->port, + nodeinfo->datafolder, nodeinfo->ipaddress, nodeinfo->status); + /* Add PGXC Node Info to the global hash table */ errcode = pgxcnode_add_info(nodeinfo); @@ -326,41 +406,26 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message) GTM_PGXCNodeId nodenum, proxynum; GTM_PGXCNodePort port; char remote_host[NI_MAXHOST]; - char remote_port[NI_MAXSERV]; - char *datafolder; + char datafolder[NI_MAXHOST]; char *ipaddress; MemoryContext oldContext; - int strlen; + int len; StringInfoData buf; + GTM_PGXCNodeStatus status; - /* Get the Remote node IP and port to register it */ - remote_host[0] = '\0'; - remote_port[0] = '\0'; - - if (myport->remote_type != PGXC_NODE_GTM_PROXY) - { - if (gtm_getnameinfo_all(&myport->raddr.addr, myport->raddr.salen, - remote_host, sizeof(remote_host), - remote_port, sizeof(remote_port), - NI_NUMERICSERV)) - { - int ret = gtm_getnameinfo_all(&myport->raddr.addr, myport->raddr.salen, - remote_host, sizeof(remote_host), - remote_port, sizeof(remote_port), - NI_NUMERICHOST | NI_NUMERICSERV); - - if (ret) - ereport(WARNING, - (errmsg_internal("gtm_getnameinfo_all() failed"))); - } - } - - /* Read Node Type and number */ + /* Read Node Type */ memcpy(&type, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeType)), sizeof (GTM_PGXCNodeType)); + /* Node Number */ memcpy(&nodenum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)), sizeof (GTM_PGXCNodeId)); + /* Read Host name */ + len = pq_getmsgint(message, sizeof (int)); + memcpy(remote_host, (char *)pq_getmsgbytes(message, len), len); + remote_host[len] = '\0'; + ipaddress = remote_host; + /* Read Port Number */ memcpy(&port, pq_getmsgbytes(message, sizeof (GTM_PGXCNodePort)), sizeof (GTM_PGXCNodePort)); @@ -368,27 +433,27 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message) /* Read Proxy ID number (0 if no proxy used) */ memcpy(&proxynum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)), sizeof (GTM_PGXCNodeId)); - - /* - * Message is received from a proxy, get also the remote node address - * In the case a proxy registering itself, the remote address - * is directly taken from socket. - */ - if (myport->remote_type == PGXC_NODE_GTM_PROXY && - !myport->is_postmaster) - { - strlen = pq_getmsgint(message, sizeof (GTM_StrLen)); - ipaddress = (char *)pq_getmsgbytes(message, strlen); - } - else - ipaddress = remote_host; + elog(LOG, "ProcessPGXCNodeRegister: ipaddress = %s", ipaddress); /* * Finish by reading Data Folder (length and then string) */ + len = pq_getmsgint(message, sizeof (GTM_StrLen)); - strlen = pq_getmsgint(message, sizeof (GTM_StrLen)); - datafolder = (char *)pq_getmsgbytes(message, strlen); + memcpy(datafolder, (char *)pq_getmsgbytes(message, len), len); + datafolder[len] = '\0'; + + status = pq_getmsgint(message, sizeof (GTM_PGXCNodeStatus)); + + if ((type!=PGXC_NODE_GTM_PROXY) && + (type!=PGXC_NODE_GTM_PROXY_POSTMASTER) && + (type!=PGXC_NODE_COORDINATOR) && + (type!=PGXC_NODE_DATANODE) && + (type!=PGXC_NODE_GTM) && + (type!=PGXC_NODE_DEFAULT)) + ereport(ERROR, + (EINVAL, + errmsg("Unknown node type."))); /* * We must use the TopMostMemoryContext because the Node ID information is @@ -427,6 +492,30 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling node_register_internal() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = node_register_internal(GetMyThreadInfo->thr_conn->standby, + type, + ipaddress, + port, + nodenum, + datafolder, + status); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "node_register_internal() returns rc %d.", _rc); + } } /* @@ -481,6 +570,112 @@ ProcessPGXCNodeUnregister(Port *myport, StringInfo message) if (myport->remote_type != PGXC_NODE_GTM_PROXY) pq_flush(myport); + + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "calling node_unregister() for standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = node_unregister(GetMyThreadInfo->thr_conn->standby, + type, + nodenum); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "node_unregister() returns rc %d.", _rc); + } +} + +/* + * Process MSG_NODE_LIST + */ +void +ProcessPGXCNodeList(Port *myport, StringInfo message) +{ + MemoryContext oldContext; + StringInfoData buf; + int num_node = 13; + int i; + + GTM_PGXCNodeInfo *data[MAX_NODES]; + char *s_data[MAX_NODES]; + size_t s_datalen[MAX_NODES]; + + /* + * We must use the TopMostMemoryContext because the Node ID information is + * not bound to a thread and can outlive any of the thread specific + * contextes. + */ + oldContext = MemoryContextSwitchTo(TopMostMemoryContext); + + memset(data, 0, sizeof(GTM_PGXCNodeInfo *) * MAX_NODES); + memset(s_data, 0, sizeof(char *) * MAX_NODES); + + num_node = pgxcnode_get_all(data, MAX_NODES); + + for (i = 0; i < num_node; i++) + { + size_t s_len; + + s_len = gtm_get_pgxcnodeinfo_size(data[i]); + + /* + * Allocate memory blocks for serialized GTM_PGXCNodeInfo data. + */ + s_data[i] = (char *)malloc(s_len+1); + memset(s_data[i], 0, s_len+1); + + s_datalen[i] = gtm_serialize_pgxcnodeinfo(data[i], s_data[i], s_len+1); + + elog(LOG, "gtm_get_pgxcnodeinfo_size: s_len=%ld, s_datalen=%ld", s_len, s_datalen[i]); + } + + MemoryContextSwitchTo(oldContext); + + pq_getmsgend(message); + + /* + * Send a SUCCESS message back to the client + */ + pq_beginmessage(&buf, 'S'); + pq_sendint(&buf, NODE_LIST_RESULT, 4); + if (myport->remote_type == PGXC_NODE_GTM_PROXY) + { + GTM_ProxyMsgHeader proxyhdr; + proxyhdr.ph_conid = myport->conn_id; + pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader)); + } + pq_sendint(&buf, num_node, sizeof(int)); /* number of nodes */ + + /* + * Send pairs of GTM_PGXCNodeInfo size and serialized GTM_PGXCNodeInfo body. + */ + for (i = 0; i < num_node; i++) + { + pq_sendint(&buf, s_datalen[i], sizeof(int)); + pq_sendbytes(&buf, s_data[i], s_datalen[i]); + } + + pq_endmessage(myport, &buf); + + if (myport->remote_type != PGXC_NODE_GTM_PROXY) + pq_flush(myport); + + /* + * Release memory blocks for the serialized data. + */ + for (i = 0; i < num_node; i++) + { + free(s_data[i]); + } + + elog(LOG, "ProcessPGXCNodeList() ok."); } /* @@ -490,10 +685,10 @@ ProcessPGXCNodeUnregister(Port *myport, StringInfo message) void Recovery_SaveRegisterInfo(void) { - GTM_PGXCNodeInfoHashBucket *bucket; - ListCell *elem; - GTM_PGXCNodeInfo *nodeinfo = NULL; - int hash, ctlfd; +GTM_PGXCNodeInfoHashBucket *bucket; +gtm_ListCell *elem; +GTM_PGXCNodeInfo *nodeinfo = NULL; +int hash, ctlfd; char filebkp[GTM_NODE_FILE_MAX_PATH]; GTM_RWLockAcquire(&RegisterFileLock, GTM_LOCKMODE_WRITE); @@ -510,18 +705,18 @@ Recovery_SaveRegisterInfo(void) return; } - for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++) +for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++) { bucket = >M_PGXCNodes[hash]; GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ); /* Write one by one information about registered nodes */ - foreach(elem, bucket->nhb_list) + gtm_foreach(elem, bucket->nhb_list) { int len; - nodeinfo = (GTM_PGXCNodeInfo *) lfirst(elem); + nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem); if (nodeinfo == NULL) break; @@ -550,7 +745,7 @@ Recovery_SaveRegisterInfo(void) } GTM_RWLockRelease(&bucket->nhb_lock); - } + } close(ctlfd); @@ -718,8 +913,11 @@ Recovery_PGXCNodeDisconnect(Port *myport) if (nodeinfo != NULL) { - /* Disconnection cannot be made with another socket than the one used for registration */ - if (myport->sock != nodeinfo->socket) + /* + * Disconnection cannot be made with another socket than the one used for registration. + * socket may have a dummy value (-1) under GTM standby node. + */ + if (nodeinfo->socket >= 0 && myport->sock != nodeinfo->socket) return; GTM_RWLockAcquire(&nodeinfo->node_lock, GTM_LOCKMODE_WRITE); @@ -805,4 +1003,29 @@ ProcessPGXCNodeBackendDisconnect(Port *myport, StringInfo message) } MemoryContextSwitchTo(oldContext); + + /* + * Forwarding MSG_BACKEND_DISCONNECT message to GTM standby. + * No need to wait any response. + */ + if (GetMyThreadInfo->thr_conn->standby) + { + int _rc; + GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby; + int count = 0; + + elog(LOG, "forwarding MSG_BACKEND_DISCONNECT to standby GTM %p.", + GetMyThreadInfo->thr_conn->standby); + +retry: + _rc = backend_disconnect(GetMyThreadInfo->thr_conn->standby, + is_postmaster, + type, + nodenum); + + if (gtm_standby_check_communication_error(&count, oldconn)) + goto retry; + + elog(LOG, "MSG_BACKEND_DISCONNECT rc=%d done.", _rc); + } } diff --git a/src/gtm/recovery/replication.c b/src/gtm/recovery/replication.c new file mode 100644 index 0000000000..a6789758b7 --- /dev/null +++ b/src/gtm/recovery/replication.c @@ -0,0 +1,121 @@ +/*------------------------------------------------------------------------- + * + * replication.c + * Controlling the initialization and end of replication process of GTM data + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * + * IDENTIFICATION + * src/gtm/recovery/replication.c + * + *------------------------------------------------------------------------- + */ +#include "gtm/replication.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "gtm/gtm_c.h" +#include "gtm/gtm.h" +#include "gtm/gtm_txn.h" +#include "gtm/standby_utils.h" +#include "gtm/gtm_standby.h" +#include "gtm/register.h" +#include "gtm/assert.h" +#include <stdio.h> +#include "gtm/libpq.h" +#include "gtm/pqformat.h" +#include "gtm/gtm_msg.h" +#include "gtm/gtm_ip.h" + +/* + * Process MSG_NODE_BEGIN_REPLICATION_INIT + */ +void +ProcessBeginReplicationInitialSyncRequest(Port *myport, StringInfo message) +{ + StringInfoData buf; + MemoryContext oldContext; + + pq_getmsgend(message); + + if (Recovery_IsStandby()) + ereport(ERROR, + (EPERM, + errmsg("Operation not permitted under the standby mode."))); + + oldContext = MemoryContextSwitchTo(TopMemoryContext); + + /* Acquire global locks to copy resource data to the standby. */ + GTM_RWLockAcquire(>MTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE); + GTM_RWLockAcquire(>MTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE); + elog(LOG, "Prepared for copying data with holding XidGenLock and TransArrayLock."); + + MemoryContextSwitchTo(oldContext); + + pq_beginmessage(&buf, 'S'); + pq_sendint(&buf, NODE_BEGIN_REPLICATION_INIT_RESULT, 4); + if (myport->remote_type == PGXC_NODE_GTM_PROXY) + { + GTM_ProxyMsgHeader proxyhdr; + proxyhdr.ph_conid = myport->conn_id; + pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader)); + } + pq_endmessage(myport, &buf); + + if (myport->remote_type != PGXC_NODE_GTM_PROXY) + pq_flush(myport); + + elog(LOG, "ProcessBeginReplicationInitialSyncRequest() ok."); + + return; +} + +/* + * Process MSG_NODE_END_REPLICATION_INIT + */ +void +ProcessEndReplicationInitialSyncRequest(Port *myport, StringInfo message) +{ + StringInfoData buf; + MemoryContext oldContext; + + pq_getmsgend(message); + + if (Recovery_IsStandby()) + ereport(ERROR, + (EPERM, + errmsg("Operation not permitted under the standby mode."))); + + oldContext = MemoryContextSwitchTo(TopMemoryContext); + + /* + * Release global locks after copying resource data to the standby. + */ + GTM_RWLockRelease(>MTransactions.gt_TransArrayLock); + GTM_RWLockRelease(>MTransactions.gt_XidGenLock); + elog(LOG, "XidGenLock and TransArrayLock released."); + + MemoryContextSwitchTo(oldContext); + + pq_beginmessage(&buf, 'S'); + pq_sendint(&buf, NODE_END_REPLICATION_INIT_RESULT, 4); + if (myport->remote_type == PGXC_NODE_GTM_PROXY) + { + GTM_ProxyMsgHeader proxyhdr; + proxyhdr.ph_conid = myport->conn_id; + pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader)); + } + pq_endmessage(myport, &buf); + + if (myport->remote_type != PGXC_NODE_GTM_PROXY) + pq_flush(myport); + + elog(LOG, "ProcessEndReplicationInitialSyncRequest() ok."); + + return; +} diff --git a/src/gtm/recovery/standby_utils.c b/src/gtm/recovery/standby_utils.c new file mode 100644 index 0000000000..4acf81c2b3 --- /dev/null +++ b/src/gtm/recovery/standby_utils.c @@ -0,0 +1,86 @@ +/*------------------------------------------------------------------------- + * + * standby_utils.c + * Utilities for GTM standby global values + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * + * IDENTIFICATION + * src/gtm/recovery/standby_utils.c + * + *------------------------------------------------------------------------- + */ + +#include "gtm/gtm_c.h" +#include "gtm/standby_utils.h" +#include "gtm/gtm_lock.h" + +/* + * Variables to interact with GTM active under GTM standby mode. + */ +static bool GTM_StandbyMode = false; +static char *GTM_ActiveAddress; +static int GTM_ActivePort; + +/* For thread safety, values above are protected by a lock */ +static GTM_RWLock StandbyLock; + +bool +Recovery_IsStandby(void) +{ + bool res; + GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_READ); + res = GTM_StandbyMode; + GTM_RWLockRelease(&StandbyLock); + return GTM_StandbyMode; +} + +void +Recovery_StandbySetStandby(bool standby) +{ + GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_WRITE); + GTM_StandbyMode = standby; + GTM_RWLockRelease(&StandbyLock); +} + +void +Recovery_StandbySetConnInfo(const char *addr, int port) +{ + GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_WRITE); + GTM_ActiveAddress = strdup(addr); + GTM_ActivePort = port; + GTM_RWLockRelease(&StandbyLock); +} + +int +Recovery_StandbyGetActivePort(void) +{ + int res; + + GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_READ); + res = GTM_ActivePort; + GTM_RWLockRelease(&StandbyLock); + + return res; +} + +char * +Recovery_StandbyGetActiveAddress(void) +{ + char *res; + + GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_READ); + res = GTM_ActiveAddress; + GTM_RWLockRelease(&StandbyLock); + + return res; +} + +void +Recovery_InitStandbyLock(void) +{ + GTM_RWLockInit(&StandbyLock); +} diff --git a/src/gtm/test/Makefile b/src/gtm/test/Makefile new file mode 100644 index 0000000000..e6155ea7f6 --- /dev/null +++ b/src/gtm/test/Makefile @@ -0,0 +1,53 @@ +# Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + +top_build_dir=../.. +include $(top_build_dir)/gtm/Makefile.global + +override CPPFLAGS := -I$(top_build_dir)/gtm/client $(CPPFLAGS) + +SRCS=test_serialize.c test_connect.c test_node.c test_node5.c test_txn.c test_txn4.c test_txn5.c test_repli.c test_repli2.c test_seq.c test_seq4.c test_seq5.c test_scenario.c test_startup.c test_standby.c test_common.c + +PROGS=test_serialize test_connect test_txn test_txn4 test_txn5 test_repli test_repli2 test_seq test_seq4 test_seq5 test_scenario test_startup test_node test_node5 test_standby + +OBJS=$(SRCS:.c=.o) +LIBS=$(top_build_dir)/gtm/client/libgtmclient.a \ + $(top_build_dir)/gtm/common/libgtm.a \ + $(top_build_dir)/gtm/libpq/libpqcomm.a + +LOADLIBES=-lpthread +CFLAGS=-g -O0 + +all: $(PROGS) + +test_serialize: test_serialize.o test_common.o $(LIBS) + +test_connect: test_connect.o test_common.o $(LIBS) +test_startup: test_startup.o test_common.o $(LIBS) + +test_node: test_node.o test_common.o $(LIBS) +test_node5: test_node5.o test_common.o $(LIBS) + +test_txn: test_txn.o test_common.o $(LIBS) + +test_txn4: test_txn4.o test_common.o $(LIBS) +test_txn5: test_txn5.o test_common.o $(LIBS) + +test_repli: test_repli.o test_common.o $(LIBS) + +test_standby: test_standby.o test_common.o $(LIBS) + +test_repli2: test_repli2.o test_common.o $(LIBS) + +test_seq: test_seq.o test_common.o $(LIBS) +test_seq4: test_seq4.o test_common.o $(LIBS) +test_seq5: test_seq5.o test_common.o $(LIBS) + +test_scenario: test_scenario.o test_common.o $(LIBS) + +clean: + rm -f $(OBJS) *~ + rm -f $(PROGS) + +distclean: clean + +maintainer-clean: distclean diff --git a/src/gtm/test/clean.sh b/src/gtm/test/clean.sh new file mode 100644 index 0000000000..87c452b3ce --- /dev/null +++ b/src/gtm/test/clean.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# GTM start script for test + +pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd + +export PATH=/tmp/pgxc/bin:$PATH + +# ------------------------------- +# starting standby... +# ------------------------------- +echo "cleaning standby..." +export DATA=/tmp/pgxc/data/gtm_standby + +pushd $DATA +rm -rf gtm.control gtm.opts gtm.pid register.node +cat /dev/null > gtm.log +popd + +# ------------------------------- +# starting active... +# ------------------------------- +echo "cleaning active..." +export DATA=/tmp/pgxc/data/gtm + +pushd $DATA +rm -rf gtm.control gtm.opts gtm.pid register.node +cat /dev/null > gtm.log +popd diff --git a/src/gtm/test/promote.sh b/src/gtm/test/promote.sh new file mode 100644 index 0000000000..ae60e9ccb3 --- /dev/null +++ b/src/gtm/test/promote.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# GTM start script for test + +pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd + +export PATH=/tmp/pgxc/bin:$PATH + +# ------------------------------- +# promoting standby... +# ------------------------------- +echo "promoting standby..." +export DATA=/tmp/pgxc/data/gtm_standby + +gtm_ctl -D ${DATA} -S gtm_standby promote + +# ------------------------------- +# process check +# ------------------------------- +echo "checking process..." +ps -aef |grep gtm diff --git a/src/gtm/test/regress.sh b/src/gtm/test/regress.sh new file mode 100644 index 0000000000..314d17cda9 --- /dev/null +++ b/src/gtm/test/regress.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +cat /dev/null>regress.log + +./test_serialize | tee -a regress.log 2>&1 + +./stop.sh +./start_a.sh +./test_connect 2>&1 | tee -a regress.log +./test_node 2>&1 | tee -a regress.log +./test_txn 2>&1 | tee -a regress.log +./test_seq 2>&1 | tee -a regress.log + +echo "" +echo "=========== SUMMARY ============" +date +echo -n "Assert: " +grep -c ASSERT regress.log diff --git a/src/gtm/test/start.sh b/src/gtm/test/start.sh new file mode 100644 index 0000000000..24951ff77a --- /dev/null +++ b/src/gtm/test/start.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# GTM start script for test + +pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd + +./start_a.sh + +echo "sleeping 3 seconds..." +sleep 3; + +./start_s.sh diff --git a/src/gtm/test/start_a.sh b/src/gtm/test/start_a.sh new file mode 100644 index 0000000000..4a6c1c16fa --- /dev/null +++ b/src/gtm/test/start_a.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# GTM start script for test + +pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd + +export PATH=/tmp/pgxc/bin:$PATH +export DATA=/tmp/pgxc/data/gtm + +# ------------------------------- +# starting active... +# ------------------------------- +echo "starting active..." + +gtm_ctl -D ${DATA} -S gtm stop +rm -rf ${DATA}/gtm.opts ${DATA}/gtm.pid ${DATA}/register.node + +gtm_ctl -D ${DATA} -S gtm -o "-n 101" start + +# ------------------------------- +# process check +# ------------------------------- +echo "checking process..." +ps -aef |grep gtm diff --git a/src/gtm/test/start_s.sh b/src/gtm/test/start_s.sh new file mode 100644 index 0000000000..b016bcee32 --- /dev/null +++ b/src/gtm/test/start_s.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# GTM start script for test + +pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd + +export PATH=/tmp/pgxc/bin:$PATH + +# ------------------------------- +# starting standby... +# ------------------------------- +echo "starting standby..." + +export DATA=/tmp/pgxc/data/gtm_standby + +gtm_ctl -D ${DATA} -S gtm stop +rm -rf ${DATA}/gtm.opts ${DATA}/gtm.pid ${DATA}/register.node + +gtm_ctl -D ${DATA} -S gtm_standby -o "-n 102 -s -p 6667 -i 127.0.0.1 -q 6666" start + +# ------------------------------- +# process check +# ------------------------------- +echo "checking process..." +ps -aef |grep gtm diff --git a/src/gtm/test/stop.sh b/src/gtm/test/stop.sh new file mode 100644 index 0000000000..4660ecc725 --- /dev/null +++ b/src/gtm/test/stop.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# GTM start script for test + +pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd + +export PATH=/tmp/pgxc/bin:$PATH + +# ------------------------------- +# starting standby... +# ------------------------------- +echo "stopping standby..." +export DATA=/tmp/pgxc/data/gtm_standby + +gtm_ctl -D ${DATA} -S gtm stop + +# ------------------------------- +# starting active... +# ------------------------------- +echo "stopping active..." +export DATA=/tmp/pgxc/data/gtm + +gtm_ctl -D ${DATA} -S gtm stop + +killall -9 gtm gtm_standby + +# ------------------------------- +# process check +# ------------------------------- +echo "checking process..." +ps -aef |grep gtm diff --git a/src/gtm/test/test_common.c b/src/gtm/test/test_common.c new file mode 100644 index 0000000000..df8b5817e3 --- /dev/null +++ b/src/gtm/test/test_common.c @@ -0,0 +1,80 @@ +#include "test_common.h" + +pthread_key_t threadinfo_key; + +GTM_ThreadID TopMostThreadID; + +GTM_Conn *conn = NULL; +GTM_Conn *conn2 = NULL; +GTM_Timestamp *timestamp = NULL; +char connect_string[100]; + +void +print_nodeinfo(GTM_PGXCNodeInfo d) +{ + client_log(("type=%d, nodenum=%d, proxynum=%d, ipaddress=%s, port=%d, datafolder=%s, status=%d\n", + d.type, + d.nodenum, + d.proxynum, + d.ipaddress, + d.port, + d.datafolder, + d.status)); +} + + +/* + * Connect to active GTM. + */ +void +connect1() +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); +} + +/* + * Connect to standby GTM. + */ +void +connect2() +{ + sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=102 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); +} + + +/* + * Get a word count with using grep command in a log file. + */ +int +grep_count(const char *file, const char *key) +{ + FILE *fp; + int count; + char cmd[1024]; + + snprintf(cmd, sizeof(cmd), "grep -c '%s' %s", key, file); + + fp = popen(cmd, "r"); + fscanf(fp, "%d", &count); + pclose(fp); + + return count; +} diff --git a/src/gtm/test/test_connect.c b/src/gtm/test/test_connect.c new file mode 100644 index 0000000000..cb25378455 --- /dev/null +++ b/src/gtm/test/test_connect.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +#define client_log(x) printf x + +void +setUp() +{ +} + +void +tearDown() +{ +} + +void +test01() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM_PROXY); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test02() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM_PROXY_POSTMASTER); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test03() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_COORDINATOR); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test04() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_DATANODE); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test05() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test06() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_DEFAULT); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test07() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + 12); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test08() +{ + GTM_Conn *conn; + char connect_string[100]; + + SETUP(); + + sprintf(connect_string, "host=localhost port=6668 pgxc_node_id=1 remote_type=%d", + 12); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + + printf("conn = %p\n", conn); + + client_log(("PGconnectGTM() ok.\n")); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); /* must fail */ + test08(); /* must fail */ + + return 0; +} diff --git a/src/gtm/test/test_node.c b/src/gtm/test/test_node.c new file mode 100644 index 0000000000..c83ac552eb --- /dev/null +++ b/src/gtm/test/test_node.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + connect1(); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_node_01() +{ + int rc; + + SETUP(); + + rc = node_register(conn, PGXC_NODE_DATANODE, 6666, 1, "/tmp/pgxc/data/gtm"); + _ASSERT( rc >= 0 ); + + TEARDOWN(); +} + +void +test_node_02() +{ + int rc; + + SETUP(); + + rc = node_unregister(conn, PGXC_NODE_DATANODE, 1); + _ASSERT( rc >= 0 ); + + TEARDOWN(); +} + +void +test_node_03() +{ + GTM_PGXCNodeInfo *data; + int rc, i; + + SETUP(); + + data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo)*128 ); + memset(data, 0, sizeof(GTM_PGXCNodeInfo)*128); + + rc = get_node_list(conn, data, 128); + _ASSERT( rc>=0 ); + + for (i=0 ; i<rc ; i++) + { + print_nodeinfo(data[i]); + } + + free(data); + + TEARDOWN(); +} + +void +test_node_04() +{ + GTM_PGXCNodeInfo *data; + int rc, i; + + SETUP(); + + data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo)*128 ); + memset(data, 0, sizeof(GTM_PGXCNodeInfo)*128); + + rc = node_register(conn, PGXC_NODE_DATANODE, 6666, 1, "/tmp/pgxc/data/gtm"); + _ASSERT( rc>=0 ); + + rc = get_node_list(conn, data, 128); + _ASSERT( rc>=0 ); + + for (i=0 ; i<rc ; i++) + { + print_nodeinfo(data[i]); + } + + free(data); + + TEARDOWN(); +} + + +void +test_node_05() +{ + int rc, i; + + SETUP(); + + rc = node_unregister(conn, PGXC_NODE_DATANODE, 101); + + rc = node_register(conn, PGXC_NODE_DATANODE, 6666, 101, "/tmp/pgxc/data/gtm"); + _ASSERT( rc>=0 ); + + sleep(5); + + rc = backend_disconnect(conn, true, PGXC_NODE_DATANODE, 101); + _ASSERT( rc>=0 ); + + rc = node_unregister(conn, PGXC_NODE_DATANODE, 101); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + + +int +main(int argc, char *argv[]) +{ + test_node_01(); + test_node_02(); + + test_node_03(); + test_node_04(); + + test_node_05(); + + return 0; +} diff --git a/src/gtm/test/test_node5.c b/src/gtm/test/test_node5.c new file mode 100644 index 0000000000..7454b8795a --- /dev/null +++ b/src/gtm/test/test_node5.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + system("./stop.sh"); + system("./clean.sh"); + system("./start.sh"); + sleep(1); +} + +void +tearDown() +{ +} + +void +test_node5_01() +{ + int rc; + + SETUP(); + + /* + * active + */ + connect1(); + + rc = node_register(conn, PGXC_NODE_DATANODE, 16666, 1001, "/tmp/pgxc/data/gtm"); + _ASSERT( rc >= 0 ); + + GTMPQfinish(conn); + sleep(3); + + /* + * standby + */ + connect2(); + + rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001); + _ASSERT( rc >= 0 ); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test_node5_02() +{ + int rc; + + SETUP(); + + /* + * active + */ + connect1(); + + rc = node_register(conn, PGXC_NODE_DATANODE, 16666, 1001, "/tmp/pgxc/data/gtm"); + _ASSERT( rc >= 0 ); + + rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001); + _ASSERT( rc >= 0 ); + + GTMPQfinish(conn); + sleep(3); + + /* + * standby + */ + connect2(); + + rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001); + _ASSERT( rc<0 ); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +void +test_node5_03() +{ + int rc; + + SETUP(); + + /* + * active + */ + connect1(); + + rc = node_register(conn, PGXC_NODE_DATANODE, 16666, 1001, "/tmp/pgxc/data/gtm"); + _ASSERT( rc >= 0 ); + + system("killall -9 gtm"); + system("./promote.sh"); + sleep(1); + + GTMPQfinish(conn); + connect2(); + + rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001); + _ASSERT( rc >= 0 ); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test_node5_01(); + test_node5_02(); + + test_node5_03(); + + return 0; +} diff --git a/src/gtm/test/test_repli.c b/src/gtm/test/test_repli.c new file mode 100644 index 0000000000..7edbbab3b7 --- /dev/null +++ b/src/gtm/test/test_repli.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" +#include "gtm/register.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + + _ASSERT( conn!=NULL ); + + client_log(("PGconnectGTM() ok.\n")); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + rc = begin_replication_initial_sync(conn); + if ( rc>=0 ) + client_log(("begin_replication_initial_sync() ok.\n")); + else + client_log(("begin_replication_initial_sync() failed.\n")); + + rc = end_replication_initial_sync(conn); + if ( rc>=0 ) + client_log(("end_replication_initial_sync() ok.\n")); + else + client_log(("end_replication_initial_sync() failed.\n")); + + TEARDOWN(); +} + +void +test04() +{ + GTM_PGXCNodeInfo *data; + int rc, i; + GTM_Transactions txn; + GlobalTransactionId tmp; + GTM_Timestamp ts; + + // data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo)*128 ); + // memset(data, 0, sizeof(GTM_PGXCNodeInfo)*128); + + SETUP(); + + tmp = begin_transaction(conn, GTM_ISOLATION_RC, &ts); + + rc = get_txn_gxid_list(conn, &txn); + if ( rc>=0 ) + client_log(("get_txn_gxid_list() ok.\n")); + else + client_log(("get_txn_gxid_list() failed.\n")); + + abort_transaction(conn, tmp); + + TEARDOWN(); +} + +void +test05() +{ + GlobalTransactionId next_gxid; + int rc, i; + + SETUP(); + + next_gxid = get_next_gxid(conn); + if ( next_gxid!=InvalidGlobalTransactionId ) + client_log(("get_next_gxid() ok. - %ld\n", next_gxid)); + else + client_log(("get_txn_gxid_list() failed.\n")); + + TEARDOWN(); +} + +void +test08() +{ + GlobalTransactionId next_gxid; + GTM_SequenceKeyData key1; + GTM_SequenceKeyData key2; + GTM_SequenceKeyData key3; + int rc, i; + GTM_SeqInfo *seq_list[1024]; + + SETUP(); + + key1.gsk_keylen = strlen("seq1"); + key1.gsk_key = strdup("seq1"); + key2.gsk_keylen = strlen("seq2"); + key2.gsk_key = strdup("seq2"); + key3.gsk_keylen = strlen("seq3"); + key3.gsk_key = strdup("seq3"); + + close_sequence(conn, &key1); + close_sequence(conn, &key2); + close_sequence(conn, &key3); + + for (i=0 ; i<1024 ; i++) + seq_list[i] = NULL; + + rc = get_sequence_list(conn, seq_list, 1024); + if ( rc>=0 ) + { + client_log(("get_seq_list() ok. - %d\n", rc)); + + for (i=0 ; i<rc ; i++) + { + client_log(("key = %s\n", seq_list[i]->gs_key->gsk_key)); + } + } + else + client_log(("get_seq_list() failed.\n")); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test01(); + test04(); + /* + test05(); + test08(); + */ + + return 0; +} diff --git a/src/gtm/test/test_repli2.c b/src/gtm/test/test_repli2.c new file mode 100644 index 0000000000..46c64d42e7 --- /dev/null +++ b/src/gtm/test/test_repli2.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" +#include "gtm/register.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + + _ASSERT( conn!=NULL ); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test01() +{ + int rc; + char host[1024]; + + SETUP(); + + node_get_local_addr(conn, host, sizeof(host)); + + rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 101, "/tmp/pgxc/data/gtm_standby", NODE_DISCONNECTED); + _ASSERT(rc == 0); + + rc = node_unregister(conn, PGXC_NODE_GTM, 101); + _ASSERT(rc == 0); + + TEARDOWN(); +} + +void +test02() +{ + int rc; + char host[1024]; + + SETUP(); + + node_get_local_addr(conn, host, sizeof(host)); + + /* + * If a node already registered as a "DISCONNECTED" node, + * node_register() would not detect conflict of node information. + * + * See pgxcnode_add_info() for more details. + */ + rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 101, "/tmp/pgxc/data/gtm_standby", NODE_CONNECTED); + _ASSERT(rc == 0); + + rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 101, "/tmp/pgxc/data/gtm_standby", NODE_CONNECTED); + _ASSERT(rc != 0); + + TEARDOWN(); +} + +void +test03() +{ + int rc; + + SETUP(); + + rc = node_unregister(conn, PGXC_NODE_GTM, 101); + _ASSERT( rc==0 ); + + TEARDOWN(); +} + +void +test04() +{ + int rc; + + SETUP(); + + rc = node_unregister(conn, PGXC_NODE_GTM, 101); + _ASSERT( rc!=0 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test01(); + test02(); + test03(); + test04(); + + return 0; +} diff --git a/src/gtm/test/test_scenario.c b/src/gtm/test/test_scenario.c new file mode 100644 index 0000000000..91c146ed3e --- /dev/null +++ b/src/gtm/test/test_scenario.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" +#include "gtm/register.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + + _ASSERT( conn!=NULL ); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test01() +{ + GlobalTransactionId gxid; + int rc; + char host[1024]; + + printf("\n=== test01:node_register ===\n"); + + setUp(); + + node_get_local_addr(conn, host, sizeof(host)); + + /* + * starting + */ + rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 102, "/tmp/pgxc/data/gtm_standby", NODE_DISCONNECTED); + _ASSERT(rc == 0); + rc = node_unregister(conn, PGXC_NODE_GTM, 102); + _ASSERT(rc == 0); + + rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 102, "/tmp/pgxc/data/gtm_standby", NODE_CONNECTED); + _ASSERT(rc == 0); + + sleep(10); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid!=InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc!=-1 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc!=-1 ); + + sleep(10); + + /* + * closing + */ + rc = node_unregister(conn, PGXC_NODE_GTM, 102); + _ASSERT( rc==0 ); + + tearDown(); +} + +int +main(int argc, char *argv[]) +{ + test01(); + + return 0; +} diff --git a/src/gtm/test/test_seq.c b/src/gtm/test/test_seq.c new file mode 100644 index 0000000000..e86e9ede4f --- /dev/null +++ b/src/gtm/test/test_seq.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" +#include "gtm/register.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + int i; + + connect1(); + + /* delete old stuffs */ + for (i=0 ; i<10 ; i++) + { + GTM_SequenceKeyData seqkey; + char key[16]; + + snprintf(key, sizeof(key), "seq%d", i); + seqkey.gsk_key = key; + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + close_sequence(conn, &seqkey); + } + + /* Create the first one. */ + { + GTM_SequenceKeyData seqkey; + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + int rc; + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + increment = 1; + minval = 0; + maxval = 10000; + startval = 0; + cycle = true; + + rc = open_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + cycle); + + _ASSERT( rc>=0 ); + } +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_seq_01() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq2"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + increment = 1; + minval = 0; + maxval = 10000; + startval = 0; + cycle = true; + + rc = open_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + cycle); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_seq_02() +{ + int rc; + GTM_SequenceKeyData seqkey; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + rc = close_sequence(conn, &seqkey); + + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_seq_03() +{ + GTM_SequenceKeyData seqkey; + GTM_Sequence cur; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + cur = get_current(conn, &seqkey); + _ASSERT( cur==0 ); + + TEARDOWN(); +} + +void +test_seq_04() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + GTM_Sequence cur; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + cur = get_next(conn, &seqkey); + _ASSERT( cur==0 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==1 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==2 ); + + TEARDOWN(); +} + +void +test_seq_05() +{ + int rc; + GTM_SequenceKeyData seqkey; + + GTM_Sequence cur; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + rc = set_val(conn, &seqkey, 1000, true); + _ASSERT( rc>=0 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==1001 ); + cur = get_next(conn, &seqkey); + _ASSERT( cur==1002 ); + + /* + * FIXME: When `iscalled' is false, set_val() does not affect the sequence value. + */ + rc = set_val(conn, &seqkey, 1000, false); + _ASSERT( rc>=0 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==0 ); /* FIXME: */ + cur = get_next(conn, &seqkey); + _ASSERT( cur==1 ); /* FIXME: */ + + TEARDOWN(); +} + + +void +test_seq_06() +{ + int rc; + GTM_SequenceKeyData seqkey; + int i; + + GTM_Sequence cur; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + for (i=0 ; i<1000 ; i++) + { + cur = get_next(conn, &seqkey); + _ASSERT( cur==i ); + } + /* get_next() x 1000 done. */ + + rc = reset_sequence(conn, &seqkey); + _ASSERT( rc>=0 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==1 ); + + TEARDOWN(); +} + +void +test_seq_07() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_SequenceKeyData newseqkey; + int i; + + GTM_Sequence cur; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + /* create a key */ + newseqkey.gsk_key = strdup("seqnew"); + newseqkey.gsk_keylen = strlen(newseqkey.gsk_key); + newseqkey.gsk_type = GTM_SEQ_FULL_NAME; + + rc = rename_sequence(conn, &seqkey, &newseqkey); + _ASSERT( rc>=0 ); + + cur = get_next(conn, &newseqkey); + _ASSERT( cur==0 ); + + cur = get_next(conn, &newseqkey); + _ASSERT( cur==1 ); + + rc = close_sequence(conn, &newseqkey); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + + +void +test_seq_08() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_SequenceKeyData newseqkey; + int i; + + GTM_Sequence cur; + + SETUP(); + + /* create a key */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + cur = get_next(conn, &seqkey); + _ASSERT( cur==0 ); + + { + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + increment = 1; + minval = 100; + maxval = 10000; + startval = 100; + cycle = true; + + rc = alter_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + 256, /* lastval */ + cycle, + true); + + _ASSERT( rc>=0 ); + } + + cur = get_current(conn, &seqkey); + _ASSERT( cur==100 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==101 ); + + TEARDOWN(); +} + + +void +test_seq_09() +{ + GlobalTransactionId next_gxid; + GTM_SequenceKeyData key1; + GTM_SequenceKeyData key2; + GTM_SequenceKeyData key3; + int rc, i; + GTM_SeqInfo *seq_list[1024]; + + SETUP(); + + key1.gsk_keylen = strlen("seq1"); + key1.gsk_key = strdup("seq1"); + key1.gsk_type = GTM_SEQ_FULL_NAME; + + key2.gsk_keylen = strlen("seq2"); + key2.gsk_key = strdup("seq2"); + key3.gsk_type = GTM_SEQ_FULL_NAME; + key3.gsk_keylen = strlen("seq3"); + key3.gsk_key = strdup("seq3"); + key3.gsk_type = GTM_SEQ_FULL_NAME; + + /* name, increment, min, max, start, cycle */ + rc = open_sequence(conn, &key2, 1, 1, 10000, 5, true); + _ASSERT( rc==0 ); + rc = open_sequence(conn, &key3, 1, 1, 10000, 7, true); + _ASSERT( rc==0 ); + + memset(seq_list, 0, sizeof(GTM_SeqInfo *) * 1024); + + rc = get_sequence_list(conn, seq_list, 1024); + _ASSERT( rc==3 ); + + _ASSERT( strncmp(seq_list[0]->gs_key->gsk_key, key1.gsk_key, 4)==0 ); + _ASSERT( strncmp(seq_list[1]->gs_key->gsk_key, key2.gsk_key, 4)==0 ); + _ASSERT( strncmp(seq_list[2]->gs_key->gsk_key, key3.gsk_key, 4)==0 ); + + { + GTM_Sequence cur; + + cur = get_next(conn, seq_list[0]->gs_key); + fprintf(stderr, "key=%s, cur=%d\n", seq_list[0]->gs_key->gsk_key, cur); + _ASSERT( cur==0 ); + + cur = get_next(conn, seq_list[1]->gs_key); + fprintf(stderr, "key=%s, cur=%d\n", seq_list[1]->gs_key->gsk_key, cur); + _ASSERT( cur==5 ); + + cur = get_next(conn, seq_list[2]->gs_key); + fprintf(stderr, "key=%s, cur=%d\n", seq_list[2]->gs_key->gsk_key, cur); + _ASSERT( cur==7 ); + } + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test_seq_01(); + test_seq_02(); + + test_seq_03(); + test_seq_04(); + + test_seq_05(); /* set_val */ + test_seq_06(); /* reset_sequence */ + + test_seq_07(); /* rename_sequence */ + + test_seq_08(); /* alter_sequence */ + + test_seq_09(); /* get_sequence_list */ + + return 0; +} diff --git a/src/gtm/test/test_seq4.c b/src/gtm/test/test_seq4.c new file mode 100644 index 0000000000..1b0c4b9d7d --- /dev/null +++ b/src/gtm/test/test_seq4.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" +#include "gtm/register.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + connect1(); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_seq4_01() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + GTM_Sequence cur; + + SETUP(); + + /* + * open sequence + */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + increment = 1; + minval = 0; + maxval = 10000; + startval = 0; + cycle = true; + + rc = open_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + cycle); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Opening sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Opening sequence seq1")==1 ); + + /* + * get current + */ + cur = get_current(conn, &seqkey); + _ASSERT( cur==0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 0 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting current value 0 for sequence seq1")==1 ); + + cur = get_current(conn, &seqkey); + _ASSERT( cur==0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 0 for sequence seq1")==2 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting current value 0 for sequence seq1")==2 ); + + /* + * get next + */ + cur = get_next(conn, &seqkey); + _ASSERT( cur==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 1 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 1 for sequence seq1")==1 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==2 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 2 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 2 for sequence seq1")==1 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==3 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 3 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 3 for sequence seq1")==1 ); + + /* + * set value + */ + rc = set_val(conn, &seqkey, 13, true); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Setting new value 13 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Setting new value 13 for sequence seq1")==1 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==14 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 14 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 14 for sequence seq1")==1 ); + +#ifdef NOT_USED /* FIXME: snaga + /* + * FIXME: When `iscalled' is false, set_val() does not affect the sequence value. + */ + rc = set_val(conn, &seqkey, 1000, false); +#endif + + /* + * reset + */ + rc = reset_sequence(conn, &seqkey); + _ASSERT( cur>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Resetting sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Resetting sequence seq1")==1 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 1 for sequence seq1")==2 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 1 for sequence seq1")==2 ); + + /* + * close + */ + rc = close_sequence(conn, &seqkey); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Closing sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Closing sequence seq1")==1 ); + + TEARDOWN(); +} + + +void +test_seq4_02() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_SequenceKeyData newseqkey; + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + SETUP(); + + /* + * create a sequence + */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + increment = 1; + minval = 0; + maxval = 10000; + startval = 0; + cycle = true; + + rc = open_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + cycle); + _ASSERT( rc>=0 ); + + /* create a new key */ + newseqkey.gsk_key = strdup("seqnew"); + newseqkey.gsk_keylen = strlen(newseqkey.gsk_key); + newseqkey.gsk_type = GTM_SEQ_FULL_NAME; + + /* + * rename key + */ + rc = rename_sequence(conn, &seqkey, &newseqkey); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Renaming sequence seq1 to seqnew")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Renaming sequence seq1 to seqnew")==1 ); + + rc = close_sequence(conn, &newseqkey); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Closing sequence seqnew")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Closing sequence seqnew")==1 ); + + TEARDOWN(); +} + + +void +test_seq4_03() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_SequenceKeyData newseqkey; + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + GTM_Sequence cur; + + SETUP(); + + /* + * create a sequence + */ + seqkey.gsk_key = strdup("seq2"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + increment = 1; + minval = 0; + maxval = 10000; + startval = 0; + cycle = true; + + rc = open_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + cycle); + _ASSERT( rc>=0 ); + + { + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + increment = 1; + minval = 100; + maxval = 10000; + startval = 100; + cycle = true; + + rc = alter_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + 256, /* lastval */ + cycle, + true); + + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Altering sequence key seq2")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Altering sequence key seq2")==1 ); + } + + cur = get_current(conn, &seqkey); + _ASSERT( cur==100 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 100 for sequence seq2")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting current value 100 for sequence seq2")==1 ); + + cur = get_next(conn, &seqkey); + _ASSERT( cur==101 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 101 for sequence seq2")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 101 for sequence seq2")==1 ); + + TEARDOWN(); +} + + +int +main(int argc, char *argv[]) +{ + system("./stop.sh"); + system("./clean.sh"); + system("./start.sh"); + + test_seq4_01(); + test_seq4_02(); /* rename */ + test_seq4_03(); /* alter */ + + return 0; +} diff --git a/src/gtm/test/test_seq5.c b/src/gtm/test/test_seq5.c new file mode 100644 index 0000000000..f14f14a23b --- /dev/null +++ b/src/gtm/test/test_seq5.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" +#include "gtm/register.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + connect1(); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_seq5_01() +{ + int rc; + GTM_SequenceKeyData seqkey; + GTM_Sequence increment; + GTM_Sequence minval; + GTM_Sequence maxval; + GTM_Sequence startval; + bool cycle; + + GTM_Sequence cur; + + SETUP(); + + /* + * open sequence + */ + seqkey.gsk_key = strdup("seq1"); + seqkey.gsk_keylen = strlen(seqkey.gsk_key); + seqkey.gsk_type = GTM_SEQ_FULL_NAME; + + increment = 1; + minval = 0; + maxval = 10000; + startval = 0; + cycle = true; + + rc = open_sequence(conn, + &seqkey, + increment, + minval, + maxval, + startval, + cycle); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Opening sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Opening sequence seq1")==1 ); + + /* get current */ + cur = get_current(conn, &seqkey); + _ASSERT( cur==0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 0 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting current value 0 for sequence seq1")==1 ); + + /* get next */ + cur = get_next(conn, &seqkey); + _ASSERT( cur==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 1 for sequence seq1")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 1 for sequence seq1")==1 ); + + system("killall -9 gtm"); + system("./promote.sh"); + sleep(1); + + GTMPQfinish(conn); + connect2(); + + /* get current */ + cur = get_current(conn, &seqkey); + _ASSERT( cur==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting current value 1 for sequence seq1")==1 ); + + /* get next */ + cur = get_next(conn, &seqkey); + _ASSERT( cur==2 ); + _ASSERT( grep_count(LOG_STANDBY, "Getting next value 2 for sequence seq1")==1 ); + + /* + * close + */ + rc = close_sequence(conn, &seqkey); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_STANDBY, "Closing sequence seq1")==1 ); + + TEARDOWN(); +} + + +int +main(int argc, char *argv[]) +{ + system("./stop.sh"); + system("./clean.sh"); + system("./start.sh"); + + test_seq5_01(); + + return 0; +} diff --git a/src/gtm/test/test_serialize.c b/src/gtm/test/test_serialize.c new file mode 100644 index 0000000000..95afef3ac6 --- /dev/null +++ b/src/gtm/test/test_serialize.c @@ -0,0 +1,318 @@ +#include <stdio.h> +#include <string.h> +#include <pthread.h> + +#include "gtm/gtm_c.h" +#include "gtm/elog.h" +#include "gtm/palloc.h" +#include "gtm/gtm.h" +#include "gtm/gtm_txn.h" +#include "gtm/assert.h" +#include "gtm/stringinfo.h" +#include "gtm/register.h" +#include "gtm/libpq.h" +#include "gtm/pqformat.h" +#include "gtm/gtm_msg.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ +} + +void +tearDown() +{ +} + +int +test_snapshotdata_1(void) +{ + GTM_SnapshotData *data, *data2; + char *buf; + int buflen; + + SETUP(); + + /* build a dummy GTM_SnapshotData data. */ + data = (GTM_SnapshotData *)malloc(sizeof(GTM_SnapshotData)); + + data->sn_xmin = 128; + data->sn_xmax = 256; + data->sn_recent_global_xmin = 512; + data->sn_xcnt = 1024; + data->sn_xip = 2048; + + printf("sn_xmin=%d, sn_xmax=%d, sn_recent_global_xmin=%d, sn_xcnt=%d, sn_xip=%d\n", + data->sn_xmin, data->sn_xmax, data->sn_recent_global_xmin, data->sn_xcnt, data->sn_xip); + + /* serialize */ + buflen = sizeof(GTM_SnapshotData); + buf = (char *)malloc(buflen); + gtm_serialize_snapshotdata(data, buf, buflen); + + /* destroy old buf */ + memset(data, 0, sizeof(GTM_SnapshotData)); + free(data); + + /* deserialize */ + data2 = (GTM_SnapshotData *)malloc(sizeof(GTM_SnapshotData)); + gtm_deserialize_snapshotdata(data2, buf, buflen); + printf("sn_xmin=%d, sn_xmax=%d, sn_recent_global_xmin=%d, sn_xcnt=%d, sn_xip=%d\n", + data2->sn_xmin, data2->sn_xmax, data2->sn_recent_global_xmin, data2->sn_xcnt, data2->sn_xip); + + TEARDOWN(); + + return 0; +} + + +GTM_TransactionInfo * +build_dummy_gtm_transactioninfo() +{ + GTM_TransactionInfo *data; + + data = (GTM_TransactionInfo *)malloc( sizeof(GTM_TransactionInfo) ); + + data->gti_handle = 3; + data->gti_backend_id = 13; + data->gti_datanodecount = 0; + data->gti_datanodes = NULL; + data->gti_coordcount = 0; + data->gti_coordinators = NULL; + + data->gti_gid = "hoge"; + + data->gti_current_snapshot.sn_xmin = 128; + data->gti_current_snapshot.sn_xmax = 256; + data->gti_current_snapshot.sn_recent_global_xmin = 512; + data->gti_current_snapshot.sn_xcnt = 1024; + data->gti_current_snapshot.sn_xip = 2048; + + return data; +} + + +int +test_transactioninfo_1(void) +{ + GTM_TransactionInfo *data,*data2; + char *buf; + int buflen; + + PGXC_NodeId datanode[3]; + PGXC_NodeId coordnode[5]; + + SETUP(); + + /* build a dummy GTM_SnapshotData data. */ + data = build_dummy_gtm_transactioninfo(); + data->gti_datanodecount = 3; + data->gti_datanodes = datanode; + data->gti_coordcount = 5; + data->gti_coordinators = coordnode; + + /* serialize */ + buflen = sizeof( GTM_TransactionInfo ); + buf = (char *)malloc(buflen); + gtm_serialize_transactioninfo(data, buf, buflen); + + /* destroy old buf */ + memset(data, 0, sizeof(GTM_TransactionInfo)); + free(data); + + /* deserialize */ + data2 = (GTM_TransactionInfo *)malloc(sizeof(GTM_TransactionInfo)); + gtm_deserialize_transactioninfo(data2, buf, buflen); + + printf("deserialized.\n"); + + _ASSERT(data2->gti_handle==3); + _ASSERT(data2->gti_backend_id==13); + _ASSERT(data2->gti_datanodecount==3); + _ASSERT(data2->gti_coordcount==5); + _ASSERT(data2->gti_current_snapshot.sn_xmin==128); + _ASSERT(data2->gti_current_snapshot.sn_xmax==256); + _ASSERT(data2->gti_current_snapshot.sn_recent_global_xmin==512); + _ASSERT(data2->gti_current_snapshot.sn_xcnt==1024); + _ASSERT(data2->gti_current_snapshot.sn_xip==2048); + + TEARDOWN(); + + return 0; +} + +int +test_transactions_1(void) +{ + GTM_Transactions *data,*data2; + GTM_TransactionInfo *d; + char *buf; + int buflen; + PGXC_NodeId datanode[3]; + PGXC_NodeId coordnode[5]; + + SETUP(); + + data = (GTM_Transactions *)malloc( sizeof(GTM_Transactions) ); + data->gt_lastslot = 13; + + /* build a dummy GTM_TransactionInfo data. */ + d = build_dummy_gtm_transactioninfo(); + d->gti_datanodecount = 3; + d->gti_datanodes = datanode; + d->gti_coordcount = 5; + d->gti_coordinators = coordnode; + + memcpy(&data->gt_transactions_array[0], d, sizeof(data->gt_transactions_array[0])); + + printf("gt_lastslot=%d\n", + data->gt_lastslot); + + /* serialize */ + buflen = gtm_get_transactions_size( data ); + buf = (char *)malloc(buflen); + + if ( !gtm_serialize_transactions(data, buf, buflen) ) + { + printf("error.\n"); + exit(1); + } + + /* destroy old buf */ + memset(data, 0, sizeof(GTM_Transactions)); + free(data); + + /* deserialize */ + data2 = (GTM_Transactions *)malloc(sizeof(GTM_Transactions)); + gtm_deserialize_transactions(data2, buf, buflen); + + printf("deserialized.\n"); + + printf("gt_lastslot=%d\n", + data2->gt_lastslot); + + printf("gti_handle=%d, gti_backend_id=%d\n", + data2->gt_transactions_array[0].gti_handle, + data2->gt_transactions_array[0].gti_backend_id); + + TEARDOWN(); + + return 0; +} + + +void +test_pgxcnodeinfo_1() +{ + GTM_PGXCNodeInfo *data,*data2; + char *buf; + size_t buflen; + + SETUP(); + + data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo) ); + data->type = 2; + data->nodenum = 3; + data->port = 7; + data->ipaddress = "foo"; + data->datafolder = "bar"; + + printf("type=%d, nodenum=%d, port=%d, ipaddress=%s, datafolder=%s\n", + data->type, data->nodenum, data->port, + data->ipaddress, data->datafolder); + + /* serialize */ + buflen = gtm_get_pgxcnodeinfo_size( data ); + buf = (char *)malloc(buflen); + + if ( !gtm_serialize_pgxcnodeinfo(data, buf, buflen) ) + { + printf("error.\n"); + exit(1); + } + + /* destroy old buf */ + memset(data, 0, sizeof(GTM_PGXCNodeInfo)); + free(data); + + /* deserialize */ + data2 = (GTM_PGXCNodeInfo *)malloc(sizeof(GTM_PGXCNodeInfo)); + gtm_deserialize_pgxcnodeinfo(data2, buf, buflen); + + printf("deserialized.\n"); + + printf("type=%d, nodenum=%d, port=%d, ipaddress=%s, datafolder=%s\n", + data2->type, data2->nodenum, data2->port, + data2->ipaddress, data2->datafolder); + + TEARDOWN(); + + return 0; +} + +int +test_seqinfo_1(void) +{ + GTM_SeqInfo *d1, *d2; + char *buf; + int buflen; + GTM_SequenceKeyData gs_key; + + SETUP(); + + /* build a dummy GTM_SnapshotData data. */ + d1 = (GTM_SeqInfo *)malloc(sizeof(GTM_SeqInfo)); + + gs_key.gsk_keylen = 0; + gs_key.gsk_key = ""; + gs_key.gsk_type = GTM_SEQ_DB_NAME; + + d1->gs_key = &gs_key; + d1->gs_value = 3; + d1->gs_init_value = 5; + d1->gs_last_value = 7; + d1->gs_increment_by = 11; + d1->gs_min_value = 13; + d1->gs_max_value = 17; + d1->gs_cycle = true; + d1->gs_called = true; + d1->gs_ref_count = 19; + d1->gs_state = 23; + + /* serialize */ + buflen = gtm_get_sequence_size(d1); + buf = (char *)malloc(buflen); + gtm_serialize_sequence(d1, buf, buflen); + + /* destroy old buf */ + memset(d1, 0, sizeof(GTM_SeqInfo)); + free(d1); + + /* deserialize */ + // d2 = (GTM_SeqInfo *)malloc(sizeof(GTM_SeqInfo)); + d2 = gtm_deserialize_sequence(buf, buflen); + + _ASSERT( d2->gs_key->gsk_keylen==0 ); + _ASSERT( strcmp(d2->gs_key->gsk_key,"")==0 ); + _ASSERT( d2->gs_key->gsk_type==GTM_SEQ_DB_NAME ); + + TEARDOWN(); + + return 0; +} + +int +main(void) +{ + test_snapshotdata_1(); + test_transactioninfo_1(); + test_transactions_1(); + test_pgxcnodeinfo_1(); + test_seqinfo_1(); + + return 0; +} diff --git a/src/gtm/test/test_standby.c b/src/gtm/test/test_standby.c new file mode 100644 index 0000000000..de53255d71 --- /dev/null +++ b/src/gtm/test/test_standby.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + system("./stop.sh > /dev/null"); + system("./clean.sh > /dev/null"); + system("./start.sh > /dev/null"); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_standby_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + system("killall -9 gtm_standby"); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + + _ASSERT( conn!=NULL ); + _ASSERT( grep_count(LOG_ACTIVE, "Failed to establish a connection with GTM standby")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + + TEARDOWN(); +} + +void +test_standby_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + connect1(); + _ASSERT( conn!=NULL ); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 ); + + system("killall -9 gtm_standby"); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby.")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 ); + + TEARDOWN(); +} + +void +test_standby_03() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + connect1(); + _ASSERT( conn!=NULL ); + + system("./status_a.sh > status"); + _ASSERT( grep_count("status", "active: 1")==1 ); + + system("./status_s.sh > status"); + _ASSERT( grep_count("status", "active: 0")==1 ); + + TEARDOWN(); +} + +void +test_standby_04() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + connect1(); + _ASSERT( conn!=NULL ); + + system("./status_a.sh > status"); + _ASSERT( grep_count("status", "active: 1")==1 ); + system("./status_s.sh > status"); + _ASSERT( grep_count("status", "active: 0")==1 ); + + system("killall -9 gtm"); + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + _ASSERT( conn!=NULL ); + + system("./status_s.sh > status"); + _ASSERT( grep_count("status", "active: 1")==1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test_standby_01(); + test_standby_02(); + + test_standby_03(); /* get status */ + + test_standby_04(); /* promote */ + + return 0; +} diff --git a/src/gtm/test/test_startup.c b/src/gtm/test/test_startup.c new file mode 100644 index 0000000000..f6d3906f66 --- /dev/null +++ b/src/gtm/test/test_startup.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +#define client_log(x) printf x + +GlobalTransactionId gxid; + +void +setUp() +{ +} + +void +tearDown() +{ +} + +void +test_startup_01() +{ + GTM_Conn *conn; + char connect_string[100]; + int rc; + + SETUP(); + + system("./start_a.sh"); + sleep(1); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d", + PGXC_NODE_DEFAULT); + + conn = PQconnectGTM(connect_string); + _ASSERT(conn != NULL); + + /* + * create a transaction data on active GTM + */ + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Preparing transaction id 3")==1 ); + + fprintf(stderr, "gxid=%d\n", gxid); + + GTMPQfinish(conn); + + /* + * start standby + */ + system("./start_s.sh"); + sleep(1); + + _ASSERT( grep_count(LOG_STANDBY, "Restoring next/last gxid from the active-GTM succeeded.")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Restoring 1 gxid(s) done.")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Restoring all of gxid(s) from the active-GTM succeeded.")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Restoring sequences done.")==1 ); + + /* + * connecting to the standby + */ + sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=102 remote_type=%d", + PGXC_NODE_DEFAULT); + + conn = PQconnectGTM(connect_string); + _ASSERT(conn != NULL); + + /* + * abort the replicated transaction + */ + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Cancelling transaction id 3")==0 ); + _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 3")==1 ); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + system("./stop.sh"); + system("./clean.sh"); + + test_startup_01(); + + return 0; +} diff --git a/src/gtm/test/test_txn.c b/src/gtm/test/test_txn.c new file mode 100644 index 0000000000..5c73e237ee --- /dev/null +++ b/src/gtm/test/test_txn.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn_03() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction_autovacuum(conn, GTM_ISOLATION_SERIALIZABLE); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn_11() +{ + GTM_IsolationLevel isolevel = GTM_ISOLATION_SERIALIZABLE; + char *gid = "test"; + GlobalTransactionId gxid =InvalidGlobalTransactionId; + GlobalTransactionId prepared_gxid =InvalidGlobalTransactionId; + int datanodecnt = 0; + PGXC_NodeId *datanodes = NULL; + int coordcnt = 0; + PGXC_NodeId *coordinators = NULL; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, isolevel, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + printf("test_txn_11: gxid=%d\n", gxid); + + rc = start_prepared_transaction(conn, gxid, gid, + datanodecnt, datanodes, coordcnt, coordinators); + _ASSERT( rc>=0 ); + + rc = get_gid_data(conn, isolevel, gid, + &gxid, &prepared_gxid, &datanodecnt, datanodes, &coordcnt, coordinators); + printf("test_txn_11: prepared_gxid=%d\n", prepared_gxid); + _ASSERT( rc>=0 ); + + rc = commit_prepared_transaction(conn, gxid, prepared_gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + + +void +test_txn_51() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid == InvalidGlobalTransactionId ); + + TEARDOWN(); +} + +void +test_txn_52() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + rc = prepare_transaction(conn, InvalidGlobalTransactionId); + _ASSERT( rc==-1 ); + + TEARDOWN(); +} + +void +test_txn_53() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + rc = commit_transaction(conn, InvalidGlobalTransactionId); + _ASSERT( rc==-1 ); + + TEARDOWN(); +} + +void +test_txn_54() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + rc = abort_transaction(conn, InvalidGlobalTransactionId); + _ASSERT( rc==-1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + test_txn_01(); + test_txn_02(); + test_txn_03(); + + test_txn_11(); + + /* + * connect to standby. must be prevented. + */ + sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + test_txn_51(); + test_txn_52(); + test_txn_53(); + test_txn_54(); + + /* + * promote GTM standby to become GTM active. + */ + system("./promote.sh"); + sleep(1); + + test_txn_01(); + test_txn_02(); + test_txn_03(); + + test_txn_11(); + + return 0; +} diff --git a/src/gtm/test/test_txn4.c b/src/gtm/test/test_txn4.c new file mode 100644 index 0000000000..e5312cf460 --- /dev/null +++ b/src/gtm/test/test_txn4.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + connect1(); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn4_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==1 ); + + TEARDOWN(); +} + +void +test_txn4_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 4")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 4")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 4")==1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + system("./stop.sh"); + system("./clean.sh"); + system("./start.sh"); + + test_txn4_01(); + test_txn4_02(); + + return 0; +} diff --git a/src/gtm/test/test_txn5.c b/src/gtm/test/test_txn5.c new file mode 100644 index 0000000000..04ca7b3a49 --- /dev/null +++ b/src/gtm/test/test_txn5.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + system("./stop.sh > /dev/null"); + system("./clean.sh > /dev/null"); + system("./start.sh > /dev/null"); + + connect1(); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn5_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_03() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_04() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_05() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_06() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + + +void +test_txn5_11() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + sleep(3); + + system("killall -9 gtm_standby"); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 ); + + TEARDOWN(); +} + +void +test_txn5_12() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + sleep(3); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + system("killall -9 gtm_standby"); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test_txn5_01(); + test_txn5_02(); + test_txn5_03(); + test_txn5_04(); + + test_txn5_05(); + test_txn5_06(); + + /* detecting communication failure, and dropping standby. */ + test_txn5_11(); + test_txn5_12(); + + return 0; +} diff --git a/src/gtm/test2/Makefile b/src/gtm/test2/Makefile new file mode 100644 index 0000000000..e6155ea7f6 --- /dev/null +++ b/src/gtm/test2/Makefile @@ -0,0 +1,53 @@ +# Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + +top_build_dir=../.. +include $(top_build_dir)/gtm/Makefile.global + +override CPPFLAGS := -I$(top_build_dir)/gtm/client $(CPPFLAGS) + +SRCS=test_serialize.c test_connect.c test_node.c test_node5.c test_txn.c test_txn4.c test_txn5.c test_repli.c test_repli2.c test_seq.c test_seq4.c test_seq5.c test_scenario.c test_startup.c test_standby.c test_common.c + +PROGS=test_serialize test_connect test_txn test_txn4 test_txn5 test_repli test_repli2 test_seq test_seq4 test_seq5 test_scenario test_startup test_node test_node5 test_standby + +OBJS=$(SRCS:.c=.o) +LIBS=$(top_build_dir)/gtm/client/libgtmclient.a \ + $(top_build_dir)/gtm/common/libgtm.a \ + $(top_build_dir)/gtm/libpq/libpqcomm.a + +LOADLIBES=-lpthread +CFLAGS=-g -O0 + +all: $(PROGS) + +test_serialize: test_serialize.o test_common.o $(LIBS) + +test_connect: test_connect.o test_common.o $(LIBS) +test_startup: test_startup.o test_common.o $(LIBS) + +test_node: test_node.o test_common.o $(LIBS) +test_node5: test_node5.o test_common.o $(LIBS) + +test_txn: test_txn.o test_common.o $(LIBS) + +test_txn4: test_txn4.o test_common.o $(LIBS) +test_txn5: test_txn5.o test_common.o $(LIBS) + +test_repli: test_repli.o test_common.o $(LIBS) + +test_standby: test_standby.o test_common.o $(LIBS) + +test_repli2: test_repli2.o test_common.o $(LIBS) + +test_seq: test_seq.o test_common.o $(LIBS) +test_seq4: test_seq4.o test_common.o $(LIBS) +test_seq5: test_seq5.o test_common.o $(LIBS) + +test_scenario: test_scenario.o test_common.o $(LIBS) + +clean: + rm -f $(OBJS) *~ + rm -f $(PROGS) + +distclean: clean + +maintainer-clean: distclean diff --git a/src/gtm/test2/regress.log b/src/gtm/test2/regress.log new file mode 100644 index 0000000000..127fe9a893 --- /dev/null +++ b/src/gtm/test2/regress.log @@ -0,0 +1,415 @@ +make: Nothing to be done for `all'. +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +stopping standby... +waiting for server to shut down... done +server stopped +stopping active... +gtm_ctl: could not send stop signal (PID: 24704): No such process +gtm: no process killed +gtm_standby: no process killed +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24750 24744 0 16:50 pts/2 00:00:00 grep gtm +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning standby... +/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning active... +/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting active... +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24763 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +gxid=3 +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting standby... +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24763 1 1 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24775 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +=== test_startup_01 in === +=== test_startup_01 out === +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +stopping standby... +waiting for server to shut down.... done +server stopped +stopping active... +waiting for server to shut down.... done +server stopped +gtm: no process killed +gtm_standby: no process killed +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning standby... +/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning active... +/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting active... +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24808 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24810 24802 0 16:50 pts/2 00:00:00 grep gtm +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +sleeping 3 seconds... +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting standby... +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24808 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24818 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +stopping standby... +waiting for server to shut down.... done +server stopped +stopping active... +waiting for server to shut down.... done +server stopped +gtm: no process killed +gtm_standby: no process killed +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning standby... +/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning active... +/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting active... +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24846 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24848 24840 0 16:50 pts/2 00:00:00 grep gtm +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +sleeping 3 seconds... +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting standby... +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24846 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24856 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +=== test_node5_01 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_node5_01 out === +=== test_node5_02 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_node5_02 out === +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +stopping standby... +waiting for server to shut down... done +server stopped +stopping active... +waiting for server to shut down... done +server stopped +gtm: no process killed +gtm_standby: no process killed +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24870 24864 0 16:50 pts/2 00:00:00 grep gtm +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning standby... +/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning active... +/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting active... +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24885 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24887 24879 0 16:50 pts/2 00:00:00 grep gtm +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +sleeping 3 seconds... +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting standby... +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24885 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24895 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +=== test_txn4_01 in === +PGconnectGTM() ok. +=== test_txn4_01 out === +=== test_txn4_02 in === +PGconnectGTM() ok. +=== test_txn4_02 out === +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +stopping standby... +waiting for server to shut down.... done +server stopped +stopping active... +waiting for server to shut down.... done +server stopped +gtm: no process killed +gtm_standby: no process killed +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24917 24910 0 16:50 pts/2 00:00:00 grep gtm +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning standby... +/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +cleaning active... +/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting active... +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24932 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24934 24926 0 16:50 pts/2 00:00:00 grep gtm +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +sleeping 3 seconds... +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +starting standby... +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +server starting +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 24932 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 24942 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +=== test_seq4_01 in === +PGconnectGTM() ok. +=== test_seq4_01 out === +=== test_seq4_02 in === +PGconnectGTM() ok. +=== test_seq4_02 out === +=== test_seq4_03 in === +PGconnectGTM() ok. +=== test_seq4_03 out === +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +gtm_ctl: could not send stop signal (PID: 25019): No such process +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +gtm_ctl: could not send stop signal (PID: 25061): No such process +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +promoting standby... +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +snaga 25158 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +=== test_standby_01 in === +=== test_standby_01 out === +=== test_standby_02 in === +PGconnectGTM() ok. +=== test_standby_02 out === +=== test_standby_03 in === +PGconnectGTM() ok. +=== test_standby_03 out === +=== test_standby_04 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_standby_04 out === +gtm_ctl: could not send stop signal (PID: 25147): No such process +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +promoting standby... +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +snaga 25208 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 25218 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25228 25224 0 16:51 pts/2 00:00:00 grep gtm +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +promoting standby... +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +snaga 25254 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 25264 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25274 25270 0 16:51 pts/2 00:00:00 grep gtm +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +promoting standby... +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +snaga 25300 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 25310 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25320 25316 0 16:51 pts/2 00:00:00 grep gtm +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +promoting standby... +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +snaga 25346 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 25356 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25366 25362 0 16:51 pts/2 00:00:00 grep gtm +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +promoting standby... +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +snaga 25392 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 25402 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25412 25408 0 16:51 pts/2 00:00:00 grep gtm +gtm: no process killed +gtm_standby: no process killed +gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist +Is server running? +gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist +Is server running? +/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test +~/pgxc/postgres-xc/src/gtm/test +promoting standby... +checking process... +snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log +snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log +snaga 25438 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101 +snaga 25448 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666 +snaga 25458 25454 0 16:51 pts/2 00:00:00 grep gtm +=== test_txn5_01 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_txn5_01 out === +=== test_txn5_02 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_txn5_02 out === +=== test_txn5_03 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_txn5_03 out === +=== test_txn5_04 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_txn5_04 out === +=== test_txn5_05 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_txn5_05 out === +=== test_txn5_06 in === +PGconnectGTM() ok. +PGconnectGTM() ok. +=== test_txn5_06 out === diff --git a/src/gtm/test2/regress2.sh b/src/gtm/test2/regress2.sh new file mode 100644 index 0000000000..835055ac7e --- /dev/null +++ b/src/gtm/test2/regress2.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +make + +./test_startup +./test_node5 +./test_txn4 +./test_seq4 + +./test_standby + +./test_txn5 + diff --git a/src/gtm/test2/run.sh b/src/gtm/test2/run.sh new file mode 100644 index 0000000000..98566eb6fb --- /dev/null +++ b/src/gtm/test2/run.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +. ./regress2.sh 2>&1 | tee regress.log + +echo "" +echo "=========== SUMMARY ============" +date +echo -n "Assert: " +grep -c ASSERT regress.log diff --git a/src/gtm/test2/status b/src/gtm/test2/status new file mode 100644 index 0000000000..ef55611eaa --- /dev/null +++ b/src/gtm/test2/status @@ -0,0 +1,3 @@ +pid: 19879 +data: /tmp/pgxc/data/gtm_standby +active: 1 diff --git a/src/gtm/test2/status_a.sh b/src/gtm/test2/status_a.sh new file mode 100644 index 0000000000..d120fd0b7a --- /dev/null +++ b/src/gtm/test2/status_a.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# GTM start script for test + +export PATH=/tmp/pgxc/bin:$PATH +export DATA=/tmp/pgxc/data/gtm + +# ------------------------------- +# starting active... +# ------------------------------- +gtm_ctl -D ${DATA} -S gtm status + diff --git a/src/gtm/test2/status_s.sh b/src/gtm/test2/status_s.sh new file mode 100644 index 0000000000..badcf1590b --- /dev/null +++ b/src/gtm/test2/status_s.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# GTM start script for test + +export PATH=/tmp/pgxc/bin:$PATH +export DATA=/tmp/pgxc/data/gtm_standby + +# ------------------------------- +# starting active... +# ------------------------------- +gtm_ctl -D ${DATA} -S gtm_standby status + diff --git a/src/gtm/test2/test.c b/src/gtm/test2/test.c new file mode 100644 index 0000000000..f7d6f945c1 --- /dev/null +++ b/src/gtm/test2/test.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +int +main(void) +{ + char *ptr[1024]; + int i; + + for (i=0 ; i<1024 ; i++) + { + fprintf(stderr, "ptr[%d] = %p\n", i, ptr[i]); + } + + return 0; +} diff --git a/src/gtm/test2/test_connect2.c b/src/gtm/test2/test_connect2.c new file mode 100644 index 0000000000..9b53d332a9 --- /dev/null +++ b/src/gtm/test2/test_connect2.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#define client_log(x) printf x + +void +setUp() +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + + +void +test11() +{ + clean_active(); + clean_standby(); + + start_active(); + sleep(10); + start_standby(); + sleep(10); + // stop_active(); + kill_standby(); + sleep(10); + kill_active(); +} + +int +main(int argc, char *argv[]) +{ + test11(); + + return 0; +} + diff --git a/src/gtm/test2/test_standby.c b/src/gtm/test2/test_standby.c new file mode 100644 index 0000000000..de53255d71 --- /dev/null +++ b/src/gtm/test2/test_standby.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + system("./stop.sh > /dev/null"); + system("./clean.sh > /dev/null"); + system("./start.sh > /dev/null"); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_standby_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + system("killall -9 gtm_standby"); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + + _ASSERT( conn!=NULL ); + _ASSERT( grep_count(LOG_ACTIVE, "Failed to establish a connection with GTM standby")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + + TEARDOWN(); +} + +void +test_standby_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + connect1(); + _ASSERT( conn!=NULL ); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 ); + + system("killall -9 gtm_standby"); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby.")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 ); + + TEARDOWN(); +} + +void +test_standby_03() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + connect1(); + _ASSERT( conn!=NULL ); + + system("./status_a.sh > status"); + _ASSERT( grep_count("status", "active: 1")==1 ); + + system("./status_s.sh > status"); + _ASSERT( grep_count("status", "active: 0")==1 ); + + TEARDOWN(); +} + +void +test_standby_04() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + connect1(); + _ASSERT( conn!=NULL ); + + system("./status_a.sh > status"); + _ASSERT( grep_count("status", "active: 1")==1 ); + system("./status_s.sh > status"); + _ASSERT( grep_count("status", "active: 0")==1 ); + + system("killall -9 gtm"); + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + _ASSERT( conn!=NULL ); + + system("./status_s.sh > status"); + _ASSERT( grep_count("status", "active: 1")==1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test_standby_01(); + test_standby_02(); + + test_standby_03(); /* get status */ + + test_standby_04(); /* promote */ + + return 0; +} diff --git a/src/gtm/test2/test_startup.c b/src/gtm/test2/test_startup.c new file mode 100644 index 0000000000..f6d3906f66 --- /dev/null +++ b/src/gtm/test2/test_startup.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +#define client_log(x) printf x + +GlobalTransactionId gxid; + +void +setUp() +{ +} + +void +tearDown() +{ +} + +void +test_startup_01() +{ + GTM_Conn *conn; + char connect_string[100]; + int rc; + + SETUP(); + + system("./start_a.sh"); + sleep(1); + + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d", + PGXC_NODE_DEFAULT); + + conn = PQconnectGTM(connect_string); + _ASSERT(conn != NULL); + + /* + * create a transaction data on active GTM + */ + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Preparing transaction id 3")==1 ); + + fprintf(stderr, "gxid=%d\n", gxid); + + GTMPQfinish(conn); + + /* + * start standby + */ + system("./start_s.sh"); + sleep(1); + + _ASSERT( grep_count(LOG_STANDBY, "Restoring next/last gxid from the active-GTM succeeded.")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Restoring 1 gxid(s) done.")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Restoring all of gxid(s) from the active-GTM succeeded.")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Restoring sequences done.")==1 ); + + /* + * connecting to the standby + */ + sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=102 remote_type=%d", + PGXC_NODE_DEFAULT); + + conn = PQconnectGTM(connect_string); + _ASSERT(conn != NULL); + + /* + * abort the replicated transaction + */ + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Cancelling transaction id 3")==0 ); + _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 3")==1 ); + + GTMPQfinish(conn); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + system("./stop.sh"); + system("./clean.sh"); + + test_startup_01(); + + return 0; +} diff --git a/src/gtm/test2/test_txn.c b/src/gtm/test2/test_txn.c new file mode 100644 index 0000000000..5c73e237ee --- /dev/null +++ b/src/gtm/test2/test_txn.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn_03() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction_autovacuum(conn, GTM_ISOLATION_SERIALIZABLE); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn_11() +{ + GTM_IsolationLevel isolevel = GTM_ISOLATION_SERIALIZABLE; + char *gid = "test"; + GlobalTransactionId gxid =InvalidGlobalTransactionId; + GlobalTransactionId prepared_gxid =InvalidGlobalTransactionId; + int datanodecnt = 0; + PGXC_NodeId *datanodes = NULL; + int coordcnt = 0; + PGXC_NodeId *coordinators = NULL; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, isolevel, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + printf("test_txn_11: gxid=%d\n", gxid); + + rc = start_prepared_transaction(conn, gxid, gid, + datanodecnt, datanodes, coordcnt, coordinators); + _ASSERT( rc>=0 ); + + rc = get_gid_data(conn, isolevel, gid, + &gxid, &prepared_gxid, &datanodecnt, datanodes, &coordcnt, coordinators); + printf("test_txn_11: prepared_gxid=%d\n", prepared_gxid); + _ASSERT( rc>=0 ); + + rc = commit_prepared_transaction(conn, gxid, prepared_gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + + +void +test_txn_51() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid == InvalidGlobalTransactionId ); + + TEARDOWN(); +} + +void +test_txn_52() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + rc = prepare_transaction(conn, InvalidGlobalTransactionId); + _ASSERT( rc==-1 ); + + TEARDOWN(); +} + +void +test_txn_53() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + rc = commit_transaction(conn, InvalidGlobalTransactionId); + _ASSERT( rc==-1 ); + + TEARDOWN(); +} + +void +test_txn_54() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + rc = abort_transaction(conn, InvalidGlobalTransactionId); + _ASSERT( rc==-1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + test_txn_01(); + test_txn_02(); + test_txn_03(); + + test_txn_11(); + + /* + * connect to standby. must be prevented. + */ + sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + test_txn_51(); + test_txn_52(); + test_txn_53(); + test_txn_54(); + + /* + * promote GTM standby to become GTM active. + */ + system("./promote.sh"); + sleep(1); + + test_txn_01(); + test_txn_02(); + test_txn_03(); + + test_txn_11(); + + return 0; +} diff --git a/src/gtm/test2/test_txn2.c b/src/gtm/test2/test_txn2.c new file mode 100644 index 0000000000..803611ea7d --- /dev/null +++ b/src/gtm/test2/test_txn2.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn_21() +{ + /* request parameters */ + int txn_count = 2; + GTM_IsolationLevel txn_isolation_level[GTM_MAX_GLOBAL_TRANSACTIONS]; + bool txn_read_only[GTM_MAX_GLOBAL_TRANSACTIONS]; + GTMProxy_ConnID txn_connid[GTM_MAX_GLOBAL_TRANSACTIONS]; + + /* results */ + int txn_count2; + GlobalTransactionId start_gxid; + GTM_Timestamp start_ts; + + /* request parameters */ + GlobalTransactionId gxid[GTM_MAX_GLOBAL_TRANSACTIONS]; + + /* results */ + int status[GTM_MAX_GLOBAL_TRANSACTIONS]; + + int rc, i; + + for (i=0 ; i<txn_count ; i++) + { + txn_isolation_level[i] = GTM_ISOLATION_SERIALIZABLE; + txn_read_only[i] = false; + // txn_connid[i] = InvalidGTMProxyConnID; + txn_connid[i] = 0; + } + + SETUP(); + + rc = begin_transaction_multi(conn, + txn_count, txn_isolation_level, txn_read_only, txn_connid, + &txn_count2, &start_gxid, &start_ts); + _ASSERT( rc>=0 ); + _ASSERT( txn_count2==2 ); + + /* + * commit test + */ + txn_count = txn_count2; + + for (i=0 ; i<txn_count ; i++) + { + gxid[i] = start_gxid + i; + } + + rc = commit_transaction_multi(conn, txn_count, gxid, &txn_count2, &status); + _ASSERT( rc>=0 ); + _ASSERT( txn_count2==2 ); + + tearDown(); +} + +void +test_txn_22() +{ + /* request parameters */ + int txn_count = 2; + GTM_IsolationLevel txn_isolation_level[GTM_MAX_GLOBAL_TRANSACTIONS]; + bool txn_read_only[GTM_MAX_GLOBAL_TRANSACTIONS]; + GTMProxy_ConnID txn_connid[GTM_MAX_GLOBAL_TRANSACTIONS]; + + /* results */ + int txn_count2; + GlobalTransactionId start_gxid; + GTM_Timestamp start_ts; + + /* request parameters */ + GlobalTransactionId gxid[GTM_MAX_GLOBAL_TRANSACTIONS]; + + /* results */ + int status[GTM_MAX_GLOBAL_TRANSACTIONS]; + + int rc, i; + + for (i=0 ; i<txn_count ; i++) + { + txn_isolation_level[i] = GTM_ISOLATION_SERIALIZABLE; + txn_read_only[i] = false; + // txn_connid[i] = InvalidGTMProxyConnID; + txn_connid[i] = 0; + } + + SETUP(); + + rc = begin_transaction_multi(conn, + txn_count, txn_isolation_level, txn_read_only, txn_connid, + &txn_count2, &start_gxid, &start_ts); + _ASSERT( rc>=0 ); + _ASSERT( txn_count2==2 ); + + /* + * abort test + */ + txn_count = txn_count2; + + for (i=0 ; i<txn_count ; i++) + { + gxid[i] = start_gxid + i; + } + + rc = abort_transaction_multi(conn, txn_count, gxid, &txn_count2, &status); + _ASSERT( rc>=0 ); + _ASSERT( txn_count2==2 ); + + tearDown(); +} + +void +test_txn_23() +{ + /* request parameters */ + int txn_count = 2; + GTM_IsolationLevel txn_isolation_level[GTM_MAX_GLOBAL_TRANSACTIONS]; + bool txn_read_only[GTM_MAX_GLOBAL_TRANSACTIONS]; + GTMProxy_ConnID txn_connid[GTM_MAX_GLOBAL_TRANSACTIONS]; + + /* results */ + int txn_count2; + GlobalTransactionId start_gxid; + GTM_Timestamp start_ts; + + /* request parameters */ + GlobalTransactionId gxid[GTM_MAX_GLOBAL_TRANSACTIONS]; + + /* results */ + int status[GTM_MAX_GLOBAL_TRANSACTIONS]; + GlobalTransactionId xmin_out; + GlobalTransactionId xmax_out; + GlobalTransactionId recent_global_xmin_out; + int32 xcnt_out; + + int rc, i; + + for (i=0 ; i<txn_count ; i++) + { + txn_isolation_level[i] = GTM_ISOLATION_SERIALIZABLE; + txn_read_only[i] = false; + // txn_connid[i] = InvalidGTMProxyConnID; + txn_connid[i] = 0; + } + + SETUP(); + + rc = begin_transaction_multi(conn, + txn_count, txn_isolation_level, txn_read_only, txn_connid, + &txn_count2, &start_gxid, &start_ts); + _ASSERT( rc>=0 ); + _ASSERT( txn_count2==2 ); + + /* + * snapshot get test + */ + txn_count = txn_count2; + + for (i=0 ; i<txn_count ; i++) + { + gxid[i] = start_gxid + i; + } + + rc = snapshot_get_multi(conn, txn_count, gxid, + &txn_count2, &status, + &xmin_out, &xmax_out, &recent_global_xmin_out, &xcnt_out); + _ASSERT( rc>=0 ); + _ASSERT( txn_count2==2 ); + + tearDown(); +} + + +void +test_txn_31() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + sleep(10); /* kill standby while sleeping. */ + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + tearDown(); +} + + +int +main(int argc, char *argv[]) +{ + test_txn2_01(); + + return 0; +} diff --git a/src/gtm/test2/test_txn3.c b/src/gtm/test2/test_txn3.c new file mode 100644 index 0000000000..a711dcae63 --- /dev/null +++ b/src/gtm/test2/test_txn3.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d", + PGXC_NODE_GTM); + + conn = PQconnectGTM(connect_string); + if (conn == NULL) + { + client_log(("Error in connection\n")); + exit(1); + } + client_log(("PGconnectGTM() ok.\n")); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn3_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + sleep(2); + system("killall -9 gtm_standby"); + sleep(1); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + tearDown(); +} + + +int +main(int argc, char *argv[]) +{ + test_txn2_01(); + + return 0; +} diff --git a/src/gtm/test2/test_txn4.c b/src/gtm/test2/test_txn4.c new file mode 100644 index 0000000000..e5312cf460 --- /dev/null +++ b/src/gtm/test2/test_txn4.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + connect1(); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn4_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==1 ); + + TEARDOWN(); +} + +void +test_txn4_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 4")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 4")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 4")==1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + system("./stop.sh"); + system("./clean.sh"); + system("./start.sh"); + + test_txn4_01(); + test_txn4_02(); + + return 0; +} diff --git a/src/gtm/test2/test_txn5.c b/src/gtm/test2/test_txn5.c new file mode 100644 index 0000000000..04ca7b3a49 --- /dev/null +++ b/src/gtm/test2/test_txn5.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "gtm/libpq-fe.h" +#include "gtm/gtm_c.h" +#include "gtm/gtm_client.h" + +#include "test_common.h" + +pthread_key_t threadinfo_key; + +void +setUp() +{ + system("./stop.sh > /dev/null"); + system("./clean.sh > /dev/null"); + system("./start.sh > /dev/null"); + + connect1(); +} + +void +tearDown() +{ + GTMPQfinish(conn); +} + +void +test_txn5_01() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_02() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_03() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_04() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_05() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + +void +test_txn5_06() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + rc = prepare_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + system("./promote.sh"); + + GTMPQfinish(conn); + connect2(); + + rc = abort_transaction(conn, gxid); + _ASSERT( rc>=0 ); + + TEARDOWN(); +} + + +void +test_txn5_11() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + sleep(3); + + system("killall -9 gtm_standby"); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 ); + _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 ); + + TEARDOWN(); +} + +void +test_txn5_12() +{ + GlobalTransactionId gxid; + int rc; + + SETUP(); + sleep(3); + + gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp); + _ASSERT( gxid != InvalidGlobalTransactionId ); + + system("killall -9 gtm_standby"); + + rc = commit_transaction(conn, gxid); + _ASSERT( rc>=0 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 ); + _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 ); + + TEARDOWN(); +} + +int +main(int argc, char *argv[]) +{ + test_txn5_01(); + test_txn5_02(); + test_txn5_03(); + test_txn5_04(); + + test_txn5_05(); + test_txn5_06(); + + /* detecting communication failure, and dropping standby. */ + test_txn5_11(); + test_txn5_12(); + + return 0; +} diff --git a/src/include/gen_alloc.h b/src/include/gen_alloc.h new file mode 100644 index 0000000000..1229ba524d --- /dev/null +++ b/src/include/gen_alloc.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * gen_alloc.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gen_alloc.h + * + *------------------------------------------------------------------------- + */ +#ifndef GEN_ALLOC_H +#define GEN_ALLOC_H + +/* + * Common memory allocation binary interface both for Postgres and GTM processes. + * Especially needed by gtm_serialize.c and gtm_serialize_debug.c + */ + +typedef struct Gen_Alloc +{ + void * (* alloc) (void *, size_t); + void * (* alloc0) (void *, size_t); + void * (* realloc) (void *, size_t); + void (* free) (void *); + void * (* current_memcontext) (void); +} Gen_Alloc; + +extern Gen_Alloc genAlloc_class; + +#define genAlloc(x) genAlloc_class.alloc(genAlloc_class.current_memcontext(), x) +#define genRealloc(x, y) genAlloc_class.realloc(x, y); +#define genFree(x) genAlloc_class.free(x); +#define genAlloc0(x) genAlloc_class.alloc0(genAlloc_class.current_memcontext(), x) + +#endif /* GEN_ALLOC_H */ diff --git a/src/include/gtm/assert.h b/src/include/gtm/assert.h index b46df46a5e..97cfc2dfb2 100644 --- a/src/include/gtm/assert.h +++ b/src/include/gtm/assert.h @@ -14,6 +14,8 @@ #ifndef GTM_ASSERT_H #define GTM_ASSERT_H +#include "c.h" + extern bool assert_enabled; /* diff --git a/src/include/gtm/elog.h b/src/include/gtm/elog.h index 115ea1fd9b..e9b7391502 100644 --- a/src/include/gtm/elog.h +++ b/src/include/gtm/elog.h @@ -15,6 +15,8 @@ #ifndef ELOG_H #define ELOG_H +#include "c.h" + /* Error level codes */ #define DEBUG5 10 /* Debugging messages, in categories of * decreasing detail. */ diff --git a/src/include/gtm/gtm.h b/src/include/gtm/gtm.h index 91eff992fa..ad5882bd1b 100644 --- a/src/include/gtm/gtm.h +++ b/src/include/gtm/gtm.h @@ -64,7 +64,7 @@ typedef struct GTM_ThreadInfo GTM_ConnectionInfo *thr_conn; GTM_RWLock thr_lock; - List *thr_cached_txninfo; + gtm_List *thr_cached_txninfo; } GTM_ThreadInfo; @@ -117,10 +117,10 @@ extern GTM_ThreadID TopMostThreadID; #define IsMainThread() (GetMyThreadInfo->thr_id == TopMostThreadID) #define GTM_CachedTransInfo (GetMyThreadInfo->thr_cached_txninfo) -#define GTM_HaveFreeCachedTransInfo() (list_length(GTM_CachedTransInfo)) +#define GTM_HaveFreeCachedTransInfo() (gtm_list_length(GTM_CachedTransInfo)) #define GTM_MAX_CACHED_TRANSINFO 0 -#define GTM_HaveEnoughCachedTransInfo() (list_length(GTM_CachedTransInfo) >= GTM_MAX_CACHED_TRANSINFO) +#define GTM_HaveEnoughCachedTransInfo() (gtm_list_length(GTM_CachedTransInfo) >= GTM_MAX_CACHED_TRANSINFO) #define START_CRIT_SECTION() (CritSectionCount++) diff --git a/src/include/gtm/gtm_c.h b/src/include/gtm/gtm_c.h index e243317a47..4f050f6d4b 100644 --- a/src/include/gtm/gtm_c.h +++ b/src/include/gtm/gtm_c.h @@ -1,6 +1,6 @@ /*------------------------------------------------------------------------- * - * c.h + * gtm_c.h * Fundamental C definitions. This is included by every .c file in * PostgreSQL (via either postgres.h or postgres_fe.h, as appropriate). * @@ -53,6 +53,7 @@ typedef enum GTM_PGXCNodeType PGXC_NODE_GTM_PROXY_POSTMASTER, /* Used by Proxy to communicate with GTM and not use Proxy headers */ PGXC_NODE_COORDINATOR, PGXC_NODE_DATANODE, + PGXC_NODE_GTM, PGXC_NODE_DEFAULT /* In case nothing is associated to connection */ } GTM_PGXCNodeType; diff --git a/src/include/gtm/gtm_client.h b/src/include/gtm/gtm_client.h index 0159212367..ca1d92edb5 100644 --- a/src/include/gtm/gtm_client.h +++ b/src/include/gtm/gtm_client.h @@ -15,7 +15,10 @@ #define GTM_CLIENT_H #include "gtm/gtm_c.h" +#include "gtm/gtm_seq.h" +#include "gtm/gtm_txn.h" #include "gtm/gtm_msg.h" +#include "gtm/register.h" #include "gtm/libpq-fe.h" typedef union GTM_ResultData @@ -35,6 +38,8 @@ typedef union GTM_ResultData * TXN_ROLLBACK */ + GlobalTransactionId grd_next_gxid; + struct { GTM_TransactionHandle txnhandle; @@ -50,6 +55,11 @@ typedef union GTM_ResultData GTM_Sequence seqval; } grd_seq; /* SEQUENCE_GET_CURRENT SEQUENCE_GET_NEXT */ + struct + { + int seq_count; + GTM_SeqInfo **seq; + } grd_seq_list; /* SEQUENCE_GET_LIST */ struct { @@ -84,10 +94,22 @@ typedef union GTM_ResultData struct { + char *ptr; + size_t len; + } grd_txn_gid_list; /* TXN_GXID_LIST_RESULT */ + + struct + { GTM_PGXCNodeType type; /* NODE_REGISTER */ GTM_PGXCNodeId nodenum; /* NODE_UNREGISTER */ } grd_node; + struct + { + int num_node; + GTM_PGXCNodeInfo *nodeinfo[MAX_NODES]; + } grd_node_list; + /* * TODO * TXN_GET_STATUS @@ -95,6 +117,16 @@ typedef union GTM_ResultData */ } GTM_ResultData; +#define GTM_RESULT_COMM_ERROR (-2) /* Communication error */ +#define GTM_RESULT_ERROR (-1) +#define GTM_RESULT_OK (0) +/* + * This error is used ion the case where allocated buffer is not large + * enough to store the errors. It may happen of an allocation failed + * so it's status is considered as unknown. + */ +#define GTM_RESULT_UNKNOWN (1) + typedef struct GTM_Result { GTM_ResultType gr_type; @@ -123,6 +155,14 @@ typedef struct GTM_Result GTM_Conn *connect_gtm(const char *connect_string); void disconnect_gtm(GTM_Conn *conn); +int begin_replication_initial_sync(GTM_Conn *); +int end_replication_initial_sync(GTM_Conn *); + +size_t get_node_list(GTM_Conn *, GTM_PGXCNodeInfo *, size_t); +GlobalTransactionId get_next_gxid(GTM_Conn *); +uint32 get_txn_gxid_list(GTM_Conn *, GTM_Transactions *); +size_t get_sequence_list(GTM_Conn *, GTM_SeqInfo **, size_t); + /* * Transaction Management API */ @@ -141,6 +181,25 @@ int get_gid_data(GTM_Conn *conn, GTM_IsolationLevel isolevel, char *gid, PGXC_NodeId **coordinators); /* + * Multiple Transaction Management API + */ +int +begin_transaction_multi(GTM_Conn *conn, int txn_count, GTM_IsolationLevel *txn_isolation_level, + bool *txn_read_only, GTMProxy_ConnID *txn_connid, + int *txn_count_out, GlobalTransactionId *gxid_out, GTM_Timestamp *ts_out); +int +commit_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid, + int *txn_count_out, int *status_out); +int +abort_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid, + int *txn_count_out, int *status_out); +int +snapshot_get_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid, + int *txn_count_out, int *status_out, + GlobalTransactionId *xmin_out, GlobalTransactionId *xmax_out, + GlobalTransactionId *recent_global_xmin_out, int32 *xcnt_out); + +/* * Snapshot Management API */ GTM_SnapshotData *get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid, @@ -151,7 +210,12 @@ GTM_SnapshotData *get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid, */ int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum, GTM_PGXCNodePort port, char *datafolder); +int node_register_internal(GTM_Conn *conn, GTM_PGXCNodeType type, const char *host, + GTM_PGXCNodePort port, GTM_PGXCNodeId nodenum, char *datafolder, + GTM_PGXCNodeStatus status); int node_unregister(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum); +int backend_disconnect(GTM_Conn *conn, bool is_postmaster, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum); +char *node_get_local_addr(GTM_Conn *conn, char *buf, size_t buflen, int *rc); /* * Sequence Management API @@ -169,5 +233,4 @@ GTM_Sequence get_next(GTM_Conn *conn, GTM_SequenceKey key); int set_val(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence nextval, bool is_called); int reset_sequence(GTM_Conn *conn, GTM_SequenceKey key); - #endif diff --git a/src/include/gtm/gtm_conn.h b/src/include/gtm/gtm_conn.h index c000497a27..40256be5fe 100644 --- a/src/include/gtm/gtm_conn.h +++ b/src/include/gtm/gtm_conn.h @@ -15,6 +15,8 @@ #define GTM_CONN_H #include "gtm/libpq-be.h" +#include "gtm/libpq-fe.h" +#include "gtm/libpq-int.h" struct GTM_ThreadInfo; @@ -24,6 +26,9 @@ typedef struct GTM_ConnectionInfo Port *con_port; struct GTM_ThreadInfo *con_thrinfo; bool con_authenticated; + + /* a connection object to the standby */ + GTM_Conn *standby; } GTM_ConnectionInfo; typedef struct GTM_Connections diff --git a/src/include/gtm/gtm_ip.h b/src/include/gtm/gtm_ip.h index 2f89418af6..bc64ba0aa0 100644 --- a/src/include/gtm/gtm_ip.h +++ b/src/include/gtm/gtm_ip.h @@ -1,6 +1,6 @@ /*------------------------------------------------------------------------- * - * ip.h + * gtm_ip.h * Definitions for IPv6-aware network access. * * These definitions are used by both frontend and backend code. Be careful diff --git a/src/include/gtm/gtm_list.h b/src/include/gtm/gtm_list.h index c5dbd93766..6a7e5ab73f 100644 --- a/src/include/gtm/gtm_list.h +++ b/src/include/gtm/gtm_list.h @@ -1,6 +1,6 @@ /*------------------------------------------------------------------------- * - * pg_list.h + * gtm_list.h * interface for PostgreSQL generic linked list package * * This package implements singly-linked homogeneous lists. @@ -39,31 +39,31 @@ #define GTM_LIST_H -typedef struct ListCell ListCell; +typedef struct gtm_ListCell gtm_ListCell; -typedef struct List +typedef struct gtm_List { int length; - ListCell *head; - ListCell *tail; -} List; + gtm_ListCell *head; + gtm_ListCell *tail; +} gtm_List; -struct ListCell +struct gtm_ListCell { union { void *ptr_value; int int_value; } data; - ListCell *next; + gtm_ListCell *next; }; /* - * The *only* valid representation of an empty list is NIL; in other - * words, a non-NIL list is guaranteed to have length >= 1 and + * The *only* valid representation of an empty list is gtm_NIL; in other + * words, a non-gtm_NIL list is guaranteed to have length >= 1 and * head/tail != NULL */ -#define NIL ((List *) NULL) +#define gtm_NIL ((gtm_List *) NULL) /* * These routines are used frequently. However, we can't implement @@ -73,150 +73,150 @@ struct ListCell */ #ifdef __GNUC__ -static __inline__ ListCell * -list_head(List *l) +static __inline__ gtm_ListCell * +gtm_list_head(gtm_List *l) { return l ? l->head : NULL; } -static __inline__ ListCell * -list_tail(List *l) +static __inline__ gtm_ListCell * +gtm_list_tail(gtm_List *l) { return l ? l->tail : NULL; } static __inline__ int -list_length(List *l) +gtm_list_length(gtm_List *l) { return l ? l->length : 0; } #else -extern ListCell *list_head(List *l); -extern ListCell *list_tail(List *l); -extern int list_length(List *l); +extern gtm_ListCell *gtm_list_head(gtm_List *l); +extern gtm_ListCell *gtm_list_tail(gtm_List *l); +extern int gtm_list_length(gtm_List *l); #endif /* __GNUC__ */ /* * NB: There is an unfortunate legacy from a previous incarnation of - * the List API: the macro lfirst() was used to mean "the data in this - * cons cell". To avoid changing every usage of lfirst(), that meaning - * has been kept. As a result, lfirst() takes a ListCell and returns + * the gtm_List API: the macro gtm_lfirst() was used to mean "the data in this + * cons cell". To avoid changing every usage of gtm_lfirst(), that meaning + * has been kept. As a result, gtm_lfirst() takes a gtm_ListCell and returns * the data it contains; to get the data in the first cell of a - * List, use linitial(). Worse, lsecond() is more closely related to - * linitial() than lfirst(): given a List, lsecond() returns the data + * gtm_List, use gtm_linitial(). Worse, gtm_lsecond() is more closely related to + * gtm_linitial() than gtm_lfirst(): given a gtm_List, gtm_lsecond() returns the data * in the second cons cell. */ -#define lnext(lc) ((lc)->next) -#define lfirst(lc) ((lc)->data.ptr_value) -#define lfirst_int(lc) ((lc)->data.int_value) +#define gtm_lnext(lc) ((lc)->next) +#define gtm_lfirst(lc) ((lc)->data.ptr_value) +#define gtm_lfirst_int(lc) ((lc)->data.int_value) -#define linitial(l) lfirst(list_head(l)) -#define linitial_int(l) lfirst_int(list_head(l)) +#define gtm_linitial(l) gtm_lfirst(gtm_list_head(l)) +#define gtm_linitial_int(l) gtm_lfirst_int(gtm_list_head(l)) -#define lsecond(l) lfirst(lnext(list_head(l))) -#define lsecond_int(l) lfirst_int(lnext(list_head(l))) +#define gtm_lsecond(l) gtm_lfirst(gtm_lnext(gtm_list_head(l))) +#define gtm_lsecond_int(l) gtm_lfirst_int(gtm_lnext(gtm_list_head(l))) -#define lthird(l) lfirst(lnext(lnext(list_head(l)))) -#define lthird_int(l) lfirst_int(lnext(lnext(list_head(l)))) +#define gtm_lthird(l) gtm_lfirst(gtm_lnext(gtm_lnext(gtm_list_head(l)))) +#define gtm_lthird_int(l) gtm_lfirst_int(gtm_lnext(gtm_lnext(gtm_list_head(l)))) -#define lfourth(l) lfirst(lnext(lnext(lnext(list_head(l))))) -#define lfourth_int(l) lfirst_int(lnext(lnext(lnext(list_head(l))))) +#define gtm_lfourth(l) gtm_lfirst(gtm_lnext(gtm_lnext(gtm_lnext(gtm_list_head(l))))) +#define gtm_lfourth_int(l) gtm_lfirst_int(gtm_lnext(gtm_lnext(gtm_lnext(gtm_list_head(l))))) -#define llast(l) lfirst(list_tail(l)) -#define llast_int(l) lfirst_int(list_tail(l)) +#define gtm_llast(l) gtm_lfirst(gtm_list_tail(l)) +#define gtm_llast_int(l) gtm_lfirst_int(gtm_list_tail(l)) /* * Convenience macros for building fixed-length lists */ -#define list_make1(x1) lcons(x1, NIL) -#define list_make2(x1,x2) lcons(x1, list_make1(x2)) -#define list_make3(x1,x2,x3) lcons(x1, list_make2(x2, x3)) -#define list_make4(x1,x2,x3,x4) lcons(x1, list_make3(x2, x3, x4)) +#define gtm_list_make1(x1) gtm_lcons(x1, gtm_NIL) +#define gtm_list_make2(x1,x2) gtm_lcons(x1, gtm_list_make1(x2)) +#define gtm_list_make3(x1,x2,x3) gtm_lcons(x1, gtm_list_make2(x2, x3)) +#define gtm_list_make4(x1,x2,x3,x4) gtm_lcons(x1, gtm_list_make3(x2, x3, x4)) -#define list_make1_int(x1) lcons_int(x1, NIL) -#define list_make2_int(x1,x2) lcons_int(x1, list_make1_int(x2)) -#define list_make3_int(x1,x2,x3) lcons_int(x1, list_make2_int(x2, x3)) -#define list_make4_int(x1,x2,x3,x4) lcons_int(x1, list_make3_int(x2, x3, x4)) +#define gtm_list_make1_int(x1) gtm_lcons_int(x1, gtm_NIL) +#define gtm_list_make2_int(x1,x2) gtm_lcons_int(x1, gtm_list_make1_int(x2)) +#define gtm_list_make3_int(x1,x2,x3) gtm_lcons_int(x1, gtm_list_make2_int(x2, x3)) +#define gtm_list_make4_int(x1,x2,x3,x4) gtm_lcons_int(x1, gtm_list_make3_int(x2, x3, x4)) /* - * foreach - + * gtm_foreach - * a convenience macro which loops through the list */ -#define foreach(cell, l) \ - for ((cell) = list_head(l); (cell) != NULL; (cell) = lnext(cell)) +#define gtm_foreach(cell, l) \ + for ((cell) = gtm_list_head(l); (cell) != NULL; (cell) = gtm_lnext(cell)) /* - * for_each_cell - + * gtm_for_each_cell - * a convenience macro which loops through a list starting from a * specified cell */ -#define for_each_cell(cell, initcell) \ - for ((cell) = (initcell); (cell) != NULL; (cell) = lnext(cell)) +#define gtm_for_each_cell(cell, initcell) \ + for ((cell) = (initcell); (cell) != NULL; (cell) = gtm_lnext(cell)) /* - * forboth - + * gtm_forboth - * a convenience macro for advancing through two linked lists * simultaneously. This macro loops through both lists at the same * time, stopping when either list runs out of elements. Depending * on the requirements of the call site, it may also be wise to - * assert that the lengths of the two lists are equal. + * assert that the lengths of the two lists are gtm_equal. */ -#define forboth(cell1, list1, cell2, list2) \ - for ((cell1) = list_head(list1), (cell2) = list_head(list2); \ +#define gtm_forboth(cell1, list1, cell2, list2) \ + for ((cell1) = gtm_list_head(list1), (cell2) = gtm_list_head(list2); \ (cell1) != NULL && (cell2) != NULL; \ - (cell1) = lnext(cell1), (cell2) = lnext(cell2)) + (cell1) = gtm_lnext(cell1), (cell2) = gtm_lnext(cell2)) -extern List *lappend(List *list, void *datum); -extern List *lappend_int(List *list, int datum); +extern gtm_List *gtm_lappend(gtm_List *list, void *datum); +extern gtm_List *gtm_lappend_int(gtm_List *list, int datum); -extern ListCell *lappend_cell(List *list, ListCell *prev, void *datum); -extern ListCell *lappend_cell_int(List *list, ListCell *prev, int datum); +extern gtm_ListCell *gtm_lappend_cell(gtm_List *list, gtm_ListCell *prev, void *datum); +extern gtm_ListCell *gtm_lappend_cell_int(gtm_List *list, gtm_ListCell *prev, int datum); -extern List *lcons(void *datum, List *list); -extern List *lcons_int(int datum, List *list); +extern gtm_List *gtm_lcons(void *datum, gtm_List *list); +extern gtm_List *gtm_lcons_int(int datum, gtm_List *list); -extern List *list_concat(List *list1, List *list2); -extern List *list_truncate(List *list, int new_size); +extern gtm_List *gtm_list_concat(gtm_List *list1, gtm_List *list2); +extern gtm_List *gtm_list_truncate(gtm_List *list, int new_size); -extern void *list_nth(List *list, int n); -extern int list_nth_int(List *list, int n); +extern void *gtm_list_nth(gtm_List *list, int n); +extern int gtm_list_nth_int(gtm_List *list, int n); -extern bool list_member(List *list, void *datum); -extern bool list_member_ptr(List *list, void *datum); -extern bool list_member_int(List *list, int datum); +extern bool gtm_list_member(gtm_List *list, void *datum); +extern bool gtm_list_member_ptr(gtm_List *list, void *datum); +extern bool gtm_list_member_int(gtm_List *list, int datum); -extern List *list_delete(List *list, void *datum); -extern List *list_delete_ptr(List *list, void *datum); -extern List *list_delete_int(List *list, int datum); -extern List *list_delete_first(List *list); -extern List *list_delete_cell(List *list, ListCell *cell, ListCell *prev); +extern gtm_List *gtm_list_delete(gtm_List *list, void *datum); +extern gtm_List *gtm_list_delete_ptr(gtm_List *list, void *datum); +extern gtm_List *gtm_list_delete_int(gtm_List *list, int datum); +extern gtm_List *gtm_list_delete_first(gtm_List *list); +extern gtm_List *gtm_list_delete_cell(gtm_List *list, gtm_ListCell *cell, gtm_ListCell *prev); -extern List *list_union(List *list1, List *list2); -extern List *list_union_ptr(List *list1, List *list2); -extern List *list_union_int(List *list1, List *list2); +extern gtm_List *gtm_list_union(gtm_List *list1, gtm_List *list2); +extern gtm_List *gtm_list_union_ptr(gtm_List *list1, gtm_List *list2); +extern gtm_List *gtm_list_union_int(gtm_List *list1, gtm_List *list2); -extern List *list_intersection(List *list1, List *list2); +extern gtm_List *gtm_list_intersection(gtm_List *list1, gtm_List *list2); /* currently, there's no need for list_intersection_int etc */ -extern List *list_difference(List *list1, List *list2); -extern List *list_difference_ptr(List *list1, List *list2); -extern List *list_difference_int(List *list1, List *list2); +extern gtm_List *gtm_list_difference(gtm_List *list1, gtm_List *list2); +extern gtm_List *gtm_list_difference_ptr(gtm_List *list1, gtm_List *list2); +extern gtm_List *gtm_list_difference_int(gtm_List *list1, gtm_List *list2); -extern List *list_append_unique(List *list, void *datum); -extern List *list_append_unique_ptr(List *list, void *datum); -extern List *list_append_unique_int(List *list, int datum); +extern gtm_List *gtm_list_append_unique(gtm_List *list, void *datum); +extern gtm_List *gtm_list_append_unique_ptr(gtm_List *list, void *datum); +extern gtm_List *gtm_list_append_unique_int(gtm_List *list, int datum); -extern List *list_concat_unique(List *list1, List *list2); -extern List *list_concat_unique_ptr(List *list1, List *list2); -extern List *list_concat_unique_int(List *list1, List *list2); +extern gtm_List *gtm_list_concat_unique(gtm_List *list1, gtm_List *list2); +extern gtm_List *gtm_list_concat_unique_ptr(gtm_List *list1, gtm_List *list2); +extern gtm_List *gtm_list_concat_unique_int(gtm_List *list1, gtm_List *list2); -extern void list_free(List *list); -extern void list_free_deep(List *list); +extern void gtm_list_free(gtm_List *list); +extern void gtm_list_free_deep(gtm_List *list); -extern List *list_copy(List *list); -extern List *list_copy_tail(List *list, int nskip); +extern gtm_List *gtm_list_copy(gtm_List *list); +extern gtm_List *gtm_list_copy_tail(gtm_List *list, int nskip); /* * To ease migration to the new list API, a set of compatibility @@ -227,54 +227,54 @@ extern List *list_copy_tail(List *list, int nskip); */ #ifdef ENABLE_LIST_COMPAT -#define lfirsti(lc) lfirst_int(lc) +#define gtm_lfirsti(lc) gtm_lfirst_int(lc) -#define makeList1(x1) list_make1(x1) -#define makeList2(x1, x2) list_make2(x1, x2) -#define makeList3(x1, x2, x3) list_make3(x1, x2, x3) -#define makeList4(x1, x2, x3, x4) list_make4(x1, x2, x3, x4) +#define gtm_makeList1(x1) gtm_list_make1(x1) +#define gtm_makeList2(x1, x2) gtm_list_make2(x1, x2) +#define gtm_makeList3(x1, x2, x3) gtm_list_make3(x1, x2, x3) +#define gtm_makeList4(x1, x2, x3, x4) gtm_list_make4(x1, x2, x3, x4) -#define makeListi1(x1) list_make1_int(x1) -#define makeListi2(x1, x2) list_make2_int(x1, x2) +#define gtm_makeListi1(x1) gtm_list_make1_int(x1) +#define gtm_makeListi2(x1, x2) gtm_list_make2_int(x1, x2) -#define lconsi(datum, list) lcons_int(datum, list) +#define gtm_lconsi(datum, list) gtm_lcons_int(datum, list) -#define lappendi(list, datum) lappend_int(list, datum) +#define gtm_lappendi(list, datum) gtm_lappend_int(list, datum) -#define nconc(l1, l2) list_concat(l1, l2) +#define gtm_nconc(l1, l2) gtm_list_concat(l1, l2) -#define nth(n, list) list_nth(list, n) +#define gtm_nth(n, list) gtm_list_nth(list, n) -#define member(datum, list) list_member(list, datum) -#define ptrMember(datum, list) list_member_ptr(list, datum) -#define intMember(datum, list) list_member_int(list, datum) +#define gtm_member(datum, list) gtm_list_member(list, datum) +#define gtm_ptrMember(datum, list) gtm_list_member_ptr(list, datum) +#define gtm_intMember(datum, list) gtm_list_member_int(list, datum) /* - * Note that the old lremove() determined equality via pointer - * comparison, whereas the new list_delete() uses equal(); in order to - * keep the same behavior, we therefore need to map lremove() calls to - * list_delete_ptr() rather than list_delete() + * Note that the old gtm_lremove() determined equality via pointer + * comparison, whereas the new gtm_list_delete() uses gtm_equal(); in order to + * keep the same behavior, we therefore need to map gtm_lremove() calls to + * gtm_list_delete_ptr() rather than gtm_list_delete() */ -#define lremove(elem, list) list_delete_ptr(list, elem) -#define LispRemove(elem, list) list_delete(list, elem) -#define lremovei(elem, list) list_delete_int(list, elem) +#define gtm_lremove(elem, list) gtm_list_delete_ptr(list, elem) +#define gtm_LispRemove(elem, list) gtm_list_delete(list, elem) +#define gtm_lremovei(elem, list) gtm_list_delete_int(list, elem) -#define ltruncate(n, list) list_truncate(list, n) +#define gtm_ltruncate(n, list) gtm_list_truncate(list, n) -#define set_union(l1, l2) list_union(l1, l2) -#define set_ptrUnion(l1, l2) list_union_ptr(l1, l2) +#define gtm_set_union(l1, l2) gtm_list_union(l1, l2) +#define gtm_set_ptrUnion(l1, l2) gtm_list_union_ptr(l1, l2) -#define set_difference(l1, l2) list_difference(l1, l2) -#define set_ptrDifference(l1, l2) list_difference_ptr(l1, l2) +#define gtm_set_difference(l1, l2) gtm_list_difference(l1, l2) +#define gtm_set_ptrDifference(l1, l2) gtm_list_difference_ptr(l1, l2) -#define equali(l1, l2) equal(l1, l2) -#define equalo(l1, l2) equal(l1, l2) +#define gtm_equali(l1, l2) gtm_equal(l1, l2) +#define gtm_equalo(l1, l2) gtm_equal(l1, l2) -#define freeList(list) list_free(list) +#define gtm_freeList(list) gtm_list_free(list) -#define listCopy(list) list_copy(list) +#define gtm_listCopy(list) gtm_list_copy(list) -extern int length(List *list); +extern int gtm_length(gtm_List *list); #endif /* ENABLE_LIST_COMPAT */ #endif /* GTM_LIST_H */ diff --git a/src/include/gtm/gtm_msg.h b/src/include/gtm/gtm_msg.h index aeee09569b..4ec7dacb28 100644 --- a/src/include/gtm/gtm_msg.h +++ b/src/include/gtm/gtm_msg.h @@ -19,6 +19,9 @@ typedef enum GTM_MessageType MSG_TYPE_INVALID, MSG_NODE_REGISTER, /* Register a PGXC Node with GTM */ MSG_NODE_UNREGISTER, /* Unregister a PGXC Node with GTM */ + MSG_NODE_LIST, + MSG_NODE_BEGIN_REPLICATION_INIT, + MSG_NODE_END_REPLICATION_INIT, MSG_TXN_BEGIN, /* Start a new transaction */ MSG_TXN_BEGIN_GETGXID, /* Start a new transaction and get GXID */ MSG_TXN_BEGIN_GETGXID_MULTI, /* Start multiple new transactions and get GXIDs */ @@ -31,6 +34,8 @@ typedef enum GTM_MessageType MSG_TXN_ROLLBACK_MULTI, /* Rollback multiple transactions */ MSG_TXN_GET_GID_DATA, /* Get info associated with a GID, and get a GXID */ MSG_TXN_GET_GXID, /* Get a GXID for a transaction */ + MSG_TXN_GET_NEXT_GXID, /* Get next GXID */ + MSG_TXN_GXID_LIST, MSG_SNAPSHOT_GET, /* Get a global snapshot */ MSG_SNAPSHOT_GET_MULTI, /* Get multiple global snapshots */ MSG_SNAPSHOT_GXID_GET, /* Get GXID and snapshot together */ @@ -43,6 +48,7 @@ typedef enum GTM_MessageType MSG_SEQUENCE_CLOSE, /* Close a previously inited sequence */ MSG_SEQUENCE_RENAME, /* Rename a sequence */ MSG_SEQUENCE_ALTER, /* Alter a sequence */ + MSG_SEQUENCE_LIST, /* Get a list of sequences */ MSG_TXN_GET_STATUS, /* Get status of a given transaction */ MSG_TXN_GET_ALL_PREPARED, /* Get information about all outstanding * prepared transactions */ @@ -60,6 +66,9 @@ typedef enum GTM_ResultType { NODE_REGISTER_RESULT, NODE_UNREGISTER_RESULT, + NODE_LIST_RESULT, + NODE_BEGIN_REPLICATION_INIT_RESULT, + NODE_END_REPLICATION_INIT_RESULT, TXN_BEGIN_RESULT, TXN_BEGIN_GETGXID_RESULT, TXN_BEGIN_GETGXID_MULTI_RESULT, @@ -72,6 +81,8 @@ typedef enum GTM_ResultType TXN_ROLLBACK_MULTI_RESULT, TXN_GET_GID_DATA_RESULT, TXN_GET_GXID_RESULT, + TXN_GET_NEXT_GXID_RESULT, + TXN_GXID_LIST_RESULT, SNAPSHOT_GET_RESULT, SNAPSHOT_GET_MULTI_RESULT, SNAPSHOT_GXID_GET_RESULT, @@ -84,6 +95,7 @@ typedef enum GTM_ResultType SEQUENCE_CLOSE_RESULT, SEQUENCE_RENAME_RESULT, SEQUENCE_ALTER_RESULT, + SEQUENCE_LIST_RESULT, TXN_GET_STATUS_RESULT, TXN_GET_ALL_PREPARED_RESULT, TXN_BEGIN_GETGXID_AUTOVACUUM_RESULT, diff --git a/src/include/gtm/gtm_proxy.h b/src/include/gtm/gtm_proxy.h index 0031d6e83b..ead825320b 100644 --- a/src/include/gtm/gtm_proxy.h +++ b/src/include/gtm/gtm_proxy.h @@ -110,11 +110,16 @@ typedef struct GTMProxy_ThreadInfo /* connection array */ GTMProxy_ConnectionInfo *thr_all_conns[GTM_PROXY_MAX_CONNECTIONS]; struct pollfd thr_poll_fds[GTM_PROXY_MAX_CONNECTIONS]; - List *thr_processed_commands; - List *thr_pending_commands[MSG_TYPE_COUNT]; - GTM_Conn *thr_gtm_conn; + /* Command backup */ + short thr_any_backup[GTM_PROXY_MAX_CONNECTIONS]; + int thr_qtype[GTM_PROXY_MAX_CONNECTIONS]; + StringInfoData thr_inBufData[GTM_PROXY_MAX_CONNECTIONS]; + + gtm_List *thr_processed_commands; + gtm_List *thr_pending_commands[MSG_TYPE_COUNT]; + GTM_Conn *thr_gtm_conn; } GTMProxy_ThreadInfo; typedef struct GTMProxy_Threads @@ -173,6 +178,7 @@ typedef union GTMProxy_CommandData GTM_PGXCNodeId proxynum; char *datafolder; char *ipaddress; + GTM_PGXCNodeStatus status; } cd_reg; } GTMProxy_CommandData; diff --git a/src/include/gtm/gtm_seq.h b/src/include/gtm/gtm_seq.h index 6ac8092ef5..92174eecc5 100644 --- a/src/include/gtm/gtm_seq.h +++ b/src/include/gtm/gtm_seq.h @@ -15,6 +15,8 @@ #define GTM_SEQ_H #include "gtm/stringinfo.h" +#include "gtm/gtm_lock.h" +#include "gtm/libpq-be.h" /* Global sequence related structures */ @@ -83,7 +85,18 @@ void ProcessSequenceCloseCommand(Port *myport, StringInfo message); void ProcessSequenceRenameCommand(Port *myport, StringInfo message); void ProcessSequenceAlterCommand(Port *myport, StringInfo message); +void ProcessSequenceListCommand(Port *myport, StringInfo message); + void GTM_SaveSeqInfo(int ctlfd); void GTM_RestoreSeqInfo(int ctlfd); +int GTM_SeqRestore(GTM_SequenceKey seqkey, + GTM_Sequence increment_by, + GTM_Sequence minval, + GTM_Sequence maxval, + GTM_Sequence startval, + GTM_Sequence curval, + int32 state, + bool cycle, + bool called); #endif diff --git a/src/include/gtm/gtm_serialize.h b/src/include/gtm/gtm_serialize.h new file mode 100644 index 0000000000..1fe80cd707 --- /dev/null +++ b/src/include/gtm/gtm_serialize.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + * + * gtm_serialize.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gtm/gtm_serialize.h + * + *------------------------------------------------------------------------- + */ + +#ifndef GTM_SERIALIZE_H +#define GTM_SERIALIZE_H + +#include <sys/types.h> + +#include "gtm/gtm_c.h" +#include "gtm/gtm_txn.h" +#include "gtm/register.h" +#include "gtm/gtm_seq.h" + +size_t gtm_get_snapshotdata_size(GTM_SnapshotData *); +size_t gtm_serialize_snapshotdata(GTM_SnapshotData *, char *, size_t); +size_t gtm_deserialize_snapshotdata(GTM_SnapshotData *, const char *, size_t); + +size_t gtm_get_transactioninfo_size(GTM_TransactionInfo *); +size_t gtm_serialize_transactioninfo(GTM_TransactionInfo *, char *, size_t); +size_t gtm_deserialize_transactioninfo(GTM_TransactionInfo *, const char *, size_t); + +size_t gtm_get_transactions_size(GTM_Transactions *); +size_t gtm_serialize_transactions(GTM_Transactions *, char *, size_t); +size_t gtm_deserialize_transactions(GTM_Transactions *, const char *, size_t); + +size_t gtm_get_pgxcnodeinfo_size(GTM_PGXCNodeInfo *); +size_t gtm_serialize_pgxcnodeinfo(GTM_PGXCNodeInfo *, char *, size_t); +size_t gtm_deserialize_pgxcnodeinfo(GTM_PGXCNodeInfo *, const char *, size_t); + +size_t gtm_get_sequence_size(GTM_SeqInfo *); +size_t gtm_serialize_sequence(GTM_SeqInfo *, char *, size_t); +GTM_SeqInfo *gtm_deserialize_sequence(const char *, size_t); + +void dump_transactions_elog(GTM_Transactions *, int); +void dump_transactioninfo_elog(GTM_TransactionInfo *); + +#endif /* GTM_SERIALIZE_H */ diff --git a/src/include/gtm/gtm_serialize_debug.h b/src/include/gtm/gtm_serialize_debug.h new file mode 100644 index 0000000000..ba9d89dc53 --- /dev/null +++ b/src/include/gtm/gtm_serialize_debug.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * gtm_serialize_debug.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gtm/gtm_serialize_debug.h + * + *------------------------------------------------------------------------- + */ +#ifndef GTM_SERIALIZE_DEBUG_H +#define GTM_SERIALIZE_DEBUG_H + +#include <sys/types.h> + +#include "gtm/gtm_txn.h" + +void dump_transactions_elog(GTM_Transactions *, int); +void dump_transactioninfo_elog(GTM_TransactionInfo *); + +#endif /* GTM_SERIALIZE_DEBUG_H */ diff --git a/src/include/gtm/gtm_standby.h b/src/include/gtm/gtm_standby.h new file mode 100644 index 0000000000..299f65c454 --- /dev/null +++ b/src/include/gtm/gtm_standby.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * gtm_standby.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gtm/gtm_standby.h + * + *------------------------------------------------------------------------- + */ + +#ifndef GTM_STANDBY_H +#define GTM_STANDBY_H + +#include "c.h" +#include "gtm/gtm_c.h" +#include "gtm/libpq-fe.h" + +/* + * Variables to interact with GTM active under GTM standby mode. + */ +bool gtm_is_standby(void); +void gtm_set_standby(bool standby); +void gtm_set_active_conninfo(const char *addr, int port); + +int gtm_standby_start_startup(void); +int gtm_standby_finish_startup(void); + +int gtm_standby_restore_next_gxid(void); +int gtm_standby_restore_gxid(void); +int gtm_standby_restore_sequence(void); +int gtm_standby_restore_node(void); + +int gtm_standby_register_self(GTM_PGXCNodeId nodenum, int port, const char *datadir); +int gtm_standby_activate_self(void); + +GTM_Conn *gtm_standby_connect_to_standby(void); +void gtm_standby_disconnect_from_standby(GTM_Conn *conn); +GTM_Conn *gtm_standby_reconnect_to_standby(GTM_Conn *old_conn, int retry_max); +bool gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn); + +#endif /* GTM_STANDBY_H */ diff --git a/src/include/gtm/gtm_txn.h b/src/include/gtm/gtm_txn.h index 9b2fb83aa1..86a0d4919b 100644 --- a/src/include/gtm/gtm_txn.h +++ b/src/include/gtm/gtm_txn.h @@ -14,6 +14,7 @@ #ifndef _GTM_TXN_H #define _GTM_TXN_H +#include "gtm/libpq-be.h" #include "gtm/gtm_c.h" #include "gtm/gtm_lock.h" #include "gtm/gtm_list.h" @@ -163,14 +164,14 @@ typedef struct GTM_Transactions int32 gt_lastslot; GTM_TransactionInfo gt_transactions_array[GTM_MAX_GLOBAL_TRANSACTIONS]; - List *gt_open_transactions; + gtm_List *gt_open_transactions; GTM_RWLock gt_TransArrayLock; } GTM_Transactions; extern GTM_Transactions GTMTransactions; -#define GTM_CountOpenTransactions() (list_length(GTMTransactions.gt_open_transactions)) +#define GTM_CountOpenTransactions() (gtm_list_length(GTMTransactions.gt_open_transactions)) /* * Two hash tables will be maintained to quickly find the @@ -239,6 +240,8 @@ void ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message); void ProcessPrepareTransactionCommand(Port *myport, StringInfo message); void ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message); void ProcessGetGXIDTransactionCommand(Port *myport, StringInfo message); +void ProcessGXIDListCommand(Port *myport, StringInfo message); +void ProcessGetNextGXIDTransactionCommand(Port *myport, StringInfo message); void ProcessBeginTransactionGetGXIDAutovacuumCommand(Port *myport, StringInfo message); void ProcessBeginTransactionGetGXIDCommandMulti(Port *myport, StringInfo message); diff --git a/src/include/gtm/gtm_utils.h b/src/include/gtm/gtm_utils.h new file mode 100644 index 0000000000..245daa9114 --- /dev/null +++ b/src/include/gtm/gtm_utils.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * gtm_utils.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gtm/gtm_utils.h + * + *------------------------------------------------------------------------- + */ +#ifndef GTM_UTILS_H +#define GTM_UTILS_H + +#include "gtm/libpq-int.h" + +#if 0 +/* + * PGXCTODO: This portion of code needs XCM support + * to be able to report GTM failures to XC watcher and + * enable a GTM reconnection kick. + */ +void gtm_report_failure(GTM_Conn *); +#endif + +#endif /* GTM_UTIL_H */ diff --git a/src/include/gtm/libpq-be.h b/src/include/gtm/libpq-be.h index d5a5df5ed0..eaea0adcf0 100644 --- a/src/include/gtm/libpq-be.h +++ b/src/include/gtm/libpq-be.h @@ -19,6 +19,9 @@ #ifndef LIBPQ_BE_H #define LIBPQ_BE_H +#include "gtm/pqcomm.h" +#include "gtm/gtm_c.h" + #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif @@ -26,8 +29,6 @@ #include <netinet/tcp.h> #endif -#include "gtm/pqcomm.h" - /* * This is used by the postmaster in its communication with frontends. It * contains all state information needed during this communication before the @@ -72,6 +73,13 @@ typedef struct Port int keepalives_idle; int keepalives_interval; int keepalives_count; + + /* + * GTM communication error handling. See libpq-int.h for details. + */ + int connErr_WaitOpt; + int connErr_WaitSecs; + int connErr_WaitCount; } Port; /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */ diff --git a/src/include/gtm/libpq-int.h b/src/include/gtm/libpq-int.h index 5c6714bc9a..2961f70dd0 100644 --- a/src/include/gtm/libpq-int.h +++ b/src/include/gtm/libpq-int.h @@ -86,6 +86,11 @@ struct gtm_conn /* Buffer for receiving various parts of messages */ PQExpBufferData workBuffer; /* expansible string */ + /* Options to handle GTM communication error */ + int gtmErrorWaitOpt; /* If true, wait reconnect signal. */ + int gtmErrorWaitSecs; /* Duration of the wait time in second */ + int gtmErrorWaitCount; /* How many durations to wait */ + /* Pointer to the result of last operation */ GTM_Result *result; }; diff --git a/src/include/gtm/palloc.h b/src/include/gtm/palloc.h index e81e9d14ac..0c82e7362b 100644 --- a/src/include/gtm/palloc.h +++ b/src/include/gtm/palloc.h @@ -87,4 +87,12 @@ extern char *pgport_pstrdup(const char *str); extern void pgport_pfree(void *pointer); #endif +#ifdef PGXC +/* + * The following part provides common palloc binary interface. This + * is needed especially for gtm_serialize.c and gtm_serialize_debug.c. + */ +#include "gen_alloc.h" +#endif + #endif /* PALLOC_H */ diff --git a/src/include/gtm/pqcomm.h b/src/include/gtm/pqcomm.h index df655a5c5b..010952e287 100644 --- a/src/include/gtm/pqcomm.h +++ b/src/include/gtm/pqcomm.h @@ -39,14 +39,6 @@ typedef struct (port)) /* - * Packet lengths are 4 bytes in network byte order. - * - * The initial length is omitted from the packet layouts appearing below. - */ - -typedef uint32 PacketLen; - -/* * In protocol 3.0 and later, the startup packet length is not fixed, but * we set an arbitrary limit on it anyway. This is just to prevent simple * denial-of-service attacks via sending enough data to run the server diff --git a/src/include/gtm/proxy_utils.h b/src/include/gtm/proxy_utils.h new file mode 100644 index 0000000000..04b14e95d2 --- /dev/null +++ b/src/include/gtm/proxy_utils.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * proxy_utils.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gtm/proxy_utils.h + * + *------------------------------------------------------------------------- + */ + +#ifndef PROXY_UTILS_H +#define PROXY_UTILS_H + +#include "gtm/libpq-int.h" + +bool gtm_standby_check_communication_error(int *, GTM_Conn *); + +#endif /* PROXY_UTILS_H */ + diff --git a/src/include/gtm/register.h b/src/include/gtm/register.h index 9b11d1d6cb..0421d2bbb9 100644 --- a/src/include/gtm/register.h +++ b/src/include/gtm/register.h @@ -14,6 +14,7 @@ #ifndef _REGISTER_H #define _REGISTER_H +#include "gtm/libpq-be.h" #include "gtm/gtm_c.h" #include "gtm/gtm_lock.h" #include "gtm/gtm_list.h" @@ -51,6 +52,12 @@ typedef struct GTM_PGXCNodeInfo int socket; /* socket number used for registration */ } GTM_PGXCNodeInfo; +/* Maximum number of nodes that can be registered */ +#define MAX_NODES 1024 + +size_t pgxcnode_get_all(GTM_PGXCNodeInfo **data, size_t maxlen); +size_t pgxcnode_find_by_type(GTM_PGXCNodeType type, GTM_PGXCNodeInfo **data, size_t maxlen); + int Recovery_PGXCNodeRegister(GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum, GTM_PGXCNodePort port, @@ -75,5 +82,6 @@ void Recovery_SaveRegisterFileName(char *dir); void ProcessPGXCNodeRegister(Port *myport, StringInfo message); void ProcessPGXCNodeUnregister(Port *myport, StringInfo message); void ProcessPGXCNodeBackendDisconnect(Port *myport, StringInfo message); +void ProcessPGXCNodeList(Port *myport, StringInfo message); #endif /* GTM_NODE_H */ diff --git a/src/include/gtm/replication.h b/src/include/gtm/replication.h new file mode 100644 index 0000000000..93ff885aff --- /dev/null +++ b/src/include/gtm/replication.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * replication.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gtm/replication.h + * + *------------------------------------------------------------------------- + */ +#ifndef REPLICATION_H +#define REPLICATION_H + +#include "c.h" + +#include "gtm/libpq.h" +#include "gtm/stringinfo.h" + +void ProcessBeginReplicationInitialSyncRequest(Port *, StringInfo); +void ProcessEndReplicationInitialSyncRequest(Port *, StringInfo); + +#endif /* REPLICATION_H */ diff --git a/src/include/gtm/standby_utils.h b/src/include/gtm/standby_utils.h new file mode 100644 index 0000000000..db05adc674 --- /dev/null +++ b/src/include/gtm/standby_utils.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * standby_utils.h + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * + * src/include/gtm/standby_utils.h + * + *------------------------------------------------------------------------- + */ + +#ifndef STANDBY_UTILS_H +#define STANDBY_UTILS_H + +#include "gtm/gtm_lock.h" + +bool Recovery_IsStandby(void); +void Recovery_StandbySetStandby(bool standby); +void Recovery_StandbySetConnInfo(const char *addr, int port); +int Recovery_StandbyGetActivePort(void); +char* Recovery_StandbyGetActiveAddress(void); +void Recovery_InitStandbyLock(void); + +#endif diff --git a/src/include/gtm/stringinfo.h b/src/include/gtm/stringinfo.h index c34e6a32fb..62bb45631a 100644 --- a/src/include/gtm/stringinfo.h +++ b/src/include/gtm/stringinfo.h @@ -146,4 +146,17 @@ extern void appendBinaryStringInfo(StringInfo str, */ extern void enlargeStringInfo(StringInfo str, int needed); +/*----------------------- + * dupStringInfo + * Get new StringInfo and copy the original to it. + */ +extern StringInfo dupStringInfo(StringInfo orig); + +/*------------------------ + * copyStringInfo + * Copy StringInfo. Deep copy: Data will be copied too. + * cursor of "to" will be initialized to zero. + */ +extern void copyStringInfo(StringInfo to, StringInfo from); + #endif /* STRINGINFO_H */ |