]> granicus.if.org Git - postgresql/commitdiff
Replace the former method of determining snapshot xmax --- to wit, calling
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 8 Sep 2007 20:31:15 +0000 (20:31 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 8 Sep 2007 20:31:15 +0000 (20:31 +0000)
ReadNewTransactionId from GetSnapshotData --- with a "latestCompletedXid"
variable that is updated during transaction commit or abort.  Since
latestCompletedXid is written only in places that had to lock ProcArrayLock
exclusively anyway, and is read only in places that had to lock ProcArrayLock
shared anyway, it adds no new locking requirements to the system despite being
cluster-wide.  Moreover, removing ReadNewTransactionId from snapshot
acquisition eliminates the need to take both XidGenLock and ProcArrayLock at
the same time.  Since XidGenLock is sometimes held across I/O this can be a
significant win.  Some preliminary benchmarking suggested that this patch has
no effect on average throughput but can significantly improve the worst-case
transaction times seen in pgbench.  Concept by Florian Pflug, implementation
by Tom Lane.

14 files changed:
src/backend/access/transam/README
src/backend/access/transam/transam.c
src/backend/access/transam/twophase.c
src/backend/access/transam/varsup.c
src/backend/access/transam/xact.c
src/backend/access/transam/xlog.c
src/backend/catalog/heap.c
src/backend/commands/vacuum.c
src/backend/storage/ipc/procarray.c
src/backend/storage/lmgr/proc.c
src/backend/utils/time/tqual.c
src/include/access/transam.h
src/include/access/xact.h
src/include/storage/procarray.h

index 7c69e09cb5db8e8943be7b8f635e11154fc58b76..f0f27af1c39c07eed96b9f0ddd4a8e4451bbbf1b 100644 (file)
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.8 2007/09/07 20:59:26 tgl Exp $
+$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.9 2007/09/08 20:31:14 tgl Exp $
 
 The Transaction System
 ----------------------
@@ -238,8 +238,10 @@ reason why this would be bad is that C would see (in the row inserted by A)
 earlier changes by B, and it would be inconsistent for C not to see any
 of B's changes elsewhere in the database.
 
-Formally, the correctness requirement is "if A sees B as committed,
-and B sees C as committed, then A must see C as committed".
+Formally, the correctness requirement is "if a snapshot A considers
+transaction X as committed, and any of transaction X's snapshots considered
+transaction Y as committed, then snapshot A must consider transaction Y as
+committed".
 
 What we actually enforce is strict serialization of commits and rollbacks
 with snapshot-taking: we do not allow any transaction to exit the set of
@@ -248,42 +250,45 @@ stronger than necessary for consistency, but is relatively simple to
 enforce, and it assists with some other issues as explained below.)  The
 implementation of this is that GetSnapshotData takes the ProcArrayLock in
 shared mode (so that multiple backends can take snapshots in parallel),
-but xact.c must take the ProcArrayLock in exclusive mode while clearing
-MyProc->xid at transaction end (either commit or abort).
+but ProcArrayEndTransaction must take the ProcArrayLock in exclusive mode
+while clearing MyProc->xid at transaction end (either commit or abort).
 
-GetSnapshotData must in fact acquire ProcArrayLock before it calls
-ReadNewTransactionId.  Otherwise it would be possible for a transaction A
-postdating the xmax to commit, and then an existing transaction B that saw
-A as committed to commit, before GetSnapshotData is able to acquire
-ProcArrayLock and finish taking its snapshot.  This would violate the
-consistency requirement, because A would be still running and B not
-according to this snapshot.
+ProcArrayEndTransaction also holds the lock while advancing the shared
+latestCompletedXid variable.  This allows GetSnapshotData to use
+latestCompletedXid + 1 as xmax for its snapshot: there can be no
+transaction >= this xid value that the snapshot needs to consider as
+completed.
 
 In short, then, the rule is that no transaction may exit the set of
-currently-running transactions between the time we fetch xmax and the time
-we finish building our snapshot.  However, this restriction only applies
-to transactions that have an XID --- read-only transactions can end without
-acquiring ProcArrayLock, since they don't affect anyone else's snapshot.
+currently-running transactions between the time we fetch latestCompletedXid
+and the time we finish building our snapshot.  However, this restriction
+only applies to transactions that have an XID --- read-only transactions
+can end without acquiring ProcArrayLock, since they don't affect anyone
+else's snapshot nor latestCompletedXid.
 
 Transaction start, per se, doesn't have any interlocking with these
 considerations, since we no longer assign an XID immediately at transaction
-start.  But when we do decide to allocate an XID, we must require
-GetNewTransactionId to store the new XID into the shared ProcArray before
-releasing XidGenLock.  This ensures that when GetSnapshotData calls
-ReadNewTransactionId (which also takes XidGenLock), all active XIDs before
-the returned value of nextXid are already present in the ProcArray and
-can't be missed by GetSnapshotData.  Unfortunately, we can't have
-GetNewTransactionId take ProcArrayLock to do this, else it could deadlock
-against GetSnapshotData.  Therefore, we simply let GetNewTransactionId
-store into MyProc->xid without any lock.  We are thereby relying on
-fetch/store of an XID to be atomic, else other backends might see a
-partially-set XID.  (NOTE: for multiprocessors that need explicit memory
-access fence instructions, this means that acquiring/releasing XidGenLock
-is just as necessary as acquiring/releasing ProcArrayLock for
-GetSnapshotData to ensure it sees up-to-date xid fields.)  This also means
-that readers of the ProcArray xid fields must be careful to fetch a value
-only once, rather than assume they can read it multiple times and get the
-same answer each time.
+start.  But when we do decide to allocate an XID, GetNewTransactionId must
+store the new XID into the shared ProcArray before releasing XidGenLock.
+This ensures that all top-level XIDs <= latestCompletedXid are either
+present in the ProcArray, or not running anymore.  (This guarantee doesn't
+apply to subtransaction XIDs, because of the possibility that there's not
+room for them in the subxid array; instead we guarantee that they are
+present or the overflow flag is set.)  If a backend released XidGenLock
+before storing its XID into MyProc, then it would be possible for another
+backend to allocate and commit a later XID, causing latestCompletedXid to
+pass the first backend's XID, before that value became visible in the
+ProcArray.  That would break GetOldestXmin, as discussed below.
+
+We allow GetNewTransactionId to store the XID into MyProc->xid (or the
+subxid array) without taking ProcArrayLock.  This was once necessary to
+avoid deadlock; while that is no longer the case, it's still beneficial for
+performance.  We are thereby relying on fetch/store of an XID to be atomic,
+else other backends might see a partially-set XID.  This also means that
+readers of the ProcArray xid fields must be careful to fetch a value only
+once, rather than assume they can read it multiple times and get the same
+answer each time.  (Use volatile-qualified pointers when doing this, to
+ensure that the C compiler does exactly what you tell it to.)
 
 Another important activity that uses the shared ProcArray is GetOldestXmin,
 which must determine a lower bound for the oldest xmin of any active MVCC
@@ -303,12 +308,10 @@ currently-active XIDs: no xact, in particular not the oldest, can exit
 while we hold shared ProcArrayLock.  So GetOldestXmin's view of the minimum
 active XID will be the same as that of any concurrent GetSnapshotData, and
 so it can't produce an overestimate.  If there is no active transaction at
-all, GetOldestXmin returns the result of ReadNewTransactionId.  Note that
-two concurrent executions of GetOldestXmin might not see the same result
-from ReadNewTransactionId --- but if there is a difference, the intervening
-execution(s) of GetNewTransactionId must have stored their XIDs into the
-ProcArray, so the later execution of GetOldestXmin will see them and
-compute the same global xmin anyway.
+all, GetOldestXmin returns latestCompletedXid + 1, which is a lower bound
+for the xmin that might be computed by concurrent or later GetSnapshotData
+calls.  (We know that no XID less than this could be about to appear in
+the ProcArray, because of the XidGenLock interlock discussed above.)
 
 GetSnapshotData also performs an oldest-xmin calculation (which had better
 match GetOldestXmin's) and stores that into RecentGlobalXmin, which is used
index 3466b50ef24ac4bc2d4b9eb642161e96a5098b46..e53b05e04d5bd2fbaa97fc595a359c2b90f4d17d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.70 2007/08/01 22:45:07 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.71 2007/09/08 20:31:14 tgl Exp $
  *
  * NOTES
  *       This file contains the high level access-method interface to the
@@ -432,6 +432,33 @@ TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
        return (diff >= 0);
 }
 
+
+/*
+ * TransactionIdLatest --- get latest XID among a main xact and its children
+ */
+TransactionId
+TransactionIdLatest(TransactionId mainxid,
+                                       int nxids, const TransactionId *xids)
+{
+       TransactionId   result;
+
+       /*
+        * In practice it is highly likely that the xids[] array is sorted, and
+        * so we could save some cycles by just taking the last child XID, but
+        * this probably isn't so performance-critical that it's worth depending
+        * on that assumption.  But just to show we're not totally stupid, scan
+        * the array back-to-front to avoid useless assignments.
+        */
+       result = mainxid;
+       while (--nxids >= 0)
+       {
+               if (TransactionIdPrecedes(result, xids[nxids]))
+                       result = xids[nxids];
+       }
+       return result;
+}
+
+
 /*
  * TransactionIdGetCommitLSN
  *
index db47e95cf849a97bdf37492409d1efdce2f9b8cb..03ac9e98e454d3e40310ca1c5fef5272c5e3a6dc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *             $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.34 2007/09/05 20:53:17 tgl Exp $
+ *             $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.35 2007/09/08 20:31:14 tgl Exp $
  *
  * NOTES
  *             Each global transaction is associated with a global transaction
@@ -1127,6 +1127,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
        char       *buf;
        char       *bufptr;
        TwoPhaseFileHeader *hdr;
+       TransactionId latestXid;
        TransactionId *children;
        RelFileNode *commitrels;
        RelFileNode *abortrels;
@@ -1162,6 +1163,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
        abortrels = (RelFileNode *) bufptr;
        bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode));
 
+       /* compute latestXid among all children */
+       latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
+
        /*
         * The order of operations here is critical: make the XLOG entry for
         * commit or abort, then mark the transaction committed or aborted in
@@ -1179,7 +1183,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
                                                                           hdr->nsubxacts, children,
                                                                           hdr->nabortrels, abortrels);
 
-       ProcArrayRemove(&gxact->proc);
+       ProcArrayRemove(&gxact->proc, latestXid);
 
        /*
         * In case we fail while running the callbacks, mark the gxact invalid so
index c9754581ec71689def586b6552f405e473d11e8a..14332c6ab26496ed223e3178e55295edd7d30e4b 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2000-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.78 2007/02/15 23:23:22 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.79 2007/09/08 20:31:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,7 +31,9 @@ VariableCache ShmemVariableCache = NULL;
 
 
 /*
- * Allocate the next XID for my new transaction.
+ * Allocate the next XID for my new transaction or subtransaction.
+ *
+ * The new XID is also stored into MyProc before returning.
  */
 TransactionId
 GetNewTransactionId(bool isSubXact)
@@ -43,7 +45,11 @@ GetNewTransactionId(bool isSubXact)
         * transaction id.
         */
        if (IsBootstrapProcessingMode())
+       {
+               Assert(!isSubXact);
+               MyProc->xid = BootstrapTransactionId;
                return BootstrapTransactionId;
+       }
 
        LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
 
@@ -112,19 +118,19 @@ GetNewTransactionId(bool isSubXact)
        TransactionIdAdvance(ShmemVariableCache->nextXid);
 
        /*
-        * We must store the new XID into the shared PGPROC array before releasing
-        * XidGenLock.  This ensures that when GetSnapshotData calls
-        * ReadNewTransactionId, all active XIDs before the returned value of
-        * nextXid are already present in PGPROC.  Else we have a race condition.
+        * We must store the new XID into the shared ProcArray before releasing
+        * XidGenLock.  This ensures that every active XID older than
+        * latestCompletedXid is present in the ProcArray, which is essential
+        * for correct OldestXmin tracking; see src/backend/access/transam/README.
         *
         * XXX by storing xid into MyProc without acquiring ProcArrayLock, we are
         * relying on fetch/store of an xid to be atomic, else other backends
         * might see a partially-set xid here.  But holding both locks at once
-        * would be a nasty concurrency hit (and in fact could cause a deadlock
-        * against GetSnapshotData).  So for now, assume atomicity. Note that
-        * readers of PGPROC xid field should be careful to fetch the value only
-        * once, rather than assume they can read it multiple times and get the
-        * same answer each time.
+        * would be a nasty concurrency hit.  So for now, assume atomicity.
+        *
+        * Note that readers of PGPROC xid fields should be careful to fetch the
+        * value only once, rather than assume they can read a value multiple
+        * times and get the same answer each time.
         *
         * The same comments apply to the subxact xid count and overflow fields.
         *
@@ -138,11 +144,10 @@ GetNewTransactionId(bool isSubXact)
         * race-condition window, in that the new XID will not appear as running
         * until its parent link has been placed into pg_subtrans. However, that
         * will happen before anyone could possibly have a reason to inquire about
-        * the status of the XID, so it seems OK. (Snapshots taken during this
+        * the status of the XID, so it seems OK.  (Snapshots taken during this
         * window *will* include the parent XID, so they will deliver the correct
         * answer later on when someone does have a reason to inquire.)
         */
-       if (MyProc != NULL)
        {
                /*
                 * Use volatile pointer to prevent code rearrangement; other backends
index 02b064179f2bc8e302b75756e19b08542b34a810..1b16f7111b21c5ee9bda14c2befd53332819c554 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.249 2007/09/07 20:59:26 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.250 2007/09/08 20:31:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -233,7 +233,7 @@ static void CallSubXactCallbacks(SubXactEvent event,
                                         SubTransactionId parentSubid);
 static void CleanupTransaction(void);
 static void CommitTransaction(void);
-static void RecordTransactionAbort(bool isSubXact);
+static TransactionId RecordTransactionAbort(bool isSubXact);
 static void StartTransaction(void);
 
 static void RecordSubTransactionCommit(void);
@@ -748,13 +748,17 @@ AtSubStart_ResourceOwner(void)
 /*
  *     RecordTransactionCommit
  *
+ * Returns latest XID among xact and its children, or InvalidTransactionId
+ * if the xact has no XID.  (We compute that here just because it's easier.)
+ *
  * This is exported only to support an ugly hack in VACUUM FULL.
  */
-void
+TransactionId
 RecordTransactionCommit(void)
 {
        TransactionId xid = GetTopTransactionIdIfAny();
        bool            markXidCommitted = TransactionIdIsValid(xid);
+       TransactionId latestXid = InvalidTransactionId;
        int                     nrels;
        RelFileNode *rels;
        bool            haveNonTemp;
@@ -930,6 +934,9 @@ RecordTransactionCommit(void)
                END_CRIT_SECTION();
        }
 
+       /* Compute latestXid while we have the child XIDs handy */
+       latestXid = TransactionIdLatest(xid, nchildren, children);
+
        /* Reset XactLastRecEnd until the next transaction writes something */
        XactLastRecEnd.xrecoff = 0;
 
@@ -939,6 +946,8 @@ cleanup:
                pfree(rels);
        if (children)
                pfree(children);
+
+       return latestXid;
 }
 
 
@@ -1084,11 +1093,15 @@ RecordSubTransactionCommit(void)
 
 /*
  *     RecordTransactionAbort
+ *
+ * Returns latest XID among xact and its children, or InvalidTransactionId
+ * if the xact has no XID.  (We compute that here just because it's easier.)
  */
-static void
+static TransactionId
 RecordTransactionAbort(bool isSubXact)
 {
        TransactionId xid = GetCurrentTransactionIdIfAny();
+       TransactionId latestXid;
        int                     nrels;
        RelFileNode *rels;
        int                     nchildren;
@@ -1108,7 +1121,7 @@ RecordTransactionAbort(bool isSubXact)
                /* Reset XactLastRecEnd until the next transaction writes something */
                if (!isSubXact)
                        XactLastRecEnd.xrecoff = 0;
-               return;
+               return InvalidTransactionId;
        }
 
        /*
@@ -1186,6 +1199,9 @@ RecordTransactionAbort(bool isSubXact)
 
        END_CRIT_SECTION();
 
+       /* Compute latestXid while we have the child XIDs handy */
+       latestXid = TransactionIdLatest(xid, nchildren, children);
+
        /*
         * If we're aborting a subtransaction, we can immediately remove failed
         * XIDs from PGPROC's cache of running child XIDs.  We do that here for
@@ -1193,7 +1209,7 @@ RecordTransactionAbort(bool isSubXact)
         * main xacts, the equivalent happens just after this function returns.
         */
        if (isSubXact)
-               XidCacheRemoveRunningXids(xid, nchildren, children);
+               XidCacheRemoveRunningXids(xid, nchildren, children, latestXid);
 
        /* Reset XactLastRecEnd until the next transaction writes something */
        if (!isSubXact)
@@ -1204,6 +1220,8 @@ RecordTransactionAbort(bool isSubXact)
                pfree(rels);
        if (children)
                pfree(children);
+
+       return latestXid;
 }
 
 /*
@@ -1481,6 +1499,7 @@ static void
 CommitTransaction(void)
 {
        TransactionState s = CurrentTransactionState;
+       TransactionId latestXid;
 
        ShowTransactionState("CommitTransaction");
 
@@ -1552,7 +1571,7 @@ CommitTransaction(void)
        /*
         * Here is where we really truly commit.
         */
-       RecordTransactionCommit();
+       latestXid = RecordTransactionCommit();
 
        PG_TRACE1(transaction__commit, MyProc->lxid);
 
@@ -1560,47 +1579,8 @@ CommitTransaction(void)
         * Let others know about no transaction in progress by me. Note that
         * this must be done _before_ releasing locks we hold and _after_
         * RecordTransactionCommit.
-        *
-        * Note: MyProc may be null during bootstrap.
         */
-       if (MyProc != NULL)
-       {
-               if (TransactionIdIsValid(MyProc->xid))
-               {
-                       /*
-                        * We must lock ProcArrayLock while clearing MyProc->xid, so
-                        * that we do not exit the set of "running" transactions while
-                        * someone else is taking a snapshot.  See discussion in
-                        * src/backend/access/transam/README.
-                        */
-                       LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-
-                       MyProc->xid = InvalidTransactionId;
-                       MyProc->lxid = InvalidLocalTransactionId;
-                       MyProc->xmin = InvalidTransactionId;
-                       MyProc->inVacuum = false;       /* must be cleared with xid/xmin */
-
-                       /* Clear the subtransaction-XID cache too while holding the lock */
-                       MyProc->subxids.nxids = 0;
-                       MyProc->subxids.overflowed = false;
-
-                       LWLockRelease(ProcArrayLock);
-               }
-               else
-               {
-                       /*
-                        * If we have no XID, we don't need to lock, since we won't
-                        * affect anyone else's calculation of a snapshot.  We might
-                        * change their estimate of global xmin, but that's OK.
-                        */
-                       MyProc->lxid = InvalidLocalTransactionId;
-                       MyProc->xmin = InvalidTransactionId;
-                       MyProc->inVacuum = false;       /* must be cleared with xid/xmin */
-
-                       Assert(MyProc->subxids.nxids == 0);
-                       Assert(MyProc->subxids.overflowed == false);
-               }
-       }
+       ProcArrayEndTransaction(MyProc, latestXid);
 
        /*
         * This is all post-commit cleanup.  Note that if an error is raised here,
@@ -1824,20 +1804,8 @@ PrepareTransaction(void)
         * Let others know about no transaction in progress by me.      This has to be
         * done *after* the prepared transaction has been marked valid, else
         * someone may think it is unlocked and recyclable.
-        *
-        * We can skip locking ProcArrayLock here, because this action does not
-        * actually change anyone's view of the set of running XIDs: our entry
-        * is duplicate with the gxact that has already been inserted into the
-        * ProcArray.
         */
-       MyProc->xid = InvalidTransactionId;
-       MyProc->lxid = InvalidLocalTransactionId;
-       MyProc->xmin = InvalidTransactionId;
-       MyProc->inVacuum = false;       /* must be cleared with xid/xmin */
-
-       /* Clear the subtransaction-XID cache too */
-       MyProc->subxids.nxids = 0;
-       MyProc->subxids.overflowed = false;
+       ProcArrayClearTransaction(MyProc);
 
        /*
         * This is all post-transaction cleanup.  Note that if an error is raised
@@ -1921,6 +1889,7 @@ static void
 AbortTransaction(void)
 {
        TransactionState s = CurrentTransactionState;
+       TransactionId latestXid;
 
        /* Prevent cancel/die interrupt while cleaning up */
        HOLD_INTERRUPTS();
@@ -1987,7 +1956,7 @@ AbortTransaction(void)
         * Advertise the fact that we aborted in pg_clog (assuming that we got as
         * far as assigning an XID to advertise).
         */
-       RecordTransactionAbort(false);
+       latestXid = RecordTransactionAbort(false);
 
        PG_TRACE1(transaction__abort, MyProc->lxid);
 
@@ -1995,49 +1964,8 @@ AbortTransaction(void)
         * Let others know about no transaction in progress by me. Note that this
         * must be done _before_ releasing locks we hold and _after_
         * RecordTransactionAbort.
-        *
-        * Note: MyProc may be null during bootstrap.
         */
-       if (MyProc != NULL)
-       {
-               if (TransactionIdIsValid(MyProc->xid))
-               {
-                       /*
-                        * We must lock ProcArrayLock while clearing MyProc->xid, so
-                        * that we do not exit the set of "running" transactions while
-                        * someone else is taking a snapshot.  See discussion in
-                        * src/backend/access/transam/README.
-                        */
-                       LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-
-                       MyProc->xid = InvalidTransactionId;
-                       MyProc->lxid = InvalidLocalTransactionId;
-                       MyProc->xmin = InvalidTransactionId;
-                       MyProc->inVacuum = false;       /* must be cleared with xid/xmin */
-                       MyProc->inCommit = false;       /* be sure this gets cleared */
-
-                       /* Clear the subtransaction-XID cache too while holding the lock */
-                       MyProc->subxids.nxids = 0;
-                       MyProc->subxids.overflowed = false;
-
-                       LWLockRelease(ProcArrayLock);
-               }
-               else
-               {
-                       /*
-                        * If we have no XID, we don't need to lock, since we won't
-                        * affect anyone else's calculation of a snapshot.  We might
-                        * change their estimate of global xmin, but that's OK.
-                        */
-                       MyProc->lxid = InvalidLocalTransactionId;
-                       MyProc->xmin = InvalidTransactionId;
-                       MyProc->inVacuum = false;       /* must be cleared with xid/xmin */
-                       MyProc->inCommit = false;       /* be sure this gets cleared */
-
-                       Assert(MyProc->subxids.nxids == 0);
-                       Assert(MyProc->subxids.overflowed == false);
-               }
-       }
+       ProcArrayEndTransaction(MyProc, latestXid);
 
        /*
         * Post-abort cleanup.  See notes in CommitTransaction() concerning
@@ -3863,7 +3791,7 @@ AbortSubTransaction(void)
                                                                        s->parent->subTransactionId);
 
                /* Advertise the fact that we aborted in pg_clog. */
-               RecordTransactionAbort(true);
+               (void) RecordTransactionAbort(true);
 
                /* Post-abort cleanup */
                if (TransactionIdIsValid(s->transactionId))
index 5474a91c247966905e55928dd871bbeb4e3b333f..44e388dbb4995033ffa26dff319f1e0b5b81ee42 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.280 2007/09/05 18:10:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.281 2007/09/08 20:31:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -5196,6 +5196,10 @@ StartupXLOG(void)
        XLogCtl->ckptXidEpoch = ControlFile->checkPointCopy.nextXidEpoch;
        XLogCtl->ckptXid = ControlFile->checkPointCopy.nextXid;
 
+       /* also initialize latestCompletedXid, to nextXid - 1 */
+       ShmemVariableCache->latestCompletedXid = ShmemVariableCache->nextXid;
+       TransactionIdRetreat(ShmemVariableCache->latestCompletedXid);
+
        /* Start up the commit log and related stuff, too */
        StartupCLOG();
        StartupSUBTRANS(oldestActiveXID);
index f1e3ee2a1efa1846d513f960b578eb0bc3dfc15c..cbf05acfb8353b7666e2b0bf67c906106381ea6c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.322 2007/06/03 22:16:02 petere Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.323 2007/09/08 20:31:14 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -704,10 +704,7 @@ AddNewRelationTuple(Relation pg_class_desc,
                 * We know that no xacts older than RecentXmin are still running,
                 * so that will do.
                 */
-               if (!IsBootstrapProcessingMode())
-                       new_rel_reltup->relfrozenxid = RecentXmin;
-               else
-                       new_rel_reltup->relfrozenxid = FirstNormalTransactionId;
+               new_rel_reltup->relfrozenxid = RecentXmin;
        }
        else
        {
index 87cf57daec3e298053d9f2faa23593fe84900d25..89ddeedbea7760c028a6c0c5970a5e13a5f4adb5 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.356 2007/09/05 18:10:47 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.357 2007/09/08 20:31:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2437,14 +2437,15 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
                 * For now, a quick hack: record status of current transaction as
                 * committed, and continue.  We force the commit to be synchronous
                 * so that it's down to disk before we truncate.  (Note: tqual.c
-                * knows that VACUUM FULL always uses sync commit, too.)
+                * knows that VACUUM FULL always uses sync commit, too.)  The
+                * transaction continues to be shown as running in the ProcArray.
                 *
                 * XXX This desperately needs to be revisited.  Any failure after
                 * this point will result in a PANIC "cannot abort transaction nnn,
                 * it was already committed"!
                 */
                ForceSyncCommit();
-               RecordTransactionCommit();
+               (void) RecordTransactionCommit();
        }
 
        /*
index b7c4ebcb1b13136064e35d99ce249a72abfb6e10..0565510c31b8676a9d8018b5fe45e1595e6ab859 100644 (file)
@@ -23,7 +23,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.32 2007/09/07 20:59:26 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.33 2007/09/08 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -147,9 +147,16 @@ ProcArrayAdd(PGPROC *proc)
 
 /*
  * Remove the specified PGPROC from the shared array.
+ *
+ * When latestXid is a valid XID, we are removing a live 2PC gxact from the
+ * array, and thus causing it to appear as "not running" anymore.  In this
+ * case we must advance latestCompletedXid.  (This is essentially the same
+ * as ProcArrayEndTransaction followed by removal of the PGPROC, but we take
+ * the ProcArrayLock only once, and don't damage the content of the PGPROC;
+ * twophase.c depends on the latter.)
  */
 void
-ProcArrayRemove(PGPROC *proc)
+ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
 {
        ProcArrayStruct *arrayP = procArray;
        int                     index;
@@ -162,6 +169,21 @@ ProcArrayRemove(PGPROC *proc)
 
        LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 
+       if (TransactionIdIsValid(latestXid))
+       {
+               Assert(TransactionIdIsValid(proc->xid));
+
+               /* Advance global latestCompletedXid while holding the lock */
+               if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
+                                                                 latestXid))
+                       ShmemVariableCache->latestCompletedXid = latestXid;
+       }
+       else
+       {
+               /* Shouldn't be trying to remove a live transaction here */
+               Assert(!TransactionIdIsValid(proc->xid));
+       }
+
        for (index = 0; index < arrayP->numProcs; index++)
        {
                if (arrayP->procs[index] == proc)
@@ -180,6 +202,100 @@ ProcArrayRemove(PGPROC *proc)
 }
 
 
+/*
+ * ProcArrayEndTransaction -- mark a transaction as no longer running
+ *
+ * This is used interchangeably for commit and abort cases.  The transaction
+ * commit/abort must already be reported to WAL and pg_clog.
+ *
+ * proc is currently always MyProc, but we pass it explicitly for flexibility.
+ * latestXid is the latest Xid among the transaction's main XID and
+ * subtransactions, or InvalidTransactionId if it has no XID.  (We must ask
+ * the caller to pass latestXid, instead of computing it from the PGPROC's
+ * contents, because the subxid information in the PGPROC might be
+ * incomplete.)
+ */
+void
+ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
+{
+       if (TransactionIdIsValid(latestXid))
+       {
+               /*
+                * We must lock ProcArrayLock while clearing proc->xid, so
+                * that we do not exit the set of "running" transactions while
+                * someone else is taking a snapshot.  See discussion in
+                * src/backend/access/transam/README.
+                */
+               Assert(TransactionIdIsValid(proc->xid));
+
+               LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+
+               proc->xid = InvalidTransactionId;
+               proc->lxid = InvalidLocalTransactionId;
+               proc->xmin = InvalidTransactionId;
+               proc->inVacuum = false;                 /* must be cleared with xid/xmin */
+               proc->inCommit = false;                 /* be sure this is cleared in abort */
+
+               /* Clear the subtransaction-XID cache too while holding the lock */
+               proc->subxids.nxids = 0;
+               proc->subxids.overflowed = false;
+
+               /* Also advance global latestCompletedXid while holding the lock */
+               if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
+                                                                 latestXid))
+                       ShmemVariableCache->latestCompletedXid = latestXid;
+
+               LWLockRelease(ProcArrayLock);
+       }
+       else
+       {
+               /*
+                * If we have no XID, we don't need to lock, since we won't
+                * affect anyone else's calculation of a snapshot.  We might
+                * change their estimate of global xmin, but that's OK.
+                */
+               Assert(!TransactionIdIsValid(proc->xid));
+
+               proc->lxid = InvalidLocalTransactionId;
+               proc->xmin = InvalidTransactionId;
+               proc->inVacuum = false;                 /* must be cleared with xid/xmin */
+               proc->inCommit = false;                 /* be sure this is cleared in abort */
+
+               Assert(proc->subxids.nxids == 0);
+               Assert(proc->subxids.overflowed == false);
+       }
+}
+
+
+/*
+ * ProcArrayClearTransaction -- clear the transaction fields
+ *
+ * This is used after successfully preparing a 2-phase transaction.  We are
+ * not actually reporting the transaction's XID as no longer running --- it
+ * will still appear as running because the 2PC's gxact is in the ProcArray
+ * too.  We just have to clear out our own PGPROC.
+ */
+void
+ProcArrayClearTransaction(PGPROC *proc)
+{
+       /*
+        * We can skip locking ProcArrayLock here, because this action does not
+        * actually change anyone's view of the set of running XIDs: our entry
+        * is duplicate with the gxact that has already been inserted into the
+        * ProcArray.
+        */
+       proc->xid = InvalidTransactionId;
+       proc->lxid = InvalidLocalTransactionId;
+       proc->xmin = InvalidTransactionId;
+       proc->inVacuum = false;                 /* redundant, but just in case */
+       proc->inCommit = false;                 /* ditto */
+
+       /* Clear the subtransaction-XID cache too */
+       proc->subxids.nxids = 0;
+       proc->subxids.overflowed = false;
+}
+
+
 /*
  * TransactionIdIsInProgress -- is given transaction running in some backend
  *
@@ -416,22 +532,17 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
        TransactionId result;
        int                     index;
 
+       LWLockAcquire(ProcArrayLock, LW_SHARED);
+
        /*
-        * We need to initialize the MIN() calculation with something.
-        * ReadNewTransactionId() is guaranteed to work, but is relatively
-        * expensive due to locking; so first we try a couple of shortcuts.
-        * If we have a valid xmin in our own PGPROC entry, that will do;
-        * or if we have assigned ourselves an XID, that will do.
+        * 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.
         */
-       result = MyProc ? MyProc->xmin : InvalidTransactionId;
-       if (!TransactionIdIsValid(result))
-       {
-               result = GetTopTransactionIdIfAny();
-               if (!TransactionIdIsValid(result))
-                       result = ReadNewTransactionId();
-       }
-
-       LWLockAcquire(ProcArrayLock, LW_SHARED);
+       result = ShmemVariableCache->latestCompletedXid;
+       Assert(TransactionIdIsNormal(result));
+       TransactionIdAdvance(result);
 
        for (index = 0; index < arrayP->numProcs; index++)
        {
@@ -473,7 +584,7 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
  * GetSnapshotData -- returns information about running transactions.
  *
  * The returned snapshot includes xmin (lowest still-running xact ID),
- * xmax (next xact ID to be assigned), and a list of running xact IDs
+ * xmax (highest completed xact ID + 1), and a list of running xact IDs
  * in the range xmin <= xid < xmax.  It is used as follows:
  *             All xact IDs < xmin are considered finished.
  *             All xact IDs >= xmax are considered still running.
@@ -555,13 +666,10 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
         */
        LWLockAcquire(ProcArrayLock, LW_SHARED);
 
-       /*
-        * Unfortunately, we have to call ReadNewTransactionId() after acquiring
-        * ProcArrayLock.  It's not good because ReadNewTransactionId() does
-        * LWLockAcquire(XidGenLock), but *necessary*.  See discussion in
-        * src/backend/access/transam/README.
-        */
-       xmax = ReadNewTransactionId();
+       /* xmax is always latestCompletedXid + 1 */
+       xmax = ShmemVariableCache->latestCompletedXid;
+       Assert(TransactionIdIsNormal(xmax));
+       TransactionIdAdvance(xmax);
 
        /* initialize xmin calculation with xmax */
        globalxmin = xmin = xmax;
@@ -592,9 +700,8 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
                /*
                 * If the transaction has been assigned an xid < xmax we add it to the
                 * snapshot, and update xmin if necessary.  There's no need to store
-                * XIDs above what we got from ReadNewTransactionId, since we'll treat
-                * them as running anyway.  We don't bother to examine their subxids
-                * either.
+                * XIDs >= xmax, since we'll treat them as running anyway.  We don't
+                * bother to examine their subxids either.
                 *
                 * We don't include our own XID (if any) in the snapshot, but we must
                 * include it into xmin.
@@ -612,7 +719,9 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
                /*
                 * Save subtransaction XIDs if possible (if we've already overflowed,
                 * there's no point).  Note that the subxact XIDs must be later than
-                * their parent, so no need to check them against xmin.
+                * their parent, so no need to check them against xmin.  We could
+                * filter against xmax, but it seems better not to do that much work
+                * while holding the ProcArrayLock.
                 *
                 * The other backend can add more subxids concurrently, but cannot
                 * remove any.  Hence it's important to fetch nxids just once. Should
@@ -1096,9 +1205,12 @@ CheckOtherDBBackends(Oid databaseId)
  * Remove a bunch of TransactionIds from the list of known-running
  * subtransactions for my backend.     Both the specified xid and those in
  * the xids[] array (of length nxids) are removed from the subxids cache.
+ * latestXid must be the latest XID among the group.
  */
 void
-XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
+XidCacheRemoveRunningXids(TransactionId xid,
+                                                 int nxids, const TransactionId *xids,
+                                                 TransactionId latestXid)
 {
        int                     i,
                                j;
@@ -1155,6 +1267,11 @@ XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
        if (j < 0 && !MyProc->subxids.overflowed)
                elog(WARNING, "did not find subXID %u in MyProc", xid);
 
+       /* Also advance global latestCompletedXid while holding the lock */
+       if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
+                                                         latestXid))
+               ShmemVariableCache->latestCompletedXid = latestXid;
+
        LWLockRelease(ProcArrayLock);
 }
 
index 5441dd322de7925150ff8e5d13153800daedd550..ddebdcc5e2ad5221ad304166df86d5b09b6c26a7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.193 2007/09/05 18:10:47 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.194 2007/09/08 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -581,7 +581,7 @@ static void
 RemoveProcFromArray(int code, Datum arg)
 {
        Assert(MyProc != NULL);
-       ProcArrayRemove(MyProc);
+       ProcArrayRemove(MyProc, InvalidTransactionId);
 }
 
 /*
index 907476ee1f149d194e828f551119f8943272f73b..f580c0b461250a589134ca9929c8a43e63be443d 100644 (file)
@@ -31,7 +31,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.104 2007/08/14 17:35:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.105 2007/09/08 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -71,10 +71,14 @@ Snapshot    LatestSnapshot = NULL;
  */
 Snapshot       ActiveSnapshot = NULL;
 
-/* These are updated by GetSnapshotData: */
-TransactionId TransactionXmin = InvalidTransactionId;
-TransactionId RecentXmin = InvalidTransactionId;
-TransactionId RecentGlobalXmin = InvalidTransactionId;
+/*
+ * These are updated by GetSnapshotData.  We initialize them this way
+ * for the convenience of TransactionIdIsInProgress: even in bootstrap
+ * mode, we don't want it to say that BootstrapTransactionId is in progress.
+ */
+TransactionId TransactionXmin = FirstNormalTransactionId;
+TransactionId RecentXmin = FirstNormalTransactionId;
+TransactionId RecentGlobalXmin = FirstNormalTransactionId;
 
 /* local functions */
 static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
index 98850cc0d3be4c5f7f5fe1f6d1ebe6840670fbc1..0408038124c104e490c21f716dc8d943fafa68c7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/transam.h,v 1.61 2007/08/01 22:45:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/transam.h,v 1.62 2007/09/08 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,6 +43,7 @@
 #define TransactionIdEquals(id1, id2)  ((id1) == (id2))
 #define TransactionIdStore(xid, dest)  (*(dest) = (xid))
 #define StoreInvalidTransactionId(dest) (*(dest) = InvalidTransactionId)
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)     \
        do { \
                        (dest) = FirstNormalTransactionId; \
        } while(0)
 
+/* back up a transaction ID variable, handling wraparound correctly */
+#define TransactionIdRetreat(dest)     \
+       do { \
+               (dest)--; \
+       } while ((dest) < FirstNormalTransactionId)
+
 
 /* ----------
  *             Object ID (OID) zero is InvalidOid.
 #define FirstNormalObjectId            16384
 
 /*
- * VariableCache is placed in shmem and used by
- * backends to get next available OID & XID.
+ * VariableCache is a data structure in shared memory that is used to track
+ * OID and XID assignment state.  For largely historical reasons, there is
+ * just one struct with different fields that are protected by different
+ * LWLocks.
  *
  * Note: xidWrapLimit and limit_datname are not "active" values, but are
  * used just to generate useful messages when xidWarnLimit or xidStopLimit
  */
 typedef struct VariableCacheData
 {
+       /*
+        * These fields are protected by OidGenLock.
+        */
        Oid                     nextOid;                /* next OID to assign */
        uint32          oidCount;               /* OIDs available before must do XLOG work */
+
+       /*
+        * These fields are protected by XidGenLock.
+        */
        TransactionId nextXid;          /* next XID to assign */
 
        TransactionId oldestXid;        /* cluster-wide minimum datfrozenxid */
@@ -97,6 +113,12 @@ typedef struct VariableCacheData
        TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */
        TransactionId xidWrapLimit; /* where the world ends */
        NameData        limit_datname;  /* database that needs vacuumed first */
+
+       /*
+        * These fields are protected by ProcArrayLock.
+        */
+       TransactionId latestCompletedXid;       /* newest XID that has committed or
+                                                                                * aborted */
 } VariableCacheData;
 
 typedef VariableCacheData *VariableCache;
@@ -127,6 +149,8 @@ extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2);
 extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
 extern bool TransactionIdFollows(TransactionId id1, TransactionId id2);
 extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2);
+extern TransactionId TransactionIdLatest(TransactionId mainxid,
+                                                                                int nxids, const TransactionId *xids);
 extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
 
 /* in transam/varsup.c */
index 731269af9a073dee29720812b89abb4d70367888..a6755619a1b24f6ba3eb448d04e7c8e3e600e83b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/xact.h,v 1.89 2007/09/05 18:10:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/xact.h,v 1.90 2007/09/08 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -178,7 +178,7 @@ extern void UnregisterXactCallback(XactCallback callback, void *arg);
 extern void RegisterSubXactCallback(SubXactCallback callback, void *arg);
 extern void UnregisterSubXactCallback(SubXactCallback callback, void *arg);
 
-extern void RecordTransactionCommit(void);
+extern TransactionId RecordTransactionCommit(void);
 
 extern int     xactGetCommittedChildren(TransactionId **ptr);
 
index 21e0c8395224937529b25256bec2944bfad27011..c330d0093fcd12259cd7f64178168079bd525e9f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.16 2007/09/07 00:58:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.17 2007/09/08 20:31:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 extern Size ProcArrayShmemSize(void);
 extern void CreateSharedProcArray(void);
 extern void ProcArrayAdd(PGPROC *proc);
-extern void ProcArrayRemove(PGPROC *proc);
+extern void ProcArrayRemove(PGPROC *proc, TransactionId latestXid);
+
+extern void ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid);
+extern void ProcArrayClearTransaction(PGPROC *proc);
 
 extern bool TransactionIdIsInProgress(TransactionId xid);
 extern bool TransactionIdIsActive(TransactionId xid);
@@ -41,6 +44,7 @@ extern int    CountUserBackends(Oid roleid);
 extern bool CheckOtherDBBackends(Oid databaseId);
 
 extern void XidCacheRemoveRunningXids(TransactionId xid,
-                                                 int nxids, TransactionId *xids);
+                                                                         int nxids, const TransactionId *xids,
+                                                                         TransactionId latestXid);
 
 #endif   /* PROCARRAY_H */