From 2ab3934f28fc986904afe8131aaa639713e4836c Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Thu, 6 Oct 2022 11:01:22 +0000
Subject: [PATCH v3] Add functions for xlogbackup.c to call back into xlog.c

---
 src/backend/access/transam/xlog.c | 191 ++++++++++++++++++++----------
 src/include/access/xlog.h         |  15 +++
 2 files changed, 142 insertions(+), 64 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 27085b15a8..3af9cc86c0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -8315,10 +8315,7 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
 	 * forcePageWrites, to ensure adequate interlocking against
 	 * XLogInsertRecord().
 	 */
-	WALInsertLockAcquireExclusive();
-	XLogCtl->Insert.runningBackups++;
-	XLogCtl->Insert.forcePageWrites = true;
-	WALInsertLockRelease();
+	SetXLogBackupRelatedInfo();
 
 	/* Ensure we release forcePageWrites if fail below */
 	PG_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0);
@@ -8356,6 +8353,8 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
 		do
 		{
 			bool		checkpointfpw;
+			ControlFileData	*ControlFile;
+
 
 			/*
 			 * Force a CHECKPOINT.  Aside from being necessary to prevent torn
@@ -8384,6 +8383,8 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
 			 * to restore starting from the checkpoint is precisely the REDO
 			 * pointer.
 			 */
+			ControlFile = GetControlFile();
+
 			LWLockAcquire(ControlFileLock, LW_SHARED);
 			state->checkpointloc = ControlFile->checkPoint;
 			state->startpoint = ControlFile->checkPointCopy.redo;
@@ -8400,9 +8401,7 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
 				 * (i.e., since last restartpoint used as backup starting
 				 * checkpoint) contain full-page writes.
 				 */
-				SpinLockAcquire(&XLogCtl->info_lck);
-				recptr = XLogCtl->lastFpwDisableRecPtr;
-				SpinLockRelease(&XLogCtl->info_lck);
+				recptr = GetlastFpwDisableRecPtr();
 
 				if (!checkpointfpw || state->startpoint <= recptr)
 					ereport(ERROR,
@@ -8435,13 +8434,7 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
 			 * taking a checkpoint right after another is not that expensive
 			 * either because only few buffers have been dirtied yet.
 			 */
-			WALInsertLockAcquireExclusive();
-			if (XLogCtl->Insert.lastBackupStart < state->startpoint)
-			{
-				XLogCtl->Insert.lastBackupStart = state->startpoint;
-				gotUniqueStartpoint = true;
-			}
-			WALInsertLockRelease();
+			gotUniqueStartpoint = SetlastBackupStart(state->startpoint);
 		} while (!gotUniqueStartpoint);
 
 		/*
@@ -8546,16 +8539,7 @@ static void
 pg_backup_start_callback(int code, Datum arg)
 {
 	/* Update backup counters and forcePageWrites on failure */
-	WALInsertLockAcquireExclusive();
-
-	Assert(XLogCtl->Insert.runningBackups > 0);
-	XLogCtl->Insert.runningBackups--;
-
-	if (XLogCtl->Insert.runningBackups == 0)
-	{
-		XLogCtl->Insert.forcePageWrites = false;
-	}
-	WALInsertLockRelease();
+	ResetXLogBackupRelatedInfo(NULL);
 }
 
 /*
@@ -8592,6 +8576,7 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
 	int			seconds_before_warning;
 	int			waits = 0;
 	bool		reported_waiting = false;
+	SessionBackupState	session_backup_state = SESSION_BACKUP_NONE;
 
 	Assert(state != NULL);
 
@@ -8613,33 +8598,11 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
 	 * Note that CHECK_FOR_INTERRUPTS() must not occur while updating them.
 	 * Otherwise they can be updated inconsistently, and which might cause
 	 * do_pg_abort_backup() to fail.
-	 */
-	WALInsertLockAcquireExclusive();
-
-	/*
-	 * It is expected that each do_pg_backup_start() call is matched by
-	 * exactly one do_pg_backup_stop() call.
-	 */
-	Assert(XLogCtl->Insert.runningBackups > 0);
-	XLogCtl->Insert.runningBackups--;
-
-	if (XLogCtl->Insert.runningBackups == 0)
-	{
-		XLogCtl->Insert.forcePageWrites = false;
-	}
-
-	/*
-	 * Clean up session-level lock.
 	 *
-	 * You might think that WALInsertLockRelease() can be called before
-	 * cleaning up session-level lock because session-level lock doesn't need
-	 * to be protected with WAL insertion lock. But since
-	 * CHECK_FOR_INTERRUPTS() can occur in it, session-level lock must be
-	 * cleaned up before it.
+	 * It is expected that each do_pg_backup_start() call is matched by exactly
+	 * one do_pg_backup_stop() call.
 	 */
-	sessionBackupState = SESSION_BACKUP_NONE;
-
-	WALInsertLockRelease();
+	ResetXLogBackupRelatedInfo(&session_backup_state);
 
 	/*
 	 * If we are taking an online backup from the standby, we confirm that the
@@ -8684,14 +8647,13 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
 	if (backup_stopped_in_recovery)
 	{
 		XLogRecPtr	recptr;
+		ControlFileData	*ControlFile;
 
 		/*
 		 * Check to see if all WAL replayed during online backup contain
 		 * full-page writes.
 		 */
-		SpinLockAcquire(&XLogCtl->info_lck);
-		recptr = XLogCtl->lastFpwDisableRecPtr;
-		SpinLockRelease(&XLogCtl->info_lck);
+		recptr = GetlastFpwDisableRecPtr();
 
 		if (state->startpoint <= recptr)
 			ereport(ERROR,
@@ -8704,6 +8666,8 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
 							 "and then try an online backup again.")));
 
 
+		ControlFile = GetControlFile();
+
 		LWLockAcquire(ControlFileLock, LW_SHARED);
 		state->stoppoint = ControlFile->minRecoveryPoint;
 		state->stoptli = ControlFile->minRecoveryPointTLI;
@@ -8725,7 +8689,7 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive)
 		 * Given that we're not in recovery, InsertTimeLineID is set and can't
 		 * change, so we can read it without a lock.
 		 */
-		state->stoptli = XLogCtl->InsertTimeLineID;
+		state->stoptli = GetWALInsertionTimeLine();
 
 		/*
 		 * Force a switch to a new xlog segment file, so that the backup is
@@ -8860,6 +8824,7 @@ void
 do_pg_abort_backup(int code, Datum arg)
 {
 	bool		emit_warning = DatumGetBool(arg);
+	SessionBackupState	session_backup_state = SESSION_BACKUP_NONE;
 
 	/*
 	 * Quick exit if session does not have a running backup.
@@ -8867,17 +8832,7 @@ do_pg_abort_backup(int code, Datum arg)
 	if (sessionBackupState != SESSION_BACKUP_RUNNING)
 		return;
 
-	WALInsertLockAcquireExclusive();
-	Assert(XLogCtl->Insert.runningBackups > 0);
-	XLogCtl->Insert.runningBackups--;
-
-	if (XLogCtl->Insert.runningBackups == 0)
-	{
-		XLogCtl->Insert.forcePageWrites = false;
-	}
-
-	sessionBackupState = SESSION_BACKUP_NONE;
-	WALInsertLockRelease();
+	ResetXLogBackupRelatedInfo(&session_backup_state);
 
 	if (emit_warning)
 		ereport(WARNING,
@@ -8899,6 +8854,114 @@ register_persistent_abort_backup_handler(void)
 	already_done = true;
 }
 
+/*
+ * Utility routine to set the session-level status of a backup running.
+ */
+void
+set_backup_status(SessionBackupState state)
+{
+	sessionBackupState = state;
+}
+
+/*
+ * Get the ControlFile.
+ */
+ControlFileData *
+GetControlFile(void)
+{
+	return ControlFile;
+}
+
+/*
+ * Get the lastFpwDisableRecPtr.
+ */
+XLogRecPtr
+GetlastFpwDisableRecPtr(void)
+{
+	XLogRecPtr recptr;
+
+	SpinLockAcquire(&XLogCtl->info_lck);
+	recptr = XLogCtl->lastFpwDisableRecPtr;
+	SpinLockRelease(&XLogCtl->info_lck);
+
+	return recptr;
+}
+
+/*
+ * Set the lastBackupStar only if it is less than passed-in rectpr and return
+ * true. Otherwise return false.
+ *
+ * Note: For those who want to set lastBackupStar unconditionally, pass rectpr
+ * value as PG_UINT64_MAX, which is higher than any real XLogRecPtr value.
+ */
+bool
+SetlastBackupStart(XLogRecPtr recptr)
+{
+	bool is_set = false;
+
+	WALInsertLockAcquireExclusive();
+	if (XLogCtl->Insert.lastBackupStart < recptr)
+	{
+		XLogCtl->Insert.lastBackupStart = recptr;
+		is_set = true;
+	}
+	WALInsertLockRelease();
+
+	return is_set;
+}
+
+/*
+ * Decrement runningBackups, reset forcePageWrites and session-level status of
+ * a backup running.
+ *
+ * NOTE: This function is tailor-made for use in xlogbackup.c. It doesn't reset
+ * the respective XLogCtl members directly, and acquires and releases locks.
+ * Hence be careful when using it elsewhere.
+ */
+void
+ResetXLogBackupRelatedInfo(SessionBackupState* session_backup_state)
+{
+	WALInsertLockAcquireExclusive();
+
+	Assert(XLogCtl->Insert.runningBackups > 0);
+	XLogCtl->Insert.runningBackups--;
+
+	if (XLogCtl->Insert.runningBackups == 0)
+	{
+		XLogCtl->Insert.forcePageWrites = false;
+	}
+
+	/*
+	 * Set session-level lock if specified.
+	 *
+	 * You might think that WALInsertLockRelease() can be called before
+	 * cleaning up session-level lock because session-level lock doesn't need
+	 * to be protected with WAL insertion lock. But since
+	 * CHECK_FOR_INTERRUPTS() can occur in it, session-level lock must be
+	 * cleaned up before it.
+	 */
+	if (session_backup_state != NULL)
+		set_backup_status(*session_backup_state);
+
+	WALInsertLockRelease();
+}
+
+/*
+ * Increment runningBackups and forcePageWrites.
+ *
+ * NOTE: This function is tailor-made for use in xlogbackup.c. It doesn't set
+ * the respective XLogCtl members directly, and acquires and releases locks.
+ * Hence be careful when using it elsewhere.
+ */
+void
+SetXLogBackupRelatedInfo(void)
+{
+	WALInsertLockAcquireExclusive();
+	XLogCtl->Insert.runningBackups++;
+	XLogCtl->Insert.forcePageWrites = true;
+	WALInsertLockRelease();
+}
+
 /*
  * Get latest WAL insert pointer
  */
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index dce265098e..8be7aa9c8e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -14,6 +14,7 @@
 #include "access/xlogbackup.h"
 #include "access/xlogdefs.h"
 #include "access/xlogreader.h"
+#include "catalog/pg_control.h"
 #include "datatype/timestamp.h"
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
@@ -285,6 +286,20 @@ extern void do_pg_backup_stop(BackupState *state, bool waitforarchive);
 extern void do_pg_abort_backup(int code, Datum arg);
 extern void register_persistent_abort_backup_handler(void);
 extern SessionBackupState get_backup_status(void);
+extern void set_backup_status(SessionBackupState state);
+
+/*
+ * Routines used by xlogbackup.c to call back into xlog.c during backup.
+ *
+ * NOTE: These functions are tailor-made for use in xlogbackup.c. They may or
+ * may not set or get the respective XLogCtl members directly, may acquire and
+ * release various locks. Hence be careful when using them elsewhere.
+ */
+extern ControlFileData *GetControlFile(void);
+extern XLogRecPtr GetlastFpwDisableRecPtr(void);
+extern bool SetlastBackupStart(XLogRecPtr recptr);
+extern void ResetXLogBackupRelatedInfo(SessionBackupState* session_backup_state);
+extern void SetXLogBackupRelatedInfo(void);
 
 /* File path names (all relative to $PGDATA) */
 #define RECOVERY_SIGNAL_FILE	"recovery.signal"
-- 
2.34.1

