diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 4308128..4599f3d 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -796,9 +796,11 @@ TransactionIdIsInProgress(TransactionId xid) static TransactionId *xids = NULL; int nxids = 0; ProcArrayStruct *arrayP = procArray; - TransactionId topxid; int i, j; + static int pgprocno; /* cached pgprocno */ + static TransactionId pxid; /* cached last parent xid */ + static TransactionId cxid; /* cached last child xid */ /* * Don't bother checking a transaction older than RecentXmin; it could not @@ -834,6 +836,64 @@ TransactionIdIsInProgress(TransactionId xid) } /* + * Check to see if we have cached the pgprocno for the xid we seek. + * We will have cached either pxid only or both pxid and cxid. + * So we check to see whether pxid or cxid matches the xid we seek, + * but then re-check just the parent pxid. If the PGXACT doesn't + * match then the transaction must be complete because an xid is + * only associated with one PGPROC/PGXACT during its lifetime + * except when we are using prepared transactions. + * Transaction wraparound is not a concern because we are checking + * the status of an xid we see in data and that might be running; + * anything very old will already be deleted or frozen. So stale + * cached values for pxid and cxid don't affect the correctness + * of the result. + */ + if (max_prepared_xacts == 0 && + (TransactionIdEquals(xid, pxid) || TransactionIdEquals(xid, cxid))) + { + volatile PGXACT *pgxact; + + pgxact = &allPgXact[pgprocno]; + + pxid = pgxact->xid; + + /* + * XXX Can we test without the lock first? If the values don't + * match without the lock they never will match... + */ + + /* + * xid matches, so wait for the lock and re-check. + */ + LWLockAcquire(ProcArrayLock, LW_SHARED); + + pxid = pgxact->xid; + + if (TransactionIdIsValid(pxid) && TransactionIdEquals(pxid, xid)) + { + LWLockRelease(ProcArrayLock); + return true; + } + + LWLockRelease(ProcArrayLock); + + /* + * xid is not in progress. Next time through we should hit + * TransactionIdKnownComplete() rather than get to here, but + * no need to invalidate the cache since we are still cacheing + * the pgprocno which was previously used by the xid. + */ + return false; + } + + /* + * Our cache didn't match, so zero the cxid so that when we reset pxid + * we don't become confused that the cxid and pxid still relate. + */ + cxid = InvalidTransactionId; + + /* * If first time through, get workspace to remember main XIDs in. We * malloc it permanently to avoid repeated palloc/pfree overhead. */ @@ -869,10 +929,12 @@ TransactionIdIsInProgress(TransactionId xid) /* No shortcuts, gotta grovel through the array */ for (i = 0; i < arrayP->numProcs; i++) { - int pgprocno = arrayP->pgprocnos[i]; - volatile PGPROC *proc = &allProcs[pgprocno]; - volatile PGXACT *pgxact = &allPgXact[pgprocno]; - TransactionId pxid; + volatile PGPROC *proc; + volatile PGXACT *pgxact; + + pgprocno = arrayP->pgprocnos[i]; + proc = &allProcs[pgprocno]; + pgxact = &allPgXact[pgprocno]; /* Ignore my own proc --- dealt with it above */ if (proc == MyProc) @@ -907,7 +969,7 @@ TransactionIdIsInProgress(TransactionId xid) for (j = pgxact->nxids - 1; j >= 0; j--) { /* Fetch xid just once - see GetNewTransactionId */ - TransactionId cxid = proc->subxids.xids[j]; + cxid = proc->subxids.xids[j]; if (TransactionIdEquals(cxid, xid)) { @@ -985,13 +1047,14 @@ TransactionIdIsInProgress(TransactionId xid) * is still running (or, more precisely, whether it was running when we * held ProcArrayLock). */ - topxid = SubTransGetTopmostTransaction(xid); - Assert(TransactionIdIsValid(topxid)); - if (!TransactionIdEquals(topxid, xid)) + pxid = SubTransGetTopmostTransaction(xid); + cxid = xid; + Assert(TransactionIdIsValid(pxid)); + if (!TransactionIdEquals(pxid, xid)) { for (i = 0; i < nxids; i++) { - if (TransactionIdEquals(xids[i], topxid)) + if (TransactionIdEquals(xids[i], pxid)) return true; } }