Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1Uxvs8-00073O-VI for pgsql-hackers@arkaria.postgresql.org; Sat, 13 Jul 2013 09:12:09 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.80) (envelope-from ) id 1Uxvs8-0003IJ-C5 for pgsql-hackers@arkaria.postgresql.org; Sat, 13 Jul 2013 09:12:08 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1Uxvs6-0003IC-Tk for pgsql-hackers@postgresql.org; Sat, 13 Jul 2013 09:12:06 +0000 Received: from raven.toroid.org ([178.63.84.200]) by magus.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1Uxvrz-0006Ok-KC for pgsql-hackers@postgresql.org; Sat, 13 Jul 2013 09:12:06 +0000 Received: from pipit.toroid.org (unknown [10.9.8.10]) by raven.toroid.org (Postfix) with ESMTP id AF95BE500B6; Sat, 13 Jul 2013 11:11:58 +0200 (CEST) Received: by pipit.toroid.org (Postfix, from userid 1000) id D6660460269; Sat, 13 Jul 2013 14:41:57 +0530 (IST) Date: Sat, 13 Jul 2013 14:41:57 +0530 From: Abhijit Menon-Sen To: Jeff Janes Cc: Josh Berkus , Simon Riggs , Mark Kirkwood , Tom Lane , Gavin Flower , pgsql-hackers@postgresql.org Subject: Re: [PERFORM] In progress INSERT wrecks plans on table Message-ID: <20130713091157.GB1011@toroid.org> References: <51870CB6.6090404@catalyst.net.nz> <20130624044350.GA32322@toroid.org> <51DAF2C8.6010705@agliodbs.com> <51DD9026.2030107@agliodbs.com> <20130711050958.GA23755@toroid.org> <20130713084923.GA1011@toroid.org> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="tThc/1wpZn/ma/RB" Content-Disposition: inline In-Reply-To: <20130713084923.GA1011@toroid.org> X-Pg-Spam-Score: -1.9 (-) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-hackers Precedence: bulk Sender: pgsql-hackers-owner@postgresql.org --tThc/1wpZn/ma/RB Content-Type: text/plain; charset=us-ascii Content-Disposition: inline At 2013-07-13 14:19:23 +0530, ams@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 --tThc/1wpZn/ma/RB Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="inprogress.diff" 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 */ --tThc/1wpZn/ma/RB Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --tThc/1wpZn/ma/RB--