static void cm_sighup_handler(SIGNAL_ARGS);
static void cm_sigterm_handler(SIGNAL_ARGS);
+static void cm_sigint_handler(SIGNAL_ARGS);
static void ClusterMonitorSetReportedGlobalXmin(GlobalTransactionId xmin);
static void ClusterMonitorSetReportingGlobalXmin(GlobalTransactionId xmin);
#define CLUSTER_MONITOR_NAPTIME 5
+/*
+ * Report xmin to the GTM and fetch the global xmin information in the
+ * response.
+ */
+static void
+ClusterMonitorReportXmin(void)
+{
+ GlobalTransactionId oldestXmin;
+ GlobalTransactionId newOldestXmin;
+ GlobalTransactionId lastGlobalXmin;
+ GlobalTransactionId latestCompletedXid;
+ int status;
+
+ /*
+ * Compute RecentGlobalXmin, report it to the GTM and sleep for the set
+ * interval. Keep doing this forever
+ */
+ lastGlobalXmin = ClusterMonitorGetGlobalXmin(true);
+ LWLockAcquire(ClusterMonitorLock, LW_EXCLUSIVE);
+ oldestXmin = GetOldestXminInternal(NULL, 0, true, lastGlobalXmin);
+ ClusterMonitorSetReportingGlobalXmin(oldestXmin);
+ LWLockRelease(ClusterMonitorLock);
+
+ if ((status = ReportGlobalXmin(oldestXmin, &newOldestXmin,
+ &latestCompletedXid)))
+ {
+ elog(DEBUG1, "Failed (status %d) to report RecentGlobalXmin "
+ "- reported RecentGlobalXmin %u, received "
+ "RecentGlobalXmin %u, " "received latestCompletedXid %u",
+ status, oldestXmin, newOldestXmin,
+ latestCompletedXid);
+ if (status == GTM_ERRCODE_TOO_OLD_XMIN ||
+ status == GTM_ERRCODE_NODE_EXCLUDED)
+ {
+ /*
+ * If we haven't seen a new transaction for a very long time or
+ * were disconncted for a while or excluded from the xmin
+ * computation for any reason, our xmin calculation could be
+ * well in the past, especially because its capped by the
+ * latestCompletedXid which may not advance on an idle server.
+ * In such cases, use the value of latestCompletedXid as
+ * returned by GTM and then recompute local xmin.
+ *
+ * If the GTM's global xmin advances even further while we are
+ * ready with a new xmin, just repeat the entire exercise as
+ * long as GTM keeps returning us a more current value of
+ * latestCompletedXid and thus pushing forward our local xmin
+ * calculation
+ */
+ if (GlobalTransactionIdIsValid(latestCompletedXid) &&
+ TransactionIdPrecedes(oldestXmin, latestCompletedXid))
+ {
+ SetLatestCompletedXid(latestCompletedXid);
+ return;
+ }
+ }
+ else if (status == GTM_ERRCODE_NODE_NOT_REGISTERED)
+ {
+ /*
+ * If we're not registered on the GTM, it could be because the
+ * GTM is restarted. Just exit and let the cluster monitor be
+ * restarted again.
+ */
+ elog(WARNING, "ClusterMonitor process exiting - node not "
+ "registered on the GTM");
+ proc_exit(0);
+ }
+ }
+ else
+ {
+ elog(DEBUG1, "Successfully reported xmin to GTM - reported_xmin %u,"
+ "received RecentGlobalXmin %u, "
+ "received latestCompletedXid %u", oldestXmin,
+ newOldestXmin, latestCompletedXid);
+
+ SetLatestCompletedXid(latestCompletedXid);
+ ClusterMonitorSetReportedGlobalXmin(oldestXmin);
+ if (GlobalTransactionIdIsValid(newOldestXmin))
+ ClusterMonitorSetGlobalXmin(newOldestXmin);
+ }
+
+ ClusterMonitorSetReportingGlobalXmin(InvalidGlobalTransactionId);
+}
+
+/*
+ * Update our local view of the global transactions using a recently fetched
+ * snapshot from the GTM. The snapshot contains the information about the
+ * currently running transactions and that's what we care about.
+ *
+ * We don't want to overwrite a future state with a past state just because a
+ * backend received an older snapshot. Checking snapshot->sn_snapid serves that
+ * purpose.
+ */
+void
+ClusterMonitorSyncGlobalStateUsingSnapshot(GTM_Snapshot snapshot)
+{
+ if (snapshot == NULL ||
+ snapshot->sn_snapid < ClusterMonitorCtl->gtm_snapid)
+ return;
+
+ /* Populate shared memory state */
+ SpinLockAcquire(&ClusterMonitorCtl->mutex);
+ ClusterMonitorCtl->gtm_xmin = snapshot->sn_xmin;
+ ClusterMonitorCtl->gtm_xmax = snapshot->sn_xmax;
+ ClusterMonitorCtl->gtm_xcnt = snapshot->sn_xcnt;
+ ClusterMonitorCtl->gtm_snapid = snapshot->sn_snapid;
+ memcpy((char *) ClusterMonitorCtl->gtm_xip, (char *) snapshot->sn_xip,
+ sizeof (GlobalTransactionId) * snapshot->sn_xcnt);
+ SpinLockRelease(&ClusterMonitorCtl->mutex);
+
+ /* Wake up all processes waiting on our CV. */
+ ConditionVariableBroadcast(&ClusterMonitorCtl->cv);
+}
+
+/*
+ * Sync global state.
+ */
+static void
+ClusterMonitorSyncGlobalState(void)
+{
+ GTM_Snapshot snapshot = GetSnapshotGTM(InvalidGlobalTransactionId, true);
+ ClusterMonitorSyncGlobalStateUsingSnapshot(snapshot);
+}
+
/*
* Main loop for the cluster monitor process.
*/
int
ClusterMonitorInit(void)
{
- sigjmp_buf local_sigjmp_buf;
GTM_PGXCNodeType nodetype = IS_PGXC_DATANODE ?
GTM_NODE_DATANODE :
GTM_NODE_COORDINATOR;
- GlobalTransactionId oldestXmin;
- GlobalTransactionId newOldestXmin;
- GlobalTransactionId lastGlobalXmin;
- GlobalTransactionId latestCompletedXid;
- int status;
+ sigjmp_buf local_sigjmp_buf;
bool bootingUp = true;
int aggreesiveReportingCount = 0;
am_clustermon = true;
+ ClusterMonitorCtl->clustermonitor_pid = MyProcPid;
+
/* Identify myself via ps */
init_ps_display("cluster monitor process", "", "", "");
* tcop/postgres.c.
*/
pqsignal(SIGHUP, cm_sighup_handler);
- pqsignal(SIGINT, StatementCancelHandler);
+ pqsignal(SIGINT, cm_sigint_handler);
pqsignal(SIGTERM, cm_sigterm_handler);
pqsignal(SIGQUIT, quickdie);
ProcessConfigFile(PGC_SIGHUP);
}
- /*
- * Compute RecentGlobalXmin, report it to the GTM and sleep for the set
- * interval. Keep doing this forever
- */
- lastGlobalXmin = ClusterMonitorGetGlobalXmin(true);
- LWLockAcquire(ClusterMonitorLock, LW_EXCLUSIVE);
- oldestXmin = GetOldestXminInternal(NULL, 0, true, lastGlobalXmin);
- ClusterMonitorSetReportingGlobalXmin(oldestXmin);
- LWLockRelease(ClusterMonitorLock);
-
- if ((status = ReportGlobalXmin(oldestXmin, &newOldestXmin,
- &latestCompletedXid)))
- {
- elog(DEBUG1, "Failed (status %d) to report RecentGlobalXmin "
- "- reported RecentGlobalXmin %u, received "
- "RecentGlobalXmin %u, " "received latestCompletedXid %u",
- status, oldestXmin, newOldestXmin,
- latestCompletedXid);
- if (status == GTM_ERRCODE_TOO_OLD_XMIN ||
- status == GTM_ERRCODE_NODE_EXCLUDED)
- {
- /*
- * If we haven't seen a new transaction for a very long time or
- * were disconncted for a while or excluded from the xmin
- * computation for any reason, our xmin calculation could be
- * well in the past, especially because its capped by the
- * latestCompletedXid which may not advance on an idle server.
- * In such cases, use the value of latestCompletedXid as
- * returned by GTM and then recompute local xmin.
- *
- * If the GTM's global xmin advances even further while we are
- * ready with a new xmin, just repeat the entire exercise as
- * long as GTM keeps returning us a more current value of
- * latestCompletedXid and thus pushing forward our local xmin
- * calculation
- */
- if (GlobalTransactionIdIsValid(latestCompletedXid) &&
- TransactionIdPrecedes(oldestXmin, latestCompletedXid))
- {
- SetLatestCompletedXid(latestCompletedXid);
- continue;
- }
- }
- else if (status == GTM_ERRCODE_NODE_NOT_REGISTERED)
- {
- /*
- * If we're not registered on the GTM, it could be because the
- * GTM is restarted. Just exit and let the cluster monitor be
- * restarted again.
- */
- elog(WARNING, "ClusterMonitor process exiting - node not "
- "registered on the GTM");
- proc_exit(0);
- }
- }
- else
- {
- elog(DEBUG1, "Successfully reported xmin to GTM - reported_xmin %u,"
- "received RecentGlobalXmin %u, "
- "received latestCompletedXid %u", oldestXmin,
- newOldestXmin, latestCompletedXid);
-
- SetLatestCompletedXid(latestCompletedXid);
- ClusterMonitorSetReportedGlobalXmin(oldestXmin);
- if (GlobalTransactionIdIsValid(newOldestXmin))
- ClusterMonitorSetGlobalXmin(newOldestXmin);
- }
-
- ClusterMonitorSetReportingGlobalXmin(InvalidGlobalTransactionId);
-
+ ClusterMonitorReportXmin();
+ ClusterMonitorSyncGlobalState();
}
/* Normal exit from the cluster monitor is here */
}
+/* SIGINT: time to report */
+static void
+cm_sigint_handler(SIGNAL_ARGS)
+{
+ int save_errno = errno;
+
+ SetLatch(MyLatch);
+
+ errno = save_errno;
+}
+
/*
* IsClusterMonitor functions
* Return whether this is either a cluster monitor process or a worker
/* First time through, so initialize */
MemSet(ClusterMonitorCtl, 0, ClusterMonitorShmemSize());
SpinLockInit(&ClusterMonitorCtl->mutex);
+ ConditionVariableInit(&ClusterMonitorCtl->cv);
}
}
return reporting_xmin;
}
+
+/*
+ * Wake up cluster monitor process.
+ */
+void
+ClusterMonitorWakeUp(void)
+{
+ (void ) kill(ClusterMonitorCtl->clustermonitor_pid, SIGINT);
+}
+
+/*
+ * ClusterMonitorTransactionIsInProgress
+ *
+ * Check if the given transaction is in-progress anywhere in the cluster. Our
+ * local copy of the global state may not be accurate and hence this might
+ * return a slightly stale result. But the callers should be prepared to deal
+ * with that.
+ */
+bool
+ClusterMonitorTransactionIsInProgress(GlobalTransactionId gxid)
+{
+ int i;
+ bool status = false;
+
+ SpinLockAcquire(&ClusterMonitorCtl->mutex);
+ if (GlobalTransactionIdPrecedes(gxid, ClusterMonitorCtl->gtm_xmin))
+ status = false;
+
+ if (GlobalTransactionIdFollowsOrEquals(gxid, ClusterMonitorCtl->gtm_xmax))
+ status = true;
+
+ for (i = 0; i < ClusterMonitorCtl->gtm_xcnt; i++)
+ {
+ if (GlobalTransactionIdEquals(ClusterMonitorCtl->gtm_xip[i], gxid))
+ {
+ status = true;
+ break;
+ }
+ }
+ SpinLockRelease(&ClusterMonitorCtl->mutex);
+
+ return status;
+}
+
+/*
+ * ClusterMonitorWaitForEOFTransaction
+ *
+ * Wait for the given transaction to complete cluster-wide.
+ */
+void
+ClusterMonitorWaitForEOFTransaction(GlobalTransactionId gxid)
+{
+ ConditionVariablePrepareToSleep(&ClusterMonitorCtl->cv);
+ while (ClusterMonitorTransactionIsInProgress(gxid))
+ ConditionVariableSleep(&ClusterMonitorCtl->cv,
+ WAIT_EVENT_CLUSTER_MONITOR_MAIN);
+ ConditionVariableCancelSleep();
+}
}
/*
- * TransactionIdIsInProgress -- is given transaction running in some backend
- *
- * Aside from some shortcuts such as checking RecentXmin and our own Xid,
- * there are four possibilities for finding a running transaction:
- *
- * 1. The given Xid is a main transaction Id. We will find this out cheaply
- * by looking at the PGXACT struct for each backend.
- *
- * 2. The given Xid is one of the cached subxact Xids in the PGPROC array.
- * We can find this out cheaply too.
- *
- * 3. In Hot Standby mode, we must search the KnownAssignedXids list to see
- * if the Xid is running on the master.
- *
- * 4. Search the SubTrans tree to find the Xid's topmost parent, and then see
- * if that is running according to PGXACT or KnownAssignedXids. This is the
- * slowest way, but sadly it has to be done always if the others failed,
- * unless we see that the cached subxact sets are complete (none have
- * overflowed).
- *
- * ProcArrayLock has to be held while we do 1, 2, 3. If we save the top Xids
- * while doing 1 and 3, we can release the ProcArrayLock while we do 4.
- * This buys back some concurrency (and we can't retrieve the main Xids from
- * PGXACT again anyway; see GetNewTransactionId).
+ * Real workhouse for TransactionIdIsInProgress. If check_gtm is true, then
+ * also check cluster monitor's global state to see if the transaction is
+ * complete on the GTM too.
*/
bool
-TransactionIdIsInProgress(TransactionId xid)
+TransactionIdIsInProgressExtended(TransactionId xid, bool check_gtm)
{
static TransactionId *xids = NULL;
int nxids = 0;
xids[nxids++] = pxid;
}
+ if (check_gtm && ClusterMonitorTransactionIsInProgress(xid))
+ {
+ elog(LOG, "ClusterMonitor reports xid %u as in-progress", xid);
+ LWLockRelease(ProcArrayLock);
+ return true;
+ }
+
/*
* Step 3: in hot standby mode, check the known-assigned-xids list. XIDs
* in the list must be treated as running.
return false;
}
+/*
+ * TransactionIdIsInProgress -- is given transaction running in some backend
+ *
+ * Aside from some shortcuts such as checking RecentXmin and our own Xid,
+ * there are four possibilities for finding a running transaction:
+ *
+ * 1. The given Xid is a main transaction Id. We will find this out cheaply
+ * by looking at the PGXACT struct for each backend.
+ *
+ * 2. The given Xid is one of the cached subxact Xids in the PGPROC array.
+ * We can find this out cheaply too.
+ *
+ * 3. In Hot Standby mode, we must search the KnownAssignedXids list to see
+ * if the Xid is running on the master.
+ *
+ * 4. Search the SubTrans tree to find the Xid's topmost parent, and then see
+ * if that is running according to PGXACT or KnownAssignedXids. This is the
+ * slowest way, but sadly it has to be done always if the others failed,
+ * unless we see that the cached subxact sets are complete (none have
+ * overflowed).
+ *
+ * ProcArrayLock has to be held while we do 1, 2, 3. If we save the top Xids
+ * while doing 1 and 3, we can release the ProcArrayLock while we do 4.
+ * This buys back some concurrency (and we can't retrieve the main Xids from
+ * PGXACT again anyway; see GetNewTransactionId).
+ */
+bool
+TransactionIdIsInProgress(TransactionId xid)
+{
+ return TransactionIdIsInProgressExtended(xid, true);
+}
+
/*
* TransactionIdIsActive -- is xid the top-level XID of an active backend?
*
SetGlobalSnapshotData(gtm_snapshot->sn_xmin, gtm_snapshot->sn_xmax,
gtm_snapshot->sn_xcnt, gtm_snapshot->sn_xip, SNAPSHOT_DIRECT);
GetSnapshotFromGlobalSnapshot(snapshot);
+
+ ClusterMonitorSyncGlobalStateUsingSnapshot(gtm_snapshot);
}
LWLockRelease(ClusterMonitorLock);
}
#include "access/xact.h"
#include "catalog/catalog.h"
#include "miscadmin.h"
+#include "postmaster/clustermon.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
LockRelease(&tag, ShareLock, false);
- if (!TransactionIdIsInProgress(xid))
+ if (!TransactionIdIsInProgressExtended(xid, false))
+ {
+ if (ClusterMonitorTransactionIsInProgress(xid))
+ ClusterMonitorWaitForEOFTransaction(xid);
break;
+ }
/*
* If the Xid belonged to a subtransaction, then the lock would have
LockRelease(&tag, ShareLock, false);
- if (!TransactionIdIsInProgress(xid))
- break;
+ if (!TransactionIdIsInProgressExtended(xid, false))
+ {
+ if (ClusterMonitorTransactionIsInProgress(xid) && !first)
+ return false;
+ else
+ break;
+ }
/* See XactLockTableWait about this case */
if (!first)
break;
}
+ if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_snapid,
+ sizeof (uint64), conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_xmin,
sizeof (GlobalTransactionId), conn))
{
memcpy(buf + len, &(data->gt_latestCompletedXid), sizeof(GlobalTransactionId));
len += sizeof(GlobalTransactionId);
+ /* GTM_Transactions.gt_snapid */
+ memcpy(buf + len, &(data->gt_snapid), sizeof(uint64));
+ len += sizeof(uint64);
+
/* GTM_Transactions.gt_recent_global_xmin */
memcpy(buf + len, &(data->gt_recent_global_xmin), sizeof(GlobalTransactionId));
len += sizeof(GlobalTransactionId);
memcpy(&(data->gt_latestCompletedXid), buf + len, sizeof(GlobalTransactionId));
len += sizeof(GlobalTransactionId);
+ /* GTM_Transactions.gt_snapid */
+ memcpy(&(data->gt_snapid), buf + len, sizeof(uint64));
+ len += sizeof(uint64);
+
/* GTM_Transactions.gt_recent_global_xmin */
memcpy(&(data->gt_recent_global_xmin), buf + len, sizeof(GlobalTransactionId));
len += sizeof(GlobalTransactionId);
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_snapid: %lu", txn->gt_snapid);
elog(LOG, " gt_recent_global_xmin: %d", txn->gt_recent_global_xmin);
elog(LOG, " gt_lastslot: %d", txn->gt_lastslot);
#include "gtm/libpq-int.h"
#include "gtm/pqformat.h"
+void
+GTM_AdvanceSnapshotCounter(void)
+{
+ GTMTransactions.gt_snapid++;
+}
+
/*
* GTM_GetTransactionSnapshot
* Compute and store snapshot(s) for specified transactions.
Assert(GlobalTransactionIdIsNormal(xmax));
GlobalTransactionIdAdvance(xmax);
+ /* Get the snapshot id */
+ snapshot->sn_snapid = GTMTransactions.gt_snapid;
+
/* initialize xmin calculation with xmax */
globalxmin = xmin = xmax;
pq_sendbytes(&buf, (char *)&gxid, sizeof (GlobalTransactionId));
pq_sendbytes(&buf, (char *)&txn_count, sizeof(txn_count));
pq_sendbytes(&buf, (char *)&status, sizeof(int) * txn_count);
+ pq_sendbytes(&buf, (char *)&snapshot->sn_snapid, sizeof (uint64));
pq_sendbytes(&buf, (char *)&snapshot->sn_xmin, sizeof (GlobalTransactionId));
pq_sendbytes(&buf, (char *)&snapshot->sn_xmax, sizeof (GlobalTransactionId));
}
pq_sendbytes(&buf, (char *)&txn_count, sizeof(txn_count));
pq_sendbytes(&buf, (char *)status, sizeof(int) * txn_count);
+ pq_sendbytes(&buf, (char *)&snapshot->sn_snapid, sizeof (uint64));
pq_sendbytes(&buf, (char *)&snapshot->sn_xmin, sizeof (GlobalTransactionId));
pq_sendbytes(&buf, (char *)&snapshot->sn_xmax, sizeof (GlobalTransactionId));
/* Read once */
GTMTransactions.gt_xidStopLimit = txn.gt_xidStopLimit;
GTMTransactions.gt_xidWrapLimit = txn.gt_xidWrapLimit;
GTMTransactions.gt_latestCompletedXid = txn.gt_latestCompletedXid;
+ GTMTransactions.gt_snapid = txn.gt_snapid;
GTMTransactions.gt_recent_global_xmin = txn.gt_recent_global_xmin;
GTMTransactions.gt_lastslot = txn.gt_lastslot;
* XXX Newest XID that is committed or aborted
*/
GTMTransactions.gt_latestCompletedXid = FirstNormalGlobalTransactionId;
+ GTMTransactions.gt_snapid = 1;
/* Initialise gt_recent_global_xmin */
GTMTransactions.gt_recent_global_xmin = FirstNormalGlobalTransactionId;
*/
GTM_RWLockAcquire(>MTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE);
+ /* Next snapshot will yield a different result. */
+ GTM_AdvanceSnapshotCounter();
+
for (ii = 0; ii < txn_count; ii++)
{
if (gtm_txninfo[ii] == NULL)
*/
GTM_RWLockAcquire(>MTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE);
+ /* Next snapshot will yield a different result. */
+ GTM_AdvanceSnapshotCounter();
+
prev = NULL;
cell = gtm_list_head(GTMTransactions.gt_open_transactions);
while (cell != NULL)
{
GlobalTransactionId saved_gxid = InvalidGlobalTransactionId;
GlobalTransactionId saved_global_xmin = InvalidGlobalTransactionId;
+ uint64 saved_snapid = 1;
if (ctlf)
{
if (fscanf(ctlf, "global_xmin: %u\n", &saved_global_xmin) != 1)
saved_global_xmin = InvalidGlobalTransactionId;
+
+ saved_snapid = 1;
+ }
+ else if (context && context->version == 20181008)
+ {
+ if (fscanf(ctlf, "next_xid: %u\n", &saved_gxid) != 1)
+ saved_gxid = InvalidGlobalTransactionId;
+
+ if (fscanf(ctlf, "global_xmin: %u\n", &saved_global_xmin) != 1)
+ saved_global_xmin = InvalidGlobalTransactionId;
+
+ if (fscanf(ctlf, "snapid: %lu\n", &saved_snapid) != 1)
+ saved_snapid = 1;
}
else
{
" use -f option")));
GTMTransactions.gt_recent_global_xmin = next_gxid;
}
-
+ GTMTransactions.gt_snapid = saved_snapid;
GTM_SetNextGlobalTransactionId(next_gxid);
elog(LOG, "Restoring last GXID to %u\n", next_gxid);
elog(LOG, "Restoring global xmin to %u\n",
fprintf(ctlf, "next_xid: %u\n", next_gxid);
fprintf(ctlf, "global_xmin: %u\n", global_xmin);
+ fprintf(ctlf, "snapid: %lu\n", GTMTransactions.gt_snapid);
}
void
elog(DEBUG1, "Saving transaction restoration info, backed-up gxid: %u", GTMTransactions.gt_backedUpXid);
fprintf(f, "next_xid: %u\n", GTMTransactions.gt_backedUpXid);
fprintf(f, "global_xmin: %u\n", GTMTransactions.gt_backedUpXid);
+ fprintf(f, "snapid: %lu\n", GTMTransactions.gt_snapid);
}
void
pq_sendint(&buf, SNAPSHOT_GET_MULTI_RESULT, 4);
pq_sendbytes(&buf, (char *)&txn_count, sizeof (txn_count));
pq_sendbytes(&buf, (char *)&status, sizeof (status));
+ pq_sendbytes(&buf, (char *)&res->gr_snapshot.sn_snapid, sizeof (uint64));
pq_sendbytes(&buf, (char *)&res->gr_snapshot.sn_xmin, sizeof (GlobalTransactionId));
pq_sendbytes(&buf, (char *)&res->gr_snapshot.sn_xmax, sizeof (GlobalTransactionId));
pq_sendint(&buf, res->gr_snapshot.sn_xcnt, sizeof (int));
((((a) + 1) == UINT32_MAX) ? 1 : ((a) + 1))
#define GTM_CONTROL_FILE "gtm.control"
-#define GTM_CONTROL_VERSION 20160302
+#define GTM_CONTROL_VERSION 20181008
#endif
typedef struct GTM_SnapshotData
{
+ uint64 sn_snapid;
GlobalTransactionId sn_xmin;
GlobalTransactionId sn_xmax;
- uint32 sn_xcnt;
+ uint32 sn_xcnt;
GlobalTransactionId *sn_xip;
} GTM_SnapshotData;
*/
GlobalTransactionId gt_latestCompletedXid; /* newest XID that has committed or
* aborted */
+ uint64 gt_snapid; /* next snapshot id to assign */
GlobalTransactionId gt_recent_global_xmin;
/*
* In gtm_snap.c
*/
+void GTM_AdvanceSnapshotCounter(void);
void ProcessGetSnapshotCommand(Port *myport, StringInfo message, bool get_gxid);
void ProcessGetSnapshotCommandMulti(Port *myport, StringInfo message);
void GTM_RememberDroppedSequence(GlobalTransactionId gxid, void *seq);
#define CLUSTERMON_H
#include "storage/s_lock.h"
+#include "storage/condition_variable.h"
#include "gtm/gtm_c.h"
typedef struct
{
slock_t mutex;
+ ConditionVariable cv;
GlobalTransactionId reported_recent_global_xmin;
GlobalTransactionId reporting_recent_global_xmin;
GlobalTransactionId gtm_recent_global_xmin;
+ pid_t clustermonitor_pid;
+ uint64 gtm_snapid;
+ GlobalTransactionId gtm_xmin;
+ GlobalTransactionId gtm_xmax;
+ int gtm_xcnt;
+ GlobalTransactionId gtm_xip[GTM_MAX_GLOBAL_TRANSACTIONS];
} ClusterMonitorCtlData;
extern void ClusterMonitorShmemInit(void);
extern GlobalTransactionId ClusterMonitorGetGlobalXmin(bool invalid_ok);
extern void ClusterMonitorSetGlobalXmin(GlobalTransactionId xmin);
extern GlobalTransactionId ClusterMonitorGetReportingGlobalXmin(void);
+extern void ClusterMonitorWakeUp(void);
+extern bool ClusterMonitorTransactionIsInProgress(GlobalTransactionId gxid);
+extern void ClusterMonitorWaitForEOFTransaction(GlobalTransactionId gxid);
+extern void ClusterMonitorSyncGlobalStateUsingSnapshot(GTM_Snapshot snapshot);
#ifdef EXEC_BACKEND
extern void ClusterMonitorIAm(void);
extern RunningTransactions GetRunningTransactionData(void);
extern bool TransactionIdIsInProgress(TransactionId xid);
+extern bool TransactionIdIsInProgressExtended(TransactionId xid,
+ bool check_gtm);
extern bool TransactionIdIsActive(TransactionId xid);
extern TransactionId GetOldestXmin(Relation rel, int flags);
extern TransactionId GetOldestXminInternal(Relation rel, int flags,
-- 3. update a row (Should fail)
update mytab1 set val2=33 where val = 1;
+ERROR: canceling statement due to statement timeout
-- 4. delete a row
-- Newly Inserted (Should pass)
delete from mytab1 where val2=456;
-- Previously Inserted (Should fail)
delete from mytab1 where val=1;
+ERROR: canceling statement due to statement timeout
-- 5. inherit form it (Should pass)
create table chld_mytab1(d int, e int) inherits (mytab1);
-- 6. create a view on it (Should pass)
fetch 1 from c1;
val | val2 | val3
-----+------+------
- 2 | 11 | 3344
+ 1 | 11 | 1122
(1 row)
end;
fetch 1 from c1;
val | val2 | val3
-----+------+------
- 2 | 11 | 3344
+ 1 | 11 | 1122
(1 row)
end;
fetch 1 from c1;
val | val2 | val3
-----+------+------
- 2 | 11 | 3344
+ 1 | 11 | 1122
(1 row)
declare c2 cursor for select * from mytab1 for update;
fetch 1 from c2;
val | val2 | val3
-----+------+------
- 2 | 11 | 3344
+ 1 | 11 | 1122
(1 row)
end;
fetch 1 from c1;
val | val2 | val3
-----+------+------
- 2 | 11 | 3344
+ 1 | 11 | 1122
(1 row)
prepare transaction 'tbl_mytab1_locked';
select * from mytab1 order by 1 ;
val | val2 | val3
-----+------+------
+ 1 | 11 | 1122
2 | 11 | 3344
-(1 row)
+(2 rows)
-- 2. insert a row (Should pass)
insert into mytab1 values(123,456);
-- 3. update a row (Should fail)
update mytab1 set val2=33 where val = 1;
+ERROR: canceling statement due to statement timeout
-- 4. delete a row
-- Newly Inserted (Should pass)
delete from mytab1 where val2=456;
-- Previously Inserted (Should fail)
delete from mytab1 where val=1;
+ERROR: canceling statement due to statement timeout
-- 5. inherit form it (Should pass)
create table chld_mytab1(d int, e int) inherits (mytab1);
-- 6. create a view on it (Should pass)
fetch 1 from c1;
val | val2 | val3
-----+------+------
- 2 | 11 | 3344
+ 1 | 11 | 1122
(1 row)
end;
fetch 1 from c1;
val | val2 | val3
-----+------+------
- 2 | 11 | 3344
+ 1 | 11 | 1122
(1 row)
end;