mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Fix MVCC bug with prepared xact with subxacts on standby
We did not recover the subtransaction IDs of prepared transactions when starting a hot standby from a shutdown checkpoint. As a result, such subtransactions were considered as aborted, rather than in-progress. That would lead to hint bits being set incorrectly, and the subtransactions suddenly becoming visible to old snapshots when the prepared transaction was committed. To fix, update pg_subtrans with prepared transactions's subxids when starting hot standby from a shutdown checkpoint. The snapshots taken from that state need to be marked as "suboverflowed", so that we also check the pg_subtrans. Backport to all supported versions. Discussion: https://www.postgresql.org/message-id/6b852e98-2d49-4ca1-9e95-db419a2696e0@iki.fi
This commit is contained in:
		@@ -2013,9 +2013,8 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
 | 
			
		||||
 * This is never called at the end of recovery - we use
 | 
			
		||||
 * RecoverPreparedTransactions() at that point.
 | 
			
		||||
 *
 | 
			
		||||
 * The lack of calls to SubTransSetParent() calls here is by design;
 | 
			
		||||
 * those calls are made by RecoverPreparedTransactions() at the end of recovery
 | 
			
		||||
 * for those xacts that need this.
 | 
			
		||||
 * This updates pg_subtrans, so that any subtransactions will be correctly
 | 
			
		||||
 * seen as in-progress in snapshots taken during recovery.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
StandbyRecoverPreparedTransactions(void)
 | 
			
		||||
@@ -2035,7 +2034,7 @@ StandbyRecoverPreparedTransactions(void)
 | 
			
		||||
 | 
			
		||||
		buf = ProcessTwoPhaseBuffer(xid,
 | 
			
		||||
									gxact->prepare_start_lsn,
 | 
			
		||||
									gxact->ondisk, false, false);
 | 
			
		||||
									gxact->ondisk, true, false);
 | 
			
		||||
		if (buf != NULL)
 | 
			
		||||
			pfree(buf);
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -5273,6 +5273,9 @@ StartupXLOG(void)
 | 
			
		||||
				RunningTransactionsData running;
 | 
			
		||||
				TransactionId latestCompletedXid;
 | 
			
		||||
 | 
			
		||||
				/* Update pg_subtrans entries for any prepared transactions */
 | 
			
		||||
				StandbyRecoverPreparedTransactions();
 | 
			
		||||
 | 
			
		||||
				/*
 | 
			
		||||
				 * Construct a RunningTransactions snapshot representing a
 | 
			
		||||
				 * shut down server, with only prepared transactions still
 | 
			
		||||
@@ -5281,7 +5284,7 @@ StartupXLOG(void)
 | 
			
		||||
				 */
 | 
			
		||||
				running.xcnt = nxids;
 | 
			
		||||
				running.subxcnt = 0;
 | 
			
		||||
				running.subxid_overflow = false;
 | 
			
		||||
				running.subxid_status = SUBXIDS_IN_SUBTRANS;
 | 
			
		||||
				running.nextXid = XidFromFullTransactionId(checkPoint.nextXid);
 | 
			
		||||
				running.oldestRunningXid = oldestActiveXID;
 | 
			
		||||
				latestCompletedXid = XidFromFullTransactionId(checkPoint.nextXid);
 | 
			
		||||
@@ -5291,8 +5294,6 @@ StartupXLOG(void)
 | 
			
		||||
				running.xids = xids;
 | 
			
		||||
 | 
			
		||||
				ProcArrayApplyRecoveryInfo(&running);
 | 
			
		||||
 | 
			
		||||
				StandbyRecoverPreparedTransactions();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -7647,6 +7648,9 @@ xlog_redo(XLogReaderState *record)
 | 
			
		||||
 | 
			
		||||
			oldestActiveXID = PrescanPreparedTransactions(&xids, &nxids);
 | 
			
		||||
 | 
			
		||||
			/* Update pg_subtrans entries for any prepared transactions */
 | 
			
		||||
			StandbyRecoverPreparedTransactions();
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * Construct a RunningTransactions snapshot representing a shut
 | 
			
		||||
			 * down server, with only prepared transactions still alive. We're
 | 
			
		||||
@@ -7655,7 +7659,7 @@ xlog_redo(XLogReaderState *record)
 | 
			
		||||
			 */
 | 
			
		||||
			running.xcnt = nxids;
 | 
			
		||||
			running.subxcnt = 0;
 | 
			
		||||
			running.subxid_overflow = false;
 | 
			
		||||
			running.subxid_status = SUBXIDS_IN_SUBTRANS;
 | 
			
		||||
			running.nextXid = XidFromFullTransactionId(checkPoint.nextXid);
 | 
			
		||||
			running.oldestRunningXid = oldestActiveXID;
 | 
			
		||||
			latestCompletedXid = XidFromFullTransactionId(checkPoint.nextXid);
 | 
			
		||||
@@ -7665,8 +7669,6 @@ xlog_redo(XLogReaderState *record)
 | 
			
		||||
			running.xids = xids;
 | 
			
		||||
 | 
			
		||||
			ProcArrayApplyRecoveryInfo(&running);
 | 
			
		||||
 | 
			
		||||
			StandbyRecoverPreparedTransactions();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user