lazyvxid-v2.patch
application/octet-stream
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 7f01a83..f0c80f1 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1725,14 +1725,7 @@ StartTransaction(void)
/*
* Lock the virtual transaction id before we announce it in the proc array
*/
- VirtualXactLockTableInsert(vxid);
-
- /*
- * Advertise it in the proc array. We assume assignment of
- * LocalTransactionID is atomic, and the backendId should be set already.
- */
- Assert(MyProc->backendId == vxid.backendId);
- MyProc->lxid = vxid.localTransactionId;
+ VirtualXactLockInitialize(vxid);
TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
@@ -1878,6 +1871,7 @@ CommitTransaction(void)
* must be done _before_ releasing locks we hold and _after_
* RecordTransactionCommit.
*/
+ VirtualXactLockCleanup();
ProcArrayEndTransaction(MyProc, latestXid);
/*
@@ -2140,6 +2134,7 @@ PrepareTransaction(void)
* done *after* the prepared transaction has been marked valid, else
* someone may think it is unlocked and recyclable.
*/
+ VirtualXactLockCleanup();
ProcArrayClearTransaction(MyProc);
/*
@@ -2306,6 +2301,7 @@ AbortTransaction(void)
* must be done _before_ releasing locks we hold and _after_
* RecordTransactionAbort.
*/
+ VirtualXactLockCleanup();
ProcArrayEndTransaction(MyProc, latestXid);
/*
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b7c021d..a583399 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -482,7 +482,7 @@ DefineIndex(RangeVar *heapRelation,
while (VirtualTransactionIdIsValid(*old_lockholders))
{
- VirtualXactLockTableWait(*old_lockholders);
+ VirtualXactLock(*old_lockholders, true);
old_lockholders++;
}
@@ -568,7 +568,7 @@ DefineIndex(RangeVar *heapRelation,
while (VirtualTransactionIdIsValid(*old_lockholders))
{
- VirtualXactLockTableWait(*old_lockholders);
+ VirtualXactLock(*old_lockholders, true);
old_lockholders++;
}
@@ -665,7 +665,7 @@ DefineIndex(RangeVar *heapRelation,
}
if (VirtualTransactionIdIsValid(old_snapshots[i]))
- VirtualXactLockTableWait(old_snapshots[i]);
+ VirtualXactLock(old_snapshots[i], true);
}
/*
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 483a829..8b233ef 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1992,7 +1992,7 @@ do_autovacuum(void)
backendID = GetTempNamespaceBackendId(classForm->relnamespace);
/* We just ignore it if the owning backend is still active */
- if (backendID == MyBackendId || !BackendIdIsActive(backendID))
+ if (backendID == MyBackendId || BackendIdGetProc(backendID) == NULL)
{
/*
* We found an orphan temp table (which was probably left
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index e7593fa..2174061 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -363,7 +363,6 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
proc->xid = InvalidTransactionId;
- proc->lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
/* must be cleared with xid/xmin: */
proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
@@ -390,7 +389,6 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
*/
Assert(!TransactionIdIsValid(proc->xid));
- proc->lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
/* must be cleared with xid/xmin: */
proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
@@ -421,7 +419,6 @@ ProcArrayClearTransaction(PGPROC *proc)
* ProcArray.
*/
proc->xid = InvalidTransactionId;
- proc->lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
proc->recoveryConflictPending = false;
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index 4f446aa..807b7be 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -139,6 +139,7 @@ typedef struct ProcState
{
/* procPid is zero in an inactive ProcState array entry. */
pid_t procPid; /* PID of backend, for signaling */
+ PGPROC *proc; /* PGPROC of backend */
/* nextMsgNum is meaningless if procPid == 0 or resetState is true. */
int nextMsgNum; /* next message number to read */
bool resetState; /* backend needs to reset its state */
@@ -245,6 +246,7 @@ CreateSharedInvalidationState(void)
for (i = 0; i < shmInvalBuffer->maxBackends; i++)
{
shmInvalBuffer->procState[i].procPid = 0; /* inactive */
+ shmInvalBuffer->procState[i].proc = NULL;
shmInvalBuffer->procState[i].nextMsgNum = 0; /* meaningless */
shmInvalBuffer->procState[i].resetState = false;
shmInvalBuffer->procState[i].signaled = false;
@@ -313,6 +315,7 @@ SharedInvalBackendInit(bool sendOnly)
/* mark myself active, with all extant messages already read */
stateP->procPid = MyProcPid;
+ stateP->proc = MyProc;
stateP->nextMsgNum = segP->maxMsgNum;
stateP->resetState = false;
stateP->signaled = false;
@@ -352,6 +355,7 @@ CleanupInvalidationState(int status, Datum arg)
/* Mark myself inactive */
stateP->procPid = 0;
+ stateP->proc = NULL;
stateP->nextMsgNum = 0;
stateP->resetState = false;
stateP->signaled = false;
@@ -368,13 +372,16 @@ CleanupInvalidationState(int status, Datum arg)
}
/*
- * BackendIdIsActive
- * Test if the given backend ID is currently assigned to a process.
+ * BackendIdGetProc
+ * Get the PGPROC structure for a backend, given the backend ID.
+ * The result may be out of date arbitrarily quickly, so the caller
+ * must be careful about how this information is used. NULL is
+ * returned if the backend is not active.
*/
-bool
-BackendIdIsActive(int backendID)
+PGPROC *
+BackendIdGetProc(int backendID)
{
- bool result;
+ PGPROC *result = NULL;
SISeg *segP = shmInvalBuffer;
/* Need to lock out additions/removals of backends */
@@ -384,10 +391,8 @@ BackendIdIsActive(int backendID)
{
ProcState *stateP = &segP->procState[backendID - 1];
- result = (stateP->procPid != 0);
+ result = stateP->proc;
}
- else
- result = false;
LWLockRelease(SInvalWriteLock);
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 75b5ab4..3456e4a 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -82,7 +82,7 @@ InitRecoveryTransactionEnvironment(void)
*/
vxid.backendId = MyBackendId;
vxid.localTransactionId = GetNextLocalTransactionId();
- VirtualXactLockTableInsert(vxid);
+ VirtualXactLockInitialize(vxid);
standbyState = STANDBY_INITIALIZED;
}
@@ -201,7 +201,7 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
standbyWait_us = STANDBY_INITIAL_WAIT_US;
/* wait until the virtual xid is gone */
- while (!ConditionalVirtualXactLockTableWait(*waitlist))
+ while (!VirtualXactLock(*waitlist, false))
{
/*
* Report via ps if we have been waiting for more than 500 msec
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 859b385..9d0994e 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -514,70 +514,6 @@ ConditionalXactLockTableWait(TransactionId xid)
return true;
}
-
-/*
- * VirtualXactLockTableInsert
- *
- * Insert a lock showing that the given virtual transaction ID is running ---
- * this is done at main transaction start when its VXID is assigned.
- * The lock can then be used to wait for the transaction to finish.
- */
-void
-VirtualXactLockTableInsert(VirtualTransactionId vxid)
-{
- LOCKTAG tag;
-
- Assert(VirtualTransactionIdIsValid(vxid));
-
- SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
- (void) LockAcquire(&tag, ExclusiveLock, false, false);
-}
-
-/*
- * VirtualXactLockTableWait
- *
- * Waits until the lock on the given VXID is released, which shows that
- * the top-level transaction owning the VXID has ended.
- */
-void
-VirtualXactLockTableWait(VirtualTransactionId vxid)
-{
- LOCKTAG tag;
-
- Assert(VirtualTransactionIdIsValid(vxid));
-
- SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
- (void) LockAcquire(&tag, ShareLock, false, false);
-
- LockRelease(&tag, ShareLock, false);
-}
-
-/*
- * ConditionalVirtualXactLockTableWait
- *
- * As above, but only lock if we can get the lock without blocking.
- * Returns TRUE if the lock was acquired.
- */
-bool
-ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid)
-{
- LOCKTAG tag;
-
- Assert(VirtualTransactionIdIsValid(vxid));
-
- SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
- if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
- return false;
-
- LockRelease(&tag, ShareLock, false);
-
- return true;
-}
-
-
/*
* LockDatabaseObject
*
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index e809a58..665f898 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -38,6 +38,7 @@
#include "miscadmin.h"
#include "pg_trace.h"
#include "pgstat.h"
+#include "storage/sinvaladt.h"
#include "storage/standby.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
@@ -138,6 +139,9 @@ static int FastPathLocalUseCount = 0;
#define FAST_PATH_CHECK_LOCKMODE(proc, n, l) \
((proc)->fpLockBits & (UINT64CONST(1) << FAST_PATH_BIT_POSITION(n, l)))
+#define FAST_PATH_DEFER_VXID_LOCK \
+ (UINT64CONST(1) << (FAST_PATH_BITS_PER_SLOT * FP_LOCK_SLOTS_PER_BACKEND))
+
/*
* The fast-path lock mechanism is concerned only with relation locks on
* unshared relations by backends bound to a database. The fast-path
@@ -3531,3 +3535,154 @@ lock_twophase_postabort(TransactionId xid, uint16 info,
{
lock_twophase_postcommit(xid, info, recdata, len);
}
+
+/*
+ * VirtualXactLockInitialize
+ *
+ * We set a flag in MyProc->fpLockState indicating that we have a
+ * "deferred" VXID lock. That is, we have an active VXID, but we
+ * haven't actually taken an exclusive lock on it. VXID locks are
+ * rarely waited for, so it makes sense to defer the actual lock
+ * acquisition to the point when it's needed. Another backend wishing
+ * to wait on the lock can acquire the lock on our behalf and then
+ * wait on it. We'll figure it all out in VirtualXactLockCleanup().
+ *
+ * We set lxid while holding the lock to guarantee that anyone who
+ * sees the lxid set and subsequently takes our backendLock will also
+ * see the FAST_PATH_DEFER_VXID lock bit set.
+ */
+void
+VirtualXactLockInitialize(VirtualTransactionId vxid)
+{
+ Assert(VirtualTransactionIdIsValid(vxid));
+
+ LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+
+ Assert(MyProc->backendId == vxid.backendId);
+ MyProc->lxid = vxid.localTransactionId;
+ MyProc->fpLockBits |= FAST_PATH_DEFER_VXID_LOCK;
+
+ LWLockRelease(MyProc->backendLock);
+}
+
+/*
+ * VirtualXactLockCleanup
+ *
+ * Check whether a VXID lock has been materialized; if so, release it,
+ * unblocking waiters.
+ */
+void
+VirtualXactLockCleanup()
+{
+ VirtualTransactionId vxid;
+ bool cleanup = false;
+
+ Assert(MyProc->backendId != InvalidBackendId);
+ Assert(MyProc->lxid != InvalidLocalTransactionId);
+
+ GET_VXID_FROM_PGPROC(vxid, *MyProc);
+
+ LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+
+ if ((MyProc->fpLockBits & FAST_PATH_DEFER_VXID_LOCK) != 0)
+ MyProc->fpLockBits &= ~FAST_PATH_DEFER_VXID_LOCK;
+ else
+ cleanup = true;
+ MyProc->lxid = InvalidLocalTransactionId;
+
+ LWLockRelease(MyProc->backendLock);
+
+ /* If someone materialized the lock on our behalf, we must release it. */
+ if (cleanup)
+ {
+ LOCKTAG locktag;
+
+ SET_LOCKTAG_VIRTUALTRANSACTION(locktag, vxid);
+ LockRefindAndRelease(LockMethods[DEFAULT_LOCKMETHOD], MyProc,
+ &locktag, ExclusiveLock, false);
+ }
+}
+
+/*
+ * VirtualXactLock
+ *
+ * If wait = true, wait until the given VXID has been released, and then
+ * return true.
+ *
+ * If wait = false, just check whether the VXID is still running, and return
+ * true or false.
+ */
+bool
+VirtualXactLock(VirtualTransactionId vxid, bool wait)
+{
+ LOCKTAG tag;
+ PGPROC *proc;
+
+ Assert(VirtualTransactionIdIsValid(vxid));
+
+ SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
+
+ /*
+ * If a lock table entry must be made, this is the PGPROC on whose behalf
+ * it must be done. Note that the transaction might end or the PGPROC
+ * might be reassigned to a new backend before we get around to examining
+ * it, but it doesn't matter. If we find upon examination that the
+ * relevant lxid is no longer running here, that's enough to prove that
+ * it's no longer running anywhere.
+ */
+ proc = BackendIdGetProc(vxid.backendId);
+
+ /*
+ * We must acquire this lock before checking the backendId and lxid
+ * against the ones we're waiting for. The target backend will only
+ * set or clear lxid while holding this lock.
+ */
+ LWLockAcquire(proc->backendLock, LW_EXCLUSIVE);
+
+ /* If the transaction has ended, our work here is done. */
+ if (proc->backendId != vxid.backendId || proc->lxid != vxid.localTransactionId)
+ {
+ LWLockRelease(proc->backendLock);
+ return true;
+ }
+
+ /*
+ * If we aren't asked to wait, there's no need to set up a lock table
+ * entry. The transaction is still in progress, so just return false.
+ */
+ if (!wait)
+ {
+ LWLockRelease(proc->backendLock);
+ return false;
+ }
+
+ /*
+ * OK, we're going to need to sleep on the VXID. But first, we must set
+ * up the primary lock table entry, if needed.
+ */
+ if ((proc->fpLockBits & FAST_PATH_DEFER_VXID_LOCK) != 0)
+ {
+ PROCLOCK *proclock;
+ uint32 hashcode;
+
+ hashcode = LockTagHashCode(&tag);
+ proclock = SetupLockInTable(LockMethods[DEFAULT_LOCKMETHOD], proc,
+ &tag, hashcode, ExclusiveLock);
+ if (!proclock)
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of shared memory"),
+ errhint("You might need to increase max_locks_per_transaction.")));
+ GrantLock(proclock->tag.myLock, proclock, ExclusiveLock);
+ proc->fpLockBits &= ~FAST_PATH_DEFER_VXID_LOCK;
+ }
+
+ /* Done with proc->fpLockBits */
+ LWLockRelease(proc->backendLock);
+
+ /* Time to wait. */
+ (void) LockAcquire(&tag, ShareLock, false, false);
+
+ LockRelease(&tag, ShareLock, false);
+ return true;
+}
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index bd44d92..340f6a3 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -56,11 +56,6 @@ extern void XactLockTableDelete(TransactionId xid);
extern void XactLockTableWait(TransactionId xid);
extern bool ConditionalXactLockTableWait(TransactionId xid);
-/* Lock a VXID (used to wait for a transaction to finish) */
-extern void VirtualXactLockTableInsert(VirtualTransactionId vxid);
-extern void VirtualXactLockTableWait(VirtualTransactionId vxid);
-extern bool ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid);
-
/* Lock a general object (other than a relation) of the current database */
extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index b3aeef9..eb6b953 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -543,4 +543,9 @@ extern void DumpLocks(PGPROC *proc);
extern void DumpAllLocks(void);
#endif
+/* Lock a VXID (used to wait for a transaction to finish) */
+extern void VirtualXactLockInitialize(VirtualTransactionId vxid);
+extern void VirtualXactLockCleanup(void);
+extern bool VirtualXactLock(VirtualTransactionId vxid, bool wait);
+
#endif /* LOCK_H */
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index c703558..a61d696 100644
--- a/src/include/storage/sinvaladt.h
+++ b/src/include/storage/sinvaladt.h
@@ -22,6 +22,7 @@
#ifndef SINVALADT_H
#define SINVALADT_H
+#include "storage/proc.h"
#include "storage/sinval.h"
/*
@@ -30,7 +31,7 @@
extern Size SInvalShmemSize(void);
extern void CreateSharedInvalidationState(void);
extern void SharedInvalBackendInit(bool sendOnly);
-extern bool BackendIdIsActive(int backendID);
+extern PGPROC *BackendIdGetProc(int backendID);
extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n);
extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize);