At 2013-07-13 14:19:23 +0530, a...@2ndquadrant.com wrote: > > The timings above are with both xid_in_snapshot_cache.v1.patch and > cache_TransactionIdInProgress.v2.patch applied
For anyone who wants to try to reproduce the results, here's the patch I used, which is both patches above plus some typo fixes in comments. -- Abhijit
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index c2f86ff..660fab0 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -840,6 +840,9 @@ TransactionIdIsInProgress(TransactionId xid) TransactionId topxid; int i, j; + static int pgprocno = -1; /* cached last 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 @@ -875,6 +878,60 @@ 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 && pgprocno >= 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); + + pxid = cxid = InvalidTransactionId; + 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 will be reset to something useful later, if approproate. + */ + cxid = InvalidTransactionId; + + /* * If first time through, get workspace to remember main XIDs in. We * malloc it permanently to avoid repeated palloc/pfree overhead. */ @@ -910,10 +967,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) @@ -948,7 +1007,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)) { @@ -975,6 +1034,14 @@ TransactionIdIsInProgress(TransactionId xid) */ if (RecoveryInProgress()) { + /* + * Hot Standby doesn't use pgprocno, so we can clear the cache. + * + * XXX we could try to remember the offset into the KnownAssignedXids + * array, which is a possibility for another day. + */ + pxid = cxid = InvalidTransactionId; + /* none of the PGXACT entries should have XIDs in hot standby mode */ Assert(nxids == 0); @@ -999,6 +1066,12 @@ TransactionIdIsInProgress(TransactionId xid) LWLockRelease(ProcArrayLock); /* + * After this point we don't remember pgprocno, so the cache is + * no use to us anymore. + */ + pxid = cxid = InvalidTransactionId; + + /* * If none of the relevant caches overflowed, we know the Xid is not * running without even looking at pg_subtrans. */ @@ -1509,6 +1582,9 @@ GetSnapshotData(Snapshot snapshot) snapshot->curcid = GetCurrentCommandId(false); + /* Initialise the single xid cache for this snapshot */ + snapshot->xid_in_snapshot = InvalidTransactionId; + /* * This is a new snapshot, so set both refcounts are zero, and mark it as * not copied in persistent memory. diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 55563ea..dc4bf54 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -1533,6 +1533,25 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) return true; /* + * If we've seen this xid last time then we use our cached knowledge + * to allow a fast path out. + * + * When XidInMVCCSnapshot is called repeatedly in a transaction it + * is usually because we're viewing the results of large transactions. + * Typically there will be just one big concurrent transaction and so + * it's very fast to just remember that and be done. Lots of short + * transactions don't cause problems because their xids quickly go + * above the snapshot's xmax and get ignored before we get here. + * + * XXX If there were more big concurrent transactions then it would make + * sense to remember a list of xids in an LRU scheme. For long lists it + * would make better sense to sort the snapshot xids. Both of those + * ideas have much more complex code and diminishing returns. + */ + if (TransactionIdEquals(xid, snapshot->xid_in_snapshot)) + return true; + + /* * Snapshot information is stored slightly differently in snapshots taken * during recovery. */ @@ -1553,7 +1572,10 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) for (j = 0; j < snapshot->subxcnt; j++) { if (TransactionIdEquals(xid, snapshot->subxip[j])) + { + snapshot->xid_in_snapshot = xid; return true; + } } /* not there, fall through to search xip[] */ @@ -1575,7 +1597,10 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) for (i = 0; i < snapshot->xcnt; i++) { if (TransactionIdEquals(xid, snapshot->xip[i])) + { + snapshot->xid_in_snapshot = xid; return true; + } } } else @@ -1611,7 +1636,10 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) for (j = 0; j < snapshot->subxcnt; j++) { if (TransactionIdEquals(xid, snapshot->subxip[j])) + { + snapshot->xid_in_snapshot = xid; return true; + } } } diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h index e747191..605895e 100644 --- a/src/include/utils/snapshot.h +++ b/src/include/utils/snapshot.h @@ -56,6 +56,12 @@ typedef struct SnapshotData bool copied; /* false if it's a static snapshot */ /* + * Single transaction cache. Keep track of common xid so we can respond + * quickly when we keep seeing an xid while we use this snapshot. + */ + TransactionId xid_in_snapshot; + + /* * note: all ids in subxip[] are >= xmin, but we don't bother filtering * out any that are >= xmax */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers