Thread

  1. [PATCH v5 1/3] Move code around in StartupXLOG().

    Heikki Linnakangas <heikki.linnakangas@iki.fi> — 2021-07-31T12:06:32Z

    This is the order that things will happen with the next commit, this
    makes it more explicit. To aid review, I added "BEGIN/END function"
    comments to mark which blocks of code are moved to separate functions in
    in the next commit.
    ---
     src/backend/access/transam/xlog.c | 469 ++++++++++++++++--------------
     1 file changed, 254 insertions(+), 215 deletions(-)
    
    diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
    index efb3ca273ed..7d30ef4de77 100644
    --- a/src/backend/access/transam/xlog.c
    +++ b/src/backend/access/transam/xlog.c
    @@ -882,7 +882,6 @@ static MemoryContext walDebugCxt = NULL;
     
     static void readRecoverySignalFile(void);
     static void validateRecoveryParameters(void);
    -static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
     static bool recoveryStopsBefore(XLogReaderState *record);
     static bool recoveryStopsAfter(XLogReaderState *record);
     static char *getRecoveryStopReason(void);
    @@ -5593,10 +5592,10 @@ validateRecoveryParameters(void)
     }
     
     /*
    - * Exit archive-recovery state
    + * Initialize the first WAL segment on new timeline.
      */
     static void
    -exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
    +XLogInitNewTimeline(TimeLineID endTLI, XLogRecPtr endOfLog)
     {
     	char		xlogfname[MAXFNAMELEN];
     	XLogSegNo	endLogSegNo;
    @@ -5605,26 +5604,11 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
     	/* we always switch to a new timeline after archive recovery */
     	Assert(endTLI != ThisTimeLineID);
     
    -	/*
    -	 * We are no longer in archive recovery state.
    -	 */
    -	InArchiveRecovery = false;
    -
     	/*
     	 * Update min recovery point one last time.
     	 */
     	UpdateMinRecoveryPoint(InvalidXLogRecPtr, true);
     
    -	/*
    -	 * If the ending log segment is still open, close it (to avoid problems on
    -	 * Windows with trying to rename or delete an open file).
    -	 */
    -	if (readFile >= 0)
    -	{
    -		close(readFile);
    -		readFile = -1;
    -	}
    -
     	/*
     	 * Calculate the last segment on the old timeline, and the first segment
     	 * on the new timeline. If the switch happens in the middle of a segment,
    @@ -5682,19 +5666,6 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog)
     	 */
     	XLogFileName(xlogfname, ThisTimeLineID, startLogSegNo, wal_segment_size);
     	XLogArchiveCleanup(xlogfname);
    -
    -	/*
    -	 * Remove the signal files out of the way, so that we don't accidentally
    -	 * re-enter archive recovery mode in a subsequent crash.
    -	 */
    -	if (standby_signal_file_found)
    -		durable_unlink(STANDBY_SIGNAL_FILE, FATAL);
    -
    -	if (recovery_signal_file_found)
    -		durable_unlink(RECOVERY_SIGNAL_FILE, FATAL);
    -
    -	ereport(LOG,
    -			(errmsg("archive recovery complete")));
     }
     
     /*
    @@ -6498,12 +6469,12 @@ StartupXLOG(void)
     				checkPointLoc,
     				EndOfLog;
     	TimeLineID	EndOfLogTLI;
    +	char	   *recoveryStopReason;
     	TimeLineID	PrevTimeLineID;
     	XLogRecord *record;
     	TransactionId oldestActiveXID;
     	bool		backupEndRequired = false;
     	bool		backupFromStandby = false;
    -	DBState		dbstate_at_startup;
     	XLogReaderState *xlogreader;
     	XLogPageReadPrivate private;
     	bool		promoted = false;
    @@ -6611,6 +6582,8 @@ StartupXLOG(void)
     		SyncDataDirectory();
     	}
     
    +	/*---- BEGIN InitWalRecovery ----*/
    +
     	/*
     	 * Initialize on the assumption we want to recover to the latest timeline
     	 * that's active according to pg_control.
    @@ -6863,20 +6836,6 @@ StartupXLOG(void)
     		wasShutdown = ((record->xl_info & ~XLR_INFO_MASK) == XLOG_CHECKPOINT_SHUTDOWN);
     	}
     
    -	/*
    -	 * Clear out any old relcache cache files.  This is *necessary* if we do
    -	 * any WAL replay, since that would probably result in the cache files
    -	 * being out of sync with database reality.  In theory we could leave them
    -	 * in place if the database had been cleanly shut down, but it seems
    -	 * safest to just remove them always and let them be rebuilt during the
    -	 * first backend startup.  These files needs to be removed from all
    -	 * directories including pg_tblspc, however the symlinks are created only
    -	 * after reading tablespace_map file in case of archive recovery from
    -	 * backup, so needs to clear old relcache files here after creating
    -	 * symlinks.
    -	 */
    -	RelationCacheInitFileRemove();
    -
     	/*
     	 * If the location of the checkpoint record is not on the expected
     	 * timeline in the history of the requested timeline, we cannot proceed:
    @@ -6939,9 +6898,113 @@ StartupXLOG(void)
     			(errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
     							 checkPoint.oldestCommitTsXid,
     							 checkPoint.newestCommitTsXid)));
    +
    +	/* sanity checks on the checkpoint record */
     	if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextXid)))
     		ereport(PANIC,
     				(errmsg("invalid next transaction ID")));
    +	if (checkPoint.redo > checkPointLoc)
    +		ereport(PANIC,
    +				(errmsg("invalid redo in checkpoint record")));
    +
    +	/*
    +	 * Check whether we need to force recovery from WAL.  If it appears to
    +	 * have been a clean shutdown and we did not have a recovery signal file,
    +	 * then assume no recovery needed.
    +	 */
    +	if (checkPoint.redo < checkPointLoc)
    +	{
    +		if (wasShutdown)
    +			ereport(PANIC,
    +					(errmsg("invalid redo record in shutdown checkpoint")));
    +		InRecovery = true;
    +	}
    +	else if (ControlFile->state != DB_SHUTDOWNED)
    +		InRecovery = true;
    +	else if (ArchiveRecoveryRequested)
    +	{
    +		/* force recovery due to presence of recovery signal file */
    +		InRecovery = true;
    +	}
    +
    +	/*
    +	 * If recovery is needed, update our in-memory copy of pg_control to show
    +	 * that we are recovering and to show the selected checkpoint as the place
    +	 * we are starting from. We also mark pg_control with any minimum recovery
    +	 * stop point obtained from a backup history file.
    +	 *
    +	 * We don't write the changes to disk yet, though. Only do that after
    +	 * initializing various subsystems.
    +	 */
    +	if (InRecovery)
    +	{
    +		DBState		dbstate_at_startup;
    +
    +		dbstate_at_startup = ControlFile->state;
    +		if (InArchiveRecovery)
    +		{
    +			ControlFile->state = DB_IN_ARCHIVE_RECOVERY;
    +		}
    +		else
    +		{
    +			ereport(LOG,
    +					(errmsg("database system was not properly shut down; "
    +							"automatic recovery in progress")));
    +			if (recoveryTargetTLI > ControlFile->checkPointCopy.ThisTimeLineID)
    +				ereport(LOG,
    +						(errmsg("crash recovery starts in timeline %u "
    +								"and has target timeline %u",
    +								ControlFile->checkPointCopy.ThisTimeLineID,
    +								recoveryTargetTLI)));
    +			ControlFile->state = DB_IN_CRASH_RECOVERY;
    +		}
    +		ControlFile->checkPoint = checkPointLoc;
    +		ControlFile->checkPointCopy = checkPoint;
    +		if (InArchiveRecovery)
    +		{
    +			/* initialize minRecoveryPoint if not set yet */
    +			if (ControlFile->minRecoveryPoint < checkPoint.redo)
    +			{
    +				ControlFile->minRecoveryPoint = checkPoint.redo;
    +				ControlFile->minRecoveryPointTLI = checkPoint.ThisTimeLineID;
    +			}
    +		}
    +
    +		/*
    +		 * Set backupStartPoint if we're starting recovery from a base backup.
    +		 *
    +		 * Also set backupEndPoint and use minRecoveryPoint as the backup end
    +		 * location if we're starting recovery from a base backup which was
    +		 * taken from a standby. In this case, the database system status in
    +		 * pg_control must indicate that the database was already in recovery.
    +		 * Usually that will be DB_IN_ARCHIVE_RECOVERY but also can be
    +		 * DB_SHUTDOWNED_IN_RECOVERY if recovery previously was interrupted
    +		 * before reaching this point; e.g. because restore_command or
    +		 * primary_conninfo were faulty.
    +		 *
    +		 * Any other state indicates that the backup somehow became corrupted
    +		 * and we can't sensibly continue with recovery.
    +		 */
    +		if (haveBackupLabel)
    +		{
    +			ControlFile->backupStartPoint = checkPoint.redo;
    +			ControlFile->backupEndRequired = backupEndRequired;
    +
    +			if (backupFromStandby)
    +			{
    +				if (dbstate_at_startup != DB_IN_ARCHIVE_RECOVERY &&
    +					dbstate_at_startup != DB_SHUTDOWNED_IN_RECOVERY)
    +					ereport(FATAL,
    +							(errmsg("backup_label contains data inconsistent with control file"),
    +							 errhint("This means that the backup is corrupted and you will "
    +									 "have to use another backup for recovery.")));
    +				ControlFile->backupEndPoint = ControlFile->minRecoveryPoint;
    +			}
    +		}
    +		ControlFile->time = (pg_time_t) time(NULL);
    +	}
    +
    +	/*---- END InitWalRecovery ----*/
     
     	/* initialize shared memory variables from the checkpoint record */
     	ShmemVariableCache->nextXid = checkPoint.nextXid;
    @@ -6955,6 +7018,20 @@ StartupXLOG(void)
     					 checkPoint.newestCommitTsXid);
     	XLogCtl->ckptFullXid = checkPoint.nextXid;
     
    +	/*
    +	 * Clear out any old relcache cache files.  This is *necessary* if we do
    +	 * any WAL replay, since that would probably result in the cache files
    +	 * being out of sync with database reality.  In theory we could leave them
    +	 * in place if the database had been cleanly shut down, but it seems
    +	 * safest to just remove them always and let them be rebuilt during the
    +	 * first backend startup.  These files needs to be removed from all
    +	 * directories including pg_tblspc, however the symlinks are created only
    +	 * after reading tablespace_map file in case of archive recovery from
    +	 * backup, so needs to clear old relcache files here after creating
    +	 * symlinks.
    +	 */
    +	RelationCacheInitFileRemove();
    +
     	/*
     	 * Initialize replication slots, before there's a chance to remove
     	 * required resources.
    @@ -7039,140 +7116,29 @@ StartupXLOG(void)
     	RedoRecPtr = XLogCtl->RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo;
     	doPageWrites = lastFullPageWrites;
     
    -	if (RecPtr < checkPoint.redo)
    -		ereport(PANIC,
    -				(errmsg("invalid redo in checkpoint record")));
    -
    -	/*
    -	 * Check whether we need to force recovery from WAL.  If it appears to
    -	 * have been a clean shutdown and we did not have a recovery signal file,
    -	 * then assume no recovery needed.
    -	 */
    -	if (checkPoint.redo < RecPtr)
    -	{
    -		if (wasShutdown)
    -			ereport(PANIC,
    -					(errmsg("invalid redo record in shutdown checkpoint")));
    -		InRecovery = true;
    -	}
    -	else if (ControlFile->state != DB_SHUTDOWNED)
    -		InRecovery = true;
    -	else if (ArchiveRecoveryRequested)
    -	{
    -		/* force recovery due to presence of recovery signal file */
    -		InRecovery = true;
    -	}
    -
     	/* REDO */
     	if (InRecovery)
     	{
     		int			rmid;
     
    +		/* Initialize state for RecoveryInProgress() */
    +		SpinLockAcquire(&XLogCtl->info_lck);
    +		if (InArchiveRecovery)
    +			XLogCtl->SharedRecoveryState = RECOVERY_STATE_ARCHIVE;
    +		else
    +			XLogCtl->SharedRecoveryState = RECOVERY_STATE_CRASH;
    +		SpinLockRelease(&XLogCtl->info_lck);
    +
     		/*
     		 * Update pg_control to show that we are recovering and to show the
     		 * selected checkpoint as the place we are starting from. We also mark
     		 * pg_control with any minimum recovery stop point obtained from a
     		 * backup history file.
    -		 */
    -		dbstate_at_startup = ControlFile->state;
    -		if (InArchiveRecovery)
    -		{
    -			ControlFile->state = DB_IN_ARCHIVE_RECOVERY;
    -
    -			SpinLockAcquire(&XLogCtl->info_lck);
    -			XLogCtl->SharedRecoveryState = RECOVERY_STATE_ARCHIVE;
    -			SpinLockRelease(&XLogCtl->info_lck);
    -		}
    -		else
    -		{
    -			ereport(LOG,
    -					(errmsg("database system was not properly shut down; "
    -							"automatic recovery in progress")));
    -			if (recoveryTargetTLI > ControlFile->checkPointCopy.ThisTimeLineID)
    -				ereport(LOG,
    -						(errmsg("crash recovery starts in timeline %u "
    -								"and has target timeline %u",
    -								ControlFile->checkPointCopy.ThisTimeLineID,
    -								recoveryTargetTLI)));
    -			ControlFile->state = DB_IN_CRASH_RECOVERY;
    -
    -			SpinLockAcquire(&XLogCtl->info_lck);
    -			XLogCtl->SharedRecoveryState = RECOVERY_STATE_CRASH;
    -			SpinLockRelease(&XLogCtl->info_lck);
    -		}
    -		ControlFile->checkPoint = checkPointLoc;
    -		ControlFile->checkPointCopy = checkPoint;
    -		if (InArchiveRecovery)
    -		{
    -			/* initialize minRecoveryPoint if not set yet */
    -			if (ControlFile->minRecoveryPoint < checkPoint.redo)
    -			{
    -				ControlFile->minRecoveryPoint = checkPoint.redo;
    -				ControlFile->minRecoveryPointTLI = checkPoint.ThisTimeLineID;
    -			}
    -		}
    -
    -		/*
    -		 * Set backupStartPoint if we're starting recovery from a base backup.
    -		 *
    -		 * Also set backupEndPoint and use minRecoveryPoint as the backup end
    -		 * location if we're starting recovery from a base backup which was
    -		 * taken from a standby. In this case, the database system status in
    -		 * pg_control must indicate that the database was already in recovery.
    -		 * Usually that will be DB_IN_ARCHIVE_RECOVERY but also can be
    -		 * DB_SHUTDOWNED_IN_RECOVERY if recovery previously was interrupted
    -		 * before reaching this point; e.g. because restore_command or
    -		 * primary_conninfo were faulty.
     		 *
    -		 * Any other state indicates that the backup somehow became corrupted
    -		 * and we can't sensibly continue with recovery.
    +		 * No need to hold ControlFileLock yet, we aren't up far enough
     		 */
    -		if (haveBackupLabel)
    -		{
    -			ControlFile->backupStartPoint = checkPoint.redo;
    -			ControlFile->backupEndRequired = backupEndRequired;
    -
    -			if (backupFromStandby)
    -			{
    -				if (dbstate_at_startup != DB_IN_ARCHIVE_RECOVERY &&
    -					dbstate_at_startup != DB_SHUTDOWNED_IN_RECOVERY)
    -					ereport(FATAL,
    -							(errmsg("backup_label contains data inconsistent with control file"),
    -							 errhint("This means that the backup is corrupted and you will "
    -									 "have to use another backup for recovery.")));
    -				ControlFile->backupEndPoint = ControlFile->minRecoveryPoint;
    -			}
    -		}
    -		ControlFile->time = (pg_time_t) time(NULL);
    -		/* No need to hold ControlFileLock yet, we aren't up far enough */
     		UpdateControlFile();
     
    -		/*
    -		 * Initialize our local copy of minRecoveryPoint.  When doing crash
    -		 * recovery we want to replay up to the end of WAL.  Particularly, in
    -		 * the case of a promoted standby minRecoveryPoint value in the
    -		 * control file is only updated after the first checkpoint.  However,
    -		 * if the instance crashes before the first post-recovery checkpoint
    -		 * is completed then recovery will use a stale location causing the
    -		 * startup process to think that there are still invalid page
    -		 * references when checking for data consistency.
    -		 */
    -		if (InArchiveRecovery)
    -		{
    -			minRecoveryPoint = ControlFile->minRecoveryPoint;
    -			minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
    -		}
    -		else
    -		{
    -			minRecoveryPoint = InvalidXLogRecPtr;
    -			minRecoveryPointTLI = 0;
    -		}
    -
    -		/*
    -		 * Reset pgstat data, because it may be invalid after recovery.
    -		 */
    -		pgstat_reset_all();
    -
     		/*
     		 * If there was a backup label file, it's done its job and the info
     		 * has now been propagated into pg_control.  We must get rid of the
    @@ -7200,6 +7166,32 @@ StartupXLOG(void)
     			durable_rename(TABLESPACE_MAP, TABLESPACE_MAP_OLD, FATAL);
     		}
     
    +		/*
    +		 * Initialize our local copy of minRecoveryPoint.  When doing crash
    +		 * recovery we want to replay up to the end of WAL.  Particularly, in
    +		 * the case of a promoted standby minRecoveryPoint value in the
    +		 * control file is only updated after the first checkpoint.  However,
    +		 * if the instance crashes before the first post-recovery checkpoint
    +		 * is completed then recovery will use a stale location causing the
    +		 * startup process to think that there are still invalid page
    +		 * references when checking for data consistency.
    +		 */
    +		if (InArchiveRecovery)
    +		{
    +			minRecoveryPoint = ControlFile->minRecoveryPoint;
    +			minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
    +		}
    +		else
    +		{
    +			minRecoveryPoint = InvalidXLogRecPtr;
    +			minRecoveryPointTLI = 0;
    +		}
    +
    +		/*
    +		 * Reset pgstat data, because it may be invalid after recovery.
    +		 */
    +		pgstat_reset_all();
    +
     		/* Check that the GUCs used to generate the WAL allow recovery */
     		CheckRequiredParameterValues();
     
    @@ -7283,12 +7275,7 @@ StartupXLOG(void)
     			}
     		}
     
    -		/* Initialize resource managers */
    -		for (rmid = 0; rmid <= RM_MAX_ID; rmid++)
    -		{
    -			if (RmgrTable[rmid].rm_startup != NULL)
    -				RmgrTable[rmid].rm_startup();
    -		}
    +		/*---- BEGIN PerformWalRecovery ----*/
     
     		/*
     		 * Initialize shared variables for tracking progress of WAL replay, as
    @@ -7296,7 +7283,7 @@ StartupXLOG(void)
     		 * checkpoint record itself, if it's a shutdown checkpoint).
     		 */
     		SpinLockAcquire(&XLogCtl->info_lck);
    -		if (checkPoint.redo < RecPtr)
    +		if (checkPoint.redo < checkPointLoc)
     			XLogCtl->replayEndRecPtr = checkPoint.redo;
     		else
     			XLogCtl->replayEndRecPtr = EndRecPtr;
    @@ -7341,7 +7328,7 @@ StartupXLOG(void)
     		 * Find the first record that logically follows the checkpoint --- it
     		 * might physically precede it, though.
     		 */
    -		if (checkPoint.redo < RecPtr)
    +		if (checkPoint.redo < checkPointLoc)
     		{
     			/* back up to find the record */
     			XLogBeginRead(xlogreader, checkPoint.redo);
    @@ -7350,6 +7337,7 @@ StartupXLOG(void)
     		else
     		{
     			/* just have to read next record after CheckPoint */
    +			Assert(RecPtr == checkPointLoc);
     			record = ReadRecord(xlogreader, LOG, false);
     		}
     
    @@ -7363,6 +7351,13 @@ StartupXLOG(void)
     
     			InRedo = true;
     
    +			/* Initialize resource managers */
    +			for (rmid = 0; rmid <= RM_MAX_ID; rmid++)
    +			{
    +				if (RmgrTable[rmid].rm_startup != NULL)
    +					RmgrTable[rmid].rm_startup();
    +			}
    +
     			ereport(LOG,
     					(errmsg("redo starts at %X/%X",
     							LSN_FORMAT_ARGS(ReadRecPtr))));
    @@ -7655,8 +7650,12 @@ StartupXLOG(void)
     			!reachedRecoveryTarget)
     			ereport(FATAL,
     					(errmsg("recovery ended before configured recovery target was reached")));
    +
    +		/*---- END PerformWalRecovery ----*/
     	}
     
    +	/*---- BEGIN FinishWalRecovery ----*/
    +
     	/*
     	 * Kill WAL receiver, if it's still running, before we continue to write
     	 * the startup checkpoint record. It will trump over the checkpoint and
    @@ -7664,23 +7663,6 @@ StartupXLOG(void)
     	 */
     	XLogShutdownWalRcv();
     
    -	/*
    -	 * Reset unlogged relations to the contents of their INIT fork. This is
    -	 * done AFTER recovery is complete so as to include any unlogged relations
    -	 * created during recovery, but BEFORE recovery is marked as having
    -	 * completed successfully. Otherwise we'd not retry if any of the post
    -	 * end-of-recovery steps fail.
    -	 */
    -	if (InRecovery)
    -		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
    -
    -	/*
    -	 * We don't need the latch anymore. It's not strictly necessary to disown
    -	 * it, but let's do it for the sake of tidiness.
    -	 */
    -	if (ArchiveRecoveryRequested)
    -		DisownLatch(&XLogCtl->recoveryWakeupLatch);
    -
     	/*
     	 * We are now done reading the xlog from stream. Turn off streaming
     	 * recovery to force fetching the files (which would be required at end of
    @@ -7709,6 +7691,32 @@ StartupXLOG(void)
     	 */
     	EndOfLogTLI = xlogreader->seg.ws_tli;
     
    +	if (ArchiveRecoveryRequested)
    +	{
    +		/*
    +		 * We are no longer in archive recovery state.
    +		 *
    +		 * We are now done reading the old WAL.  Turn off archive fetching if
    +		 * it was active.
    +		 */
    +		Assert(InArchiveRecovery);
    +		InArchiveRecovery = false;
    +
    +		/*
    +		 * If the ending log segment is still open, close it (to avoid problems on
    +		 * Windows with trying to rename or delete an open file).
    +		 */
    +		if (readFile >= 0)
    +		{
    +			close(readFile);
    +			readFile = -1;
    +		}
    +	}
    +
    +	recoveryStopReason = getRecoveryStopReason();
    +
    +	/*---- END FinishWalRecovery ----*/
    +
     	/*
     	 * Complain if we did not roll forward far enough to render the backup
     	 * dump consistent.  Note: it is indeed okay to look at the local variable
    @@ -7745,6 +7753,16 @@ StartupXLOG(void)
     		}
     	}
     
    +	/*
    +	 * Reset unlogged relations to the contents of their INIT fork. This is
    +	 * done AFTER recovery is complete so as to include any unlogged relations
    +	 * created during recovery, but BEFORE recovery is marked as having
    +	 * completed successfully. Otherwise we'd not retry if any of the post
    +	 * end-of-recovery steps fail.
    +	 */
    +	if (InRecovery)
    +		ResetUnloggedRelations(UNLOGGED_RELATION_INIT);
    +
     	/*
     	 * Pre-scan prepared transactions to find out the range of XIDs present.
     	 * This information is not quite needed yet, but it is positioned here so
    @@ -7753,8 +7771,8 @@ StartupXLOG(void)
     	oldestActiveXID = PrescanPreparedTransactions(NULL, NULL);
     
     	/*
    -	 * Allow ordinary WAL segment creation before any exitArchiveRecovery(),
    -	 * which sometimes creates a segment, and after the last ReadRecord().
    +	 * Allow ordinary WAL segment creation before switching to a new timeline,
    +	 * which creates a new segment, and after the last ReadRecord().
     	 */
     	LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
     	XLogCtl->InstallXLogFileSegmentActive = true;
    @@ -7777,24 +7795,26 @@ StartupXLOG(void)
     	PrevTimeLineID = ThisTimeLineID;
     	if (ArchiveRecoveryRequested)
     	{
    -		char	   *reason;
    -		char		recoveryPath[MAXPGPATH];
    -
    -		Assert(InArchiveRecovery);
    -
     		ThisTimeLineID = findNewestTimeLine(recoveryTargetTLI) + 1;
     		ereport(LOG,
     				(errmsg("selected new timeline ID: %u", ThisTimeLineID)));
     
    -		reason = getRecoveryStopReason();
    +		/*
    +		 * Make a writable copy of the last WAL segment.  (Note that we also
    +		 * have a copy of the last block of the old WAL in readBuf; we will
    +		 * use that below.)
    +		 */
    +		XLogInitNewTimeline(EndOfLogTLI, EndOfLog);
     
     		/*
    -		 * We are now done reading the old WAL.  Turn off archive fetching if
    -		 * it was active, and make a writable copy of the last WAL segment.
    -		 * (Note that we also have a copy of the last block of the old WAL in
    -		 * readBuf; we will use that below.)
    +		 * Remove the signal files out of the way, so that we don't accidentally
    +		 * re-enter archive recovery mode in a subsequent crash.
     		 */
    -		exitArchiveRecovery(EndOfLogTLI, EndOfLog);
    +		if (standby_signal_file_found)
    +			durable_unlink(STANDBY_SIGNAL_FILE, FATAL);
    +
    +		if (recovery_signal_file_found)
    +			durable_unlink(RECOVERY_SIGNAL_FILE, FATAL);
     
     		/*
     		 * Write the timeline history file, and have it archived. After this
    @@ -7807,18 +7827,10 @@ StartupXLOG(void)
     		 * between here and writing the end-of-recovery record.
     		 */
     		writeTimeLineHistory(ThisTimeLineID, recoveryTargetTLI,
    -							 EndRecPtr, reason);
    +							 EndOfLog, recoveryStopReason);
     
    -		/*
    -		 * Since there might be a partial WAL segment named RECOVERYXLOG, get
    -		 * rid of it.
    -		 */
    -		snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYXLOG");
    -		unlink(recoveryPath);	/* ignore any error */
    -
    -		/* Get rid of any remaining recovered timeline-history file, too */
    -		snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYHISTORY");
    -		unlink(recoveryPath);	/* ignore any error */
    +		ereport(LOG,
    +				(errmsg("archive recovery complete")));
     	}
     
     	/* Save the selected TimeLineID in shared memory, too */
    @@ -8054,6 +8066,8 @@ StartupXLOG(void)
     	if (standbyState != STANDBY_DISABLED)
     		ShutdownRecoveryTransactionEnvironment();
     
    +	/*---- BEGIN FreeWalRecovery ----*/
    +
     	/* Shut down xlogreader */
     	if (readFile >= 0)
     	{
    @@ -8062,6 +8076,31 @@ StartupXLOG(void)
     	}
     	XLogReaderFree(xlogreader);
     
    +	if (ArchiveRecoveryRequested)
    +	{
    +		char		recoveryPath[MAXPGPATH];
    +
    +		/*
    +		 * Since there might be a partial WAL segment named RECOVERYXLOG, get
    +		 * rid of it.
    +		 */
    +		snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYXLOG");
    +		unlink(recoveryPath);	/* ignore any error */
    +
    +		/* Get rid of any remaining recovered timeline-history file, too */
    +		snprintf(recoveryPath, MAXPGPATH, XLOGDIR "/RECOVERYHISTORY");
    +		unlink(recoveryPath);	/* ignore any error */
    +	}
    +
    +	/*
    +	 * We don't need the latch anymore. It's not strictly necessary to disown
    +	 * it, but let's do it for the sake of tidiness.
    +	 */
    +	if (ArchiveRecoveryRequested)
    +		DisownLatch(&XLogCtl->recoveryWakeupLatch);
    +
    +	/*---- END FreeWalRecovery ----*/
    +
     	/*
     	 * If any of the critical GUCs have changed, log them before we allow
     	 * backends to write WAL.
    -- 
    2.30.2
    
    
    --------------F589ECF1004AF34308A0B206
    Content-Type: text/x-patch; charset=UTF-8;
     name="v5-0002-Split-xlog.c-into-xlog.c-and-xlogrecovery.c.patch"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: attachment;
     filename*0="v5-0002-Split-xlog.c-into-xlog.c-and-xlogrecovery.c.patch"