From 0a51e7073c045b5fce50092dae19f398f7b38e16 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 7 Sep 2007 20:59:26 +0000
Subject: [PATCH] Don't take ProcArrayLock while exiting a transaction that has
 no XID; there is no need for serialization against snapshot-taking because
 the xact doesn't affect anyone else's snapshot anyway.  Per discussion. 
 Also, move various info about the interlocking of transactions and snapshots
 out of code comments and into a hopefully-more-cohesive discussion in
 access/transam/README.

Also, remove a couple of now-obsolete comments about having to force some WAL
to be written to persuade RecordTransactionCommit to do its thing.
---
 src/backend/access/heap/heapam.c    |   7 +-
 src/backend/access/transam/README   | 106 ++++++++++++++++++-
 src/backend/access/transam/xact.c   | 151 ++++++++++++++++------------
 src/backend/commands/copy.c         |   9 +-
 src/backend/executor/execMain.c     |   8 +-
 src/backend/storage/ipc/procarray.c |  79 ++++-----------
 6 files changed, 216 insertions(+), 144 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 3f23378b8fa..f1ca5249cae 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.238 2007/09/05 18:10:47 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.239 2007/09/07 20:59:26 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1546,9 +1546,8 @@ UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
  * If use_wal is false, the new tuple is not logged in WAL, even for a
  * non-temp relation.  Safe usage of this behavior requires that we arrange
  * that all new tuples go into new pages not containing any tuples from other
- * transactions, that the relation gets fsync'd before commit, and that the
- * transaction emits at least one WAL record to ensure RecordTransactionCommit
- * will decide to WAL-log the commit.  (See also heap_sync() comments)
+ * transactions, and that the relation gets fsync'd before commit.
+ * (See also heap_sync() comments)
  *
  * use_fsm is passed directly to RelationGetBufferForTuple, which see for
  * more info.
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index 87b40591702..7c69e09cb5d 100644
--- a/src/backend/access/transam/README
+++ b/src/backend/access/transam/README
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.7 2007/09/05 18:10:47 tgl Exp $
+$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.8 2007/09/07 20:59:26 tgl Exp $
 
 The Transaction System
 ----------------------
@@ -221,6 +221,110 @@ InvalidSubTransactionId.)  Note that subtransactions do not have their
 own VXIDs; they use the parent top transaction's VXID.
 
 
+Interlocking transaction begin, transaction end, and snapshots
+--------------------------------------------------------------
+
+We try hard to minimize the amount of overhead and lock contention involved
+in the frequent activities of beginning/ending a transaction and taking a
+snapshot.  Unfortunately, we must have some interlocking for this, because
+we must ensure consistency about the commit order of transactions.
+For example, suppose an UPDATE in xact A is blocked by xact B's prior
+update of the same row, and xact B is doing commit while xact C gets a
+snapshot.  Xact A can complete and commit as soon as B releases its locks.
+If xact C's GetSnapshotData sees xact B as still running, then it had
+better see xact A as still running as well, or it will be able to see two
+tuple versions - one deleted by xact B and one inserted by xact A.  Another
+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".
+
+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
+running transactions while a snapshot is being taken.  (This rule is
+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).
+
+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.
+
+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.
+
+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.
+
+Another important activity that uses the shared ProcArray is GetOldestXmin,
+which must determine a lower bound for the oldest xmin of any active MVCC
+snapshot, system-wide.  Each individual backend advertises the smallest
+xmin of its own snapshots in MyProc->xmin, or zero if it currently has no
+live snapshots (eg, if it's between transactions or hasn't yet set a
+snapshot for a new transaction).  GetOldestXmin takes the MIN() of the
+valid xmin fields.  It does this with only shared lock on ProcArrayLock,
+which means there is a potential race condition against other backends
+doing GetSnapshotData concurrently: we must be certain that a concurrent
+backend that is about to set its xmin does not compute an xmin less than
+what GetOldestXmin returns.  We ensure that by including all the active
+XIDs into the MIN() calculation, along with the valid xmins.  The rule that
+transactions can't exit without taking exclusive ProcArrayLock ensures that
+concurrent holders of shared ProcArrayLock will compute the same minimum of
+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.
+
+GetSnapshotData also performs an oldest-xmin calculation (which had better
+match GetOldestXmin's) and stores that into RecentGlobalXmin, which is used
+for some tuple age cutoff checks where a fresh call of GetOldestXmin seems
+too expensive.  Note that while it is certain that two concurrent
+executions of GetSnapshotData will compute the same xmin for their own
+snapshots, as argued above, it is not certain that they will arrive at the
+same estimate of RecentGlobalXmin.  This is because we allow XID-less
+transactions to clear their MyProc->xmin asynchronously (without taking
+ProcArrayLock), so one execution might see what had been the oldest xmin,
+and another not.  This is OK since RecentGlobalXmin need only be a valid
+lower bound.  As noted above, we are already assuming that fetch/store
+of the xid fields is atomic, so assuming it for xmin as well is no extra
+risk.
+
+
 pg_clog and pg_subtrans
 -----------------------
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 2e972d56f60..02b064179f2 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.248 2007/09/05 18:10:47 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.249 2007/09/07 20:59:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -747,6 +747,8 @@ AtSubStart_ResourceOwner(void)
 
 /*
  *	RecordTransactionCommit
+ *
+ * This is exported only to support an ugly hack in VACUUM FULL.
  */
 void
 RecordTransactionCommit(void)
@@ -1552,46 +1554,53 @@ CommitTransaction(void)
 	 */
 	RecordTransactionCommit();
 
-	/*----------
+	PG_TRACE1(transaction__commit, MyProc->lxid);
+
+	/*
 	 * Let others know about no transaction in progress by me. Note that
 	 * this must be done _before_ releasing locks we hold and _after_
 	 * RecordTransactionCommit.
 	 *
-	 * LWLockAcquire(ProcArrayLock) is required; consider this example:
-	 *		UPDATE with xid 0 is blocked by xid 1's UPDATE.
-	 *		xid 1 is doing commit while xid 2 gets snapshot.
-	 * If xid 2's GetSnapshotData sees xid 1 as running then it must see
-	 * xid 0 as running as well, or it will be able to see two tuple versions
-	 * - one deleted by xid 1 and one inserted by xid 0.  See notes in
-	 * GetSnapshotData.
-	 *
 	 * Note: MyProc may be null during bootstrap.
-	 *----------
 	 */
 	if (MyProc != NULL)
 	{
-		/*
-		 * Lock ProcArrayLock because that's what GetSnapshotData uses.
-		 * You might assume that we can skip this step if we had no
-		 * transaction id assigned, because the failure case outlined
-		 * in GetSnapshotData cannot happen in that case. This is true,
-		 * but we *still* need the lock guarantee that two concurrent
-		 * computations of the *oldest* xmin will get the same result.
-		 */
-		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-		MyProc->xid = InvalidTransactionId;
-		MyProc->lxid = InvalidLocalTransactionId;
-		MyProc->xmin = InvalidTransactionId;
-		MyProc->inVacuum = false;		/* must be cleared with xid/xmin */
+		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);
 
-		/* Clear the subtransaction-XID cache too while holding the lock */
-		MyProc->subxids.nxids = 0;
-		MyProc->subxids.overflowed = false;
+			MyProc->xid = InvalidTransactionId;
+			MyProc->lxid = InvalidLocalTransactionId;
+			MyProc->xmin = InvalidTransactionId;
+			MyProc->inVacuum = false;	/* must be cleared with xid/xmin */
 
-		LWLockRelease(ProcArrayLock);
-	}
+			/* Clear the subtransaction-XID cache too while holding the lock */
+			MyProc->subxids.nxids = 0;
+			MyProc->subxids.overflowed = false;
 
-	PG_TRACE1(transaction__commit, s->transactionId);
+			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);
+		}
+	}
 
 	/*
 	 * This is all post-commit cleanup.  Note that if an error is raised here,
@@ -1815,28 +1824,21 @@ 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.
 	 */
-
-	/*
-	 * Lock ProcArrayLock because that's what GetSnapshotData uses.
-	 * You might assume that we can skip this step if we have no
-	 * transaction id assigned, because the failure case outlined
-	 * in GetSnapshotData cannot happen in that case. This is true,
-	 * but we *still* need the lock guarantee that two concurrent
-	 * computations of the *oldest* xmin will get the same result.
-	 */
-	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 */
+	/* Clear the subtransaction-XID cache too */
 	MyProc->subxids.nxids = 0;
 	MyProc->subxids.overflowed = false;
 
-	LWLockRelease(ProcArrayLock);
-
 	/*
 	 * This is all post-transaction cleanup.  Note that if an error is raised
 	 * here, it's too late to abort the transaction.  This should be just
@@ -1987,36 +1989,55 @@ AbortTransaction(void)
 	 */
 	RecordTransactionAbort(false);
 
+	PG_TRACE1(transaction__abort, MyProc->lxid);
+
 	/*
 	 * 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)
 	{
-		/*
-		 * Lock ProcArrayLock because that's what GetSnapshotData uses.
-		 * You might assume that we can skip this step if we have no
-		 * transaction id assigned, because the failure case outlined
-		 * in GetSnapshotData cannot happen in that case. This is true,
-		 * but we *still* need the lock guarantee that two concurrent
-		 * computations of the *oldest* xmin will get the same result.
-		 */
-		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);
-	}
+		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);
 
-	PG_TRACE1(transaction__abort, s->transactionId);
+			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);
+		}
+	}
 
 	/*
 	 * Post-abort cleanup.	See notes in CommitTransaction() concerning
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 9bc1c388ee7..d28a6ad11c2 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.285 2007/06/20 02:02:49 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.286 2007/09/07 20:59:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1678,13 +1678,6 @@ CopyFrom(CopyState cstate)
 	 * rd_newRelfilenodeSubid can be cleared before the end of the transaction.
 	 * However this is OK since at worst we will fail to make the optimization.
 	 *
-	 * When skipping WAL it's entirely possible that COPY itself will write no
-	 * WAL records at all.  This is of concern because RecordTransactionCommit
-	 * might decide it doesn't need to log our eventual commit, which we
-	 * certainly need it to do.  However, we need no special action here for
-	 * that, because if we have a new table or new relfilenode then there
-	 * must have been a WAL-logged pg_class update earlier in the transaction.
-	 *
 	 * Also, if the target file is new-in-transaction, we assume that checking
 	 * FSM for free space is a waste of time, even if we must use WAL because
 	 * of archiving.  This could possibly be wrong, but it's unlikely.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 322c1d1a27d..7e5873b89df 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.296 2007/08/15 21:39:50 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.297 2007/09/07 20:59:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2635,12 +2635,6 @@ OpenIntoRel(QueryDesc *queryDesc)
 
 	/*
 	 * We can skip WAL-logging the insertions, unless PITR is in use.
-	 *
-	 * Note that for a non-temp INTO table, this is safe only because we know
-	 * that the catalog changes above will have been WAL-logged, and so
-	 * RecordTransactionCommit will think it needs to WAL-log the eventual
-	 * transaction commit.	Else the commit might be lost, even though all the
-	 * data is safely fsync'd ...
 	 */
 	estate->es_into_relation_use_wal = XLogArchivingActive();
 	estate->es_into_relation_descriptor = intoRelationDesc;
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 15701b959ce..b7c4ebcb1b1 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -10,7 +10,7 @@
  *
  * Because of various subtle race conditions it is critical that a backend
  * hold the correct locks while setting or clearing its MyProc->xid field.
- * See notes in GetSnapshotData.
+ * See notes in src/backend/access/transam/README.
  *
  * The process array now also includes PGPROC structures representing
  * prepared transactions.  The xid and subxids fields of these are valid,
@@ -23,7 +23,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.31 2007/09/07 00:58:56 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.32 2007/09/07 20:59:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -407,6 +407,7 @@ TransactionIdIsActive(TransactionId xid)
  * Note: we include all currently running xids in the set of considered xids.
  * This ensures that if a just-started xact has not yet set its snapshot,
  * when it does set the snapshot it cannot set xmin less than what we compute.
+ * See notes in src/backend/access/transam/README.
  */
 TransactionId
 GetOldestXmin(bool allDbs, bool ignoreVacuum)
@@ -468,7 +469,7 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
 	return result;
 }
 
-/*----------
+/*
  * GetSnapshotData -- returns information about running transactions.
  *
  * The returned snapshot includes xmin (lowest still-running xact ID),
@@ -481,12 +482,13 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
  * This ensures that the set of transactions seen as "running" by the
  * current xact will not change after it takes the snapshot.
  *
- * All running top-level XIDs are included in the snapshot.  We also try
- * to include running subtransaction XIDs, but since PGPROC has only a
- * limited cache area for subxact XIDs, full information may not be
- * available.  If we find any overflowed subxid arrays, we have to mark
- * the snapshot's subxid data as overflowed, and extra work will need to
- * be done to determine what's running (see XidInMVCCSnapshot() in tqual.c).
+ * All running top-level XIDs are included in the snapshot, except for lazy
+ * VACUUM processes.  We also try to include running subtransaction XIDs,
+ * but since PGPROC has only a limited cache area for subxact XIDs, full
+ * information may not be available.  If we find any overflowed subxid arrays,
+ * we have to mark the snapshot's subxid data as overflowed, and extra work
+ * will need to be done to determine what's running (see XidInMVCCSnapshot()
+ * in tqual.c).
  *
  * We also update the following backend-global variables:
  *		TransactionXmin: the oldest xmin of any snapshot in use in the
@@ -497,7 +499,6 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
  *		RecentGlobalXmin: the global xmin (oldest TransactionXmin across all
  *			running transactions, except those running LAZY VACUUM).  This is
  *			the same computation done by GetOldestXmin(true, true).
- *----------
  */
 Snapshot
 GetSnapshotData(Snapshot snapshot, bool serializable)
@@ -550,57 +551,16 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
 
 	/*
 	 * It is sufficient to get shared lock on ProcArrayLock, even if we are
-	 * computing a serializable snapshot and therefore will be setting
-	 * MyProc->xmin.  This is because any two backends that have overlapping
-	 * shared holds on ProcArrayLock will certainly compute the same xmin
-	 * (since no xact, in particular not the oldest, can exit the set of
-	 * running transactions while we hold ProcArrayLock --- see further
-	 * discussion just below).	So it doesn't matter whether another backend
-	 * concurrently doing GetSnapshotData or GetOldestXmin sees our xmin as
-	 * set or not; he'd compute the same xmin for himself either way.
-	 * (We are assuming here that xmin can be set and read atomically,
-	 * just like xid.)
-	 *
-	 * There is a corner case in which the above argument doesn't work: if
-	 * there isn't any oldest xact, ie, all xids in the array are invalid.
-	 * In that case we will compute xmin as the result of ReadNewTransactionId,
-	 * and since GetNewTransactionId doesn't take the ProcArrayLock, it's not
-	 * so obvious that two backends with overlapping shared locks will get
-	 * the same answer.  But GetNewTransactionId is required to store the XID
-	 * it assigned into the ProcArray before releasing XidGenLock.  Therefore
-	 * the backend that did ReadNewTransactionId later will see that XID in
-	 * the array, and will compute the same xmin as the earlier one that saw
-	 * no XIDs in the array.
+	 * going to set MyProc->xmin.
 	 */
 	LWLockAcquire(ProcArrayLock, LW_SHARED);
 
-	/*--------------------
+	/*
 	 * Unfortunately, we have to call ReadNewTransactionId() after acquiring
-	 * ProcArrayLock above.  It's not good because ReadNewTransactionId() does
-	 * LWLockAcquire(XidGenLock), but *necessary*.	We need to be sure that
-	 * no transactions exit the set of currently-running transactions
-	 * between the time we fetch xmax and the time we finish building our
-	 * snapshot.  Otherwise we could have a situation like this:
-	 *
-	 *		1. Tx Old is running (in Read Committed mode).
-	 *		2. Tx S reads new transaction ID into xmax, then
-	 *		   is swapped out before acquiring ProcArrayLock.
-	 *		3. Tx New gets new transaction ID (>= S' xmax),
-	 *		   makes changes and commits.
-	 *		4. Tx Old changes some row R changed by Tx New and commits.
-	 *		5. Tx S finishes getting its snapshot data.  It sees Tx Old as
-	 *		   done, but sees Tx New as still running (since New >= xmax).
-	 *
-	 * Now S will see R changed by both Tx Old and Tx New, *but* does not
-	 * see other changes made by Tx New.  If S is supposed to be in
-	 * Serializable mode, this is wrong.
-	 *
-	 * By locking ProcArrayLock before we read xmax, we ensure that TX Old
-	 * cannot exit the set of running transactions seen by Tx S.  Therefore
-	 * both Old and New will be seen as still running => no inconsistency.
-	 *--------------------
+	 * ProcArrayLock.  It's not good because ReadNewTransactionId() does
+	 * LWLockAcquire(XidGenLock), but *necessary*.  See discussion in
+	 * src/backend/access/transam/README.
 	 */
-
 	xmax = ReadNewTransactionId();
 
 	/* initialize xmin calculation with xmax */
@@ -1147,9 +1107,10 @@ XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
 
 	/*
 	 * We must hold ProcArrayLock exclusively in order to remove transactions
-	 * from the PGPROC array.  (See notes in GetSnapshotData.)	It's possible
-	 * this could be relaxed since we know this routine is only used to abort
-	 * subtransactions, but pending closer analysis we'd best be conservative.
+	 * from the PGPROC array.  (See src/backend/access/transam/README.)  It's
+	 * possible this could be relaxed since we know this routine is only used
+	 * to abort subtransactions, but pending closer analysis we'd best be
+	 * conservative.
 	 */
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 
-- 
GitLab