#include "miscadmin.h"
#include "storage/procarray.h"
#include "storage/spin.h"
-#include "storage/snaparray.h" /* XXX: might not be needed, by the end */
+#include "storage/snaparray.h"
#include "utils/builtins.h"
#include "utils/snapmgr.h"
static void KnownAssignedXidsRemoveTree(TransactionId xid, int nsubxids,
TransactionId *subxids);
static void KnownAssignedXidsRemovePreceding(TransactionId xid);
-static TransactionId KnownAssignedXidsGetOldestXmin(void);
static void KnownAssignedXidsDisplay(int trace_level);
/*
Assert(TransactionIdIsValid(proc->xid));
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
- LWLockAcquire(SnapArrayLock, LW_EXCLUSIVE); /* XXX: FIXME */
proc->xid = InvalidTransactionId;
proc->lxid = InvalidLocalTransactionId;
latestXid))
ShmemVariableCache->latestCompletedXid = latestXid;
- LWLockRelease(SnapArrayLock);
LWLockRelease(ProcArrayLock);
}
else
/* Cannot look for individual databases during recovery */
Assert(allDbs || !RecoveryInProgress());
- LWLockAcquire(ProcArrayLock, LW_SHARED);
-
/*
- * We initialize the MIN() calculation with latestCompletedXid + 1. This
- * is a lower bound for the XIDs that might appear in the ProcArray later,
- * and so protects us against overestimating the result due to future
- * additions.
+ * Initialize the MIN() calculation. This is a lower bound for the XIDs
+ * that might appear in the ProcArray later, and so protects us against
+ * overestimating the result due to future additions.
*/
- result = ShmemVariableCache->latestCompletedXid;
+ result = SnapArraySetFreshXmin();
Assert(TransactionIdIsNormal(result));
- TransactionIdAdvance(result);
+
+ LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
proc->databaseId == MyDatabaseId ||
proc->databaseId == 0) /* include WalSender */
{
- /* Fetch xid just once - see GetNewTransactionId */
- TransactionId xid = proc->xid;
+ /* Fetch xmin just once - see GetNewTransactionId */
+ TransactionId xmin = proc->xmin;
- /* First consider the transaction's own Xid, if any */
- if (TransactionIdIsNormal(xid) &&
- TransactionIdPrecedes(xid, result))
- result = xid;
+ if (TransactionIdIsNormal(xmin) &&
+ TransactionIdPrecedes(xmin, result))
+ result = xmin;
- /*
- * Also consider the transaction's Xmin, if set.
- *
- * We must check both Xid and Xmin because a transaction might
- * have an Xmin but not (yet) an Xid; conversely, if it has an
- * Xid, that could determine some not-yet-set Xmin.
- */
- xid = proc->xmin; /* Fetch just once */
- if (TransactionIdIsNormal(xid) &&
- TransactionIdPrecedes(xid, result))
- result = xid;
}
}
- if (RecoveryInProgress())
- {
- /*
- * Check to see whether KnownAssignedXids contains an xid value older
- * than the main procarray.
- */
- TransactionId kaxmin = KnownAssignedXidsGetOldestXmin();
-
- LWLockRelease(ProcArrayLock);
+ LWLockRelease(ProcArrayLock);
- if (TransactionIdIsNormal(kaxmin) &&
- TransactionIdPrecedes(kaxmin, result))
- result = kaxmin;
- }
- else
+ if (!RecoveryInProgress())
{
- /*
- * No other information needed, so release the lock immediately.
- */
- LWLockRelease(ProcArrayLock);
-
/*
* Compute the cutoff XID, being careful not to generate a "permanent"
* XID. We need do this only on the primary, never on standby.
result = FirstNormalTransactionId;
}
+ /* Adjust, and possibly publish, new value. */
+ result = SnapArrayAdjustGlobalXmin(result, allDbs && !ignoreVacuum);
+
return result;
}
KnownAssignedXidsCompress(false);
}
-/*
- * Get oldest XID in the KnownAssignedXids array, or InvalidTransactionId
- * if nothing there.
- */
-static TransactionId
-KnownAssignedXidsGetOldestXmin(void)
-{
- /* use volatile pointer to prevent code rearrangement */
- volatile ProcArrayStruct *pArray = procArray;
- int head,
- tail;
- int i;
-
- /*
- * Fetch head just once, since it may change while we loop.
- */
- SpinLockAcquire(&pArray->known_assigned_xids_lck);
- tail = pArray->tailKnownAssignedXids;
- head = pArray->headKnownAssignedXids;
- SpinLockRelease(&pArray->known_assigned_xids_lck);
-
- for (i = tail; i < head; i++)
- {
- /* Skip any gaps in the array */
- if (KnownAssignedXidsValid[i])
- return KnownAssignedXids[i];
- }
-
- return InvalidTransactionId;
-}
-
/*
* Display KnownAssignedXids to provide debug trail
*
* that we read a slightly out-of-date, older value. That's acceptable.
*/
RecentXmin = xmin;
- RecentGlobalXmin = SnapArray->global_xmin - vacuum_defer_cleanup_age;
+ RecentGlobalXmin = SnapArray->global_xmin;
if (!TransactionIdIsNormal(RecentGlobalXmin))
RecentGlobalXmin = FirstNormalTransactionId;
return snapshot;
}
+/*
+ * Prepare for global xmin calculation.
+ *
+ * This function will return a xmin which must be folded into the caller's
+ * global xmin calculation; that is, the caller must not compute a global xmin
+ * which precedes the return value of this function. In turn, we guarantee
+ * that subsequently dervied snapshots will have an xmin greater than or equal
+ * to the value returned by this function.
+ *
+ * It's possible for xmin values preceding the return value to appear in
+ * ProcArray transiently. However, those snapshots won't be used: we'll
+ * automatically recompute them before returning to the caller.
+ */
+TransactionId
+SnapArraySetFreshXmin(void)
+{
+ TransactionId xmin;
+
+ /* Get latest information from shared memory. */
+ if (!SnapArrayUpdateCache(false))
+ {
+ LWLockAcquire(SnapArrayLock, LW_SHARED);
+ SnapArrayUpdateCache(true);
+ LWLockRelease(SnapArrayLock);
+ }
+
+ /* Compute xmin. */
+ if (SnapArrayCache.buffer[3] == 0)
+ xmin = SnapArrayCache.buffer[1];
+ else
+ xmin = SnapArrayCache.buffer[SNAPARRAY_SUMMARY_ITEMS];
+
+ /* Advance fresh_xmin. */
+ SpinLockAcquire(&SnapArray->misc_mutex);
+ if (TransactionIdPrecedes(xmin, SnapArray->fresh_xmin))
+ SnapArray->fresh_xmin = xmin;
+ else
+ xmin = SnapArray->fresh_xmin;
+ SpinLockRelease(&SnapArray->misc_mutex);
+
+ return xmin;
+}
+
+/*
+ * Final adjustments after computing a global xmin.
+ *
+ * We don't want the global xmin to ever move backwards, so we check whether
+ * some other backend has already published a newer global xmin. If so, we
+ * return that value to the caller, who should use it in lieu of the computed
+ * value. If not, we return the caller-supplied value.
+ *
+ * In some cases, the caller may not have included all backends in the global
+ * xmin calculation (e.g. because we're just looking for the minimum across
+ * a single DB). If the caller has included all backends in the calculation,
+ * they should set publish = true; if they do, we'll advance the cached global
+ * xmin to the value provided.
+ */
+TransactionId
+SnapArrayAdjustGlobalXmin(TransactionId global_xmin, bool publish)
+{
+ SpinLockAcquire(&SnapArray->misc_mutex);
+ if (TransactionIdPrecedes(global_xmin, SnapArray->global_xmin))
+ global_xmin = SnapArray->global_xmin;
+ else if (publish &&
+ TransactionIdPrecedes(SnapArray->global_xmin, global_xmin))
+ SnapArray->global_xmin = global_xmin;
+ SpinLockRelease(&SnapArray->misc_mutex);
+
+ return global_xmin;
+}
+
/*
* TransactionIdIsInProgress -- is given transaction running in some backend
*