v5-0001-Refactor-backup-related-code.patch
application/octet-stream
Filename: v5-0001-Refactor-backup-related-code.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v5-0001
Subject: Refactor backup related code
| File | + | − |
|---|---|---|
| src/backend/access/transam/Makefile | 1 | 0 |
| src/backend/access/transam/xlogbackup.c | 98 | 0 |
| src/backend/access/transam/xlog.c | 80 | 148 |
| src/backend/access/transam/xlogfuncs.c | 15 | 28 |
| src/backend/backup/basebackup.c | 21 | 11 |
| src/include/access/xlogbackup.h | 56 | 0 |
| src/include/access/xlog.h | 4 | 5 |
From e940effe593b18cdd3c9c33dbf3ebaeb74f0b537 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Sat, 17 Sep 2022 07:13:58 +0000
Subject: [PATCH v5] Refactor backup related code
Refactor backup related code, advantages of doing so are following:
1) backup state is more structured now - all in a single structure,
callers can create backup_label contents whenever required, either
during the pg_backup_start or the pg_backup_stop or in between.
2) no parsing of backup_label file lines now, no error checking
for invalid parsing.
3) backup_label and history file contents have most of the things
in common, they can now be created within a single function.
4) makes backup related code extensible and readable.
This introduces new source files xlogbackup.c/.h for backup related
code and adds the new code in there. The xlog.c file has already grown
to ~9000 LOC (as of this time). Eventually, we would want to move
all the backup related code from xlog.c, xlogfuncs.c, elsewhere to
here.
Author: Bharath Rupireddy
Reviewed-by: Michael Paquier
Discussion: https://www.postgresql.org/message-id/CALj2ACVqNUEXkaMKyHHOdvScfN9E4LuCWsX_R-YRNfzQ727CdA%40mail.gmail.com
---
src/backend/access/transam/Makefile | 1 +
src/backend/access/transam/xlog.c | 228 +++++++++---------------
src/backend/access/transam/xlogbackup.c | 98 ++++++++++
src/backend/access/transam/xlogfuncs.c | 43 ++---
src/backend/backup/basebackup.c | 32 ++--
src/include/access/xlog.h | 9 +-
src/include/access/xlogbackup.h | 56 ++++++
7 files changed, 275 insertions(+), 192 deletions(-)
create mode 100644 src/backend/access/transam/xlogbackup.c
create mode 100644 src/include/access/xlogbackup.h
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
index 3e5444a6f7..661c55a9db 100644
--- a/src/backend/access/transam/Makefile
+++ b/src/backend/access/transam/Makefile
@@ -29,6 +29,7 @@ OBJS = \
xact.o \
xlog.o \
xlogarchive.o \
+ xlogbackup.o \
xlogfuncs.o \
xloginsert.o \
xlogprefetcher.o \
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 81d339d57d..2f6717e100 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -8236,46 +8236,38 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
/*
* do_pg_backup_start is the workhorse of the user-visible pg_backup_start()
* function. It creates the necessary starting checkpoint and constructs the
- * backup label and tablespace map.
+ * backup state.
*
- * Input parameters are "backupidstr" (the backup label string) and "fast"
- * (if true, we do the checkpoint in immediate mode to make it faster).
+ * Input parameters are "state" (containing backup state), "fast" (if true,
+ * we do the checkpoint in immediate mode to make it faster) and "tablespaces"
+ * (if non-NULL, indicates a list of tablespaceinfo structs describing the
+ * cluster's tablespaces.)
*
- * The backup label and tablespace map contents are appended to *labelfile and
- * *tblspcmapfile, and the caller is responsible for including them in the
- * backup archive as 'backup_label' and 'tablespace_map'.
- * tblspcmapfile is required mainly for tar format in windows as native windows
- * utilities are not able to create symlinks while extracting files from tar.
- * However for consistency and platform-independence, we do it the same way
- * everywhere.
- *
- * If "tablespaces" isn't NULL, it receives a list of tablespaceinfo structs
- * describing the cluster's tablespaces.
+ * The tablespace map contents are appended to backup state's tablespace_map
+ * and the caller is responsible for including it in the backup archive as
+ * 'tablespace_map'. The tablespace_map file is required mainly for tar format
+ * in windows as native windows utilities are not able to create symlinks while
+ * extracting files from tar. However for consistency and
+ * platform-independence, we do it the same way everywhere.
*
* Returns the minimum WAL location that must be present to restore from this
- * backup, and the corresponding timeline ID in *starttli_p.
+ * backup, and the corresponding timeline ID via backup state startpoint and
+ * starttli respectively.
*
* Every successfully started backup must be stopped by calling
- * do_pg_backup_stop() or do_pg_abort_backup(). There can be many
- * backups active at the same time.
+ * do_pg_backup_stop() or do_pg_abort_backup(). There can be many backups
+ * active at the same time.
*
* It is the responsibility of the caller of this function to verify the
* permissions of the calling user!
*/
-XLogRecPtr
-do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
- StringInfo labelfile, List **tablespaces,
- StringInfo tblspcmapfile)
+void
+do_pg_backup_start(BackupState *state, bool fast, List **tablespaces)
{
bool backup_started_in_recovery = false;
- XLogRecPtr checkpointloc;
- XLogRecPtr startpoint;
- TimeLineID starttli;
- pg_time_t stamp_time;
- char strfbuf[128];
- char xlogfilename[MAXFNAMELEN];
XLogSegNo _logSegNo;
+ Assert(state != NULL);
backup_started_in_recovery = RecoveryInProgress();
/*
@@ -8288,7 +8280,7 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
errmsg("WAL level not sufficient for making an online backup"),
errhint("wal_level must be set to \"replica\" or \"logical\" at server start.")));
- if (strlen(backupidstr) > MAXPGPATH)
+ if (strlen(state->name) > MAXPGPATH)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("backup label too long (max %d bytes)",
@@ -8385,9 +8377,9 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* pointer.
*/
LWLockAcquire(ControlFileLock, LW_SHARED);
- checkpointloc = ControlFile->checkPoint;
- startpoint = ControlFile->checkPointCopy.redo;
- starttli = ControlFile->checkPointCopy.ThisTimeLineID;
+ state->checkpointloc = ControlFile->checkPoint;
+ state->startpoint = ControlFile->checkPointCopy.redo;
+ state->starttli = ControlFile->checkPointCopy.ThisTimeLineID;
checkpointfpw = ControlFile->checkPointCopy.fullPageWrites;
LWLockRelease(ControlFileLock);
@@ -8404,7 +8396,7 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
recptr = XLogCtl->lastFpwDisableRecPtr;
SpinLockRelease(&XLogCtl->info_lck);
- if (!checkpointfpw || startpoint <= recptr)
+ if (!checkpointfpw || state->startpoint <= recptr)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL generated with full_page_writes=off was replayed "
@@ -8436,16 +8428,17 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* either because only few buffers have been dirtied yet.
*/
WALInsertLockAcquireExclusive();
- if (XLogCtl->Insert.lastBackupStart < startpoint)
+ if (XLogCtl->Insert.lastBackupStart < state->startpoint)
{
- XLogCtl->Insert.lastBackupStart = startpoint;
+ XLogCtl->Insert.lastBackupStart = state->startpoint;
gotUniqueStartpoint = true;
}
WALInsertLockRelease();
} while (!gotUniqueStartpoint);
- XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
- XLogFileName(xlogfilename, starttli, _logSegNo, wal_segment_size);
+ XLByteToSeg(state->startpoint, _logSegNo, wal_segment_size);
+ XLogFileName(state->startxlogfile, state->starttli, _logSegNo,
+ wal_segment_size);
/*
* Construct tablespace_map file.
@@ -8525,46 +8518,23 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
if (tablespaces)
*tablespaces = lappend(*tablespaces, ti);
- appendStringInfo(tblspcmapfile, "%s %s\n",
+ appendStringInfo(state->tablespace_map, "%s %s\n",
ti->oid, escapedpath.data);
pfree(escapedpath.data);
}
FreeDir(tblspcdir);
- /*
- * Construct backup label file.
- */
-
- /* Use the log timezone here, not the session timezone */
- stamp_time = (pg_time_t) time(NULL);
- pg_strftime(strfbuf, sizeof(strfbuf),
- "%Y-%m-%d %H:%M:%S %Z",
- pg_localtime(&stamp_time, log_timezone));
- appendStringInfo(labelfile, "START WAL LOCATION: %X/%X (file %s)\n",
- LSN_FORMAT_ARGS(startpoint), xlogfilename);
- appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n",
- LSN_FORMAT_ARGS(checkpointloc));
- appendStringInfo(labelfile, "BACKUP METHOD: streamed\n");
- appendStringInfo(labelfile, "BACKUP FROM: %s\n",
- backup_started_in_recovery ? "standby" : "primary");
- appendStringInfo(labelfile, "START TIME: %s\n", strfbuf);
- appendStringInfo(labelfile, "LABEL: %s\n", backupidstr);
- appendStringInfo(labelfile, "START TIMELINE: %u\n", starttli);
+ state->starttime = (pg_time_t) time(NULL);
}
PG_END_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0);
+ state->started_in_recovery = backup_started_in_recovery;
+
/*
* Mark that the start phase has correctly finished for the backup.
*/
sessionBackupState = SESSION_BACKUP_RUNNING;
-
- /*
- * We're done. As a convenience, return the starting WAL location.
- */
- if (starttli_p)
- *starttli_p = starttli;
- return startpoint;
}
/* Error cleanup callback for pg_backup_start */
@@ -8596,48 +8566,39 @@ get_backup_status(void)
/*
* do_pg_backup_stop
*
- * Utility function called at the end of an online backup. It cleans up the
- * backup state and can optionally wait for WAL segments to be archived.
+ * Utility function called at the end of an online backup. It creates backup
+ * label contents and history file (if required), resets sessionBackupState
+ * and so on. It can optionally wait for WAL segments to be archived.
*
* Returns the last WAL location that must be present to restore from this
- * backup, and the corresponding timeline ID in *stoptli_p.
+ * backup, and the corresponding timeline ID via backup state stoppoint and
+ * stoptli respectively.
*
* It is the responsibility of the caller of this function to verify the
* permissions of the calling user!
*/
-XLogRecPtr
-do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
+void
+do_pg_backup_stop(BackupState *state, bool waitforarchive)
{
- bool backup_started_in_recovery = false;
- XLogRecPtr startpoint;
- XLogRecPtr stoppoint;
- TimeLineID stoptli;
- pg_time_t stamp_time;
- char strfbuf[128];
+ bool backup_stopped_in_recovery = false;
char histfilepath[MAXPGPATH];
- char startxlogfilename[MAXFNAMELEN];
- char stopxlogfilename[MAXFNAMELEN];
char lastxlogfilename[MAXFNAMELEN];
char histfilename[MAXFNAMELEN];
- char backupfrom[20];
XLogSegNo _logSegNo;
FILE *fp;
- char ch;
int seconds_before_warning;
int waits = 0;
bool reported_waiting = false;
- char *remaining;
- char *ptr;
- uint32 hi,
- lo;
- backup_started_in_recovery = RecoveryInProgress();
+ Assert(state != NULL);
+
+ backup_stopped_in_recovery = RecoveryInProgress();
/*
* During recovery, we don't need to check WAL level. Because, if WAL
* level is not sufficient, it's impossible to get here during recovery.
*/
- if (!backup_started_in_recovery && !XLogIsNeeded())
+ if (!backup_stopped_in_recovery && !XLogIsNeeded())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL level not sufficient for making an online backup"),
@@ -8678,29 +8639,11 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
WALInsertLockRelease();
/*
- * Read and parse the START WAL LOCATION line (this code is pretty crude,
- * but we are not expecting any variability in the file format).
+ * If we are taking an online backup from the standby, we confirm that the
+ * standby has not been promoted during the backup.
*/
- if (sscanf(labelfile, "START WAL LOCATION: %X/%X (file %24s)%c",
- &hi, &lo, startxlogfilename,
- &ch) != 4 || ch != '\n')
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
- startpoint = ((uint64) hi) << 32 | lo;
- remaining = strchr(labelfile, '\n') + 1; /* %n is not portable enough */
-
- /*
- * Parse the BACKUP FROM line. If we are taking an online backup from the
- * standby, we confirm that the standby has not been promoted during the
- * backup.
- */
- ptr = strstr(remaining, "BACKUP FROM:");
- if (!ptr || sscanf(ptr, "BACKUP FROM: %19s\n", backupfrom) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
- if (strcmp(backupfrom, "standby") == 0 && !backup_started_in_recovery)
+ if (state->started_in_recovery == true &&
+ backup_stopped_in_recovery == false)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("the standby was promoted during online backup"),
@@ -8736,7 +8679,7 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
* an archiver is not invoked. So it doesn't seem worthwhile to write a
* backup history file during recovery.
*/
- if (backup_started_in_recovery)
+ if (backup_stopped_in_recovery)
{
XLogRecPtr recptr;
@@ -8748,7 +8691,7 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
recptr = XLogCtl->lastFpwDisableRecPtr;
SpinLockRelease(&XLogCtl->info_lck);
- if (startpoint <= recptr)
+ if (state->startpoint <= recptr)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL generated with full_page_writes=off was replayed "
@@ -8760,8 +8703,8 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
LWLockAcquire(ControlFileLock, LW_SHARED);
- stoppoint = ControlFile->minRecoveryPoint;
- stoptli = ControlFile->minRecoveryPointTLI;
+ state->stoppoint = ControlFile->minRecoveryPoint;
+ state->stoptli = ControlFile->minRecoveryPointTLI;
LWLockRelease(ControlFileLock);
}
else
@@ -8770,14 +8713,15 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
* Write the backup-end xlog record
*/
XLogBeginInsert();
- XLogRegisterData((char *) (&startpoint), sizeof(startpoint));
- stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);
+ XLogRegisterData((char *) (&state->startpoint),
+ sizeof(state->startpoint));
+ state->stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);
/*
* Given that we're not in recovery, InsertTimeLineID is set and can't
* change, so we can read it without a lock.
*/
- stoptli = XLogCtl->InsertTimeLineID;
+ state->stoptli = XLogCtl->InsertTimeLineID;
/*
* Force a switch to a new xlog segment file, so that the backup is
@@ -8785,39 +8729,30 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
*/
RequestXLogSwitch(false);
- XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size);
- XLogFileName(stopxlogfilename, stoptli, _logSegNo, wal_segment_size);
+ XLByteToPrevSeg(state->stoppoint, _logSegNo, wal_segment_size);
+ XLogFileName(state->stopxlogfile, state->stoptli, _logSegNo,
+ wal_segment_size);
- /* Use the log timezone here, not the session timezone */
- stamp_time = (pg_time_t) time(NULL);
- pg_strftime(strfbuf, sizeof(strfbuf),
- "%Y-%m-%d %H:%M:%S %Z",
- pg_localtime(&stamp_time, log_timezone));
+ state->stoptime = (pg_time_t) time(NULL);
/*
- * Write the backup history file
+ * Write the backup history file. Add backup_label file contents too it.
*/
- XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
- BackupHistoryFilePath(histfilepath, stoptli, _logSegNo,
- startpoint, wal_segment_size);
+ XLByteToSeg(state->startpoint, _logSegNo, wal_segment_size);
+ BackupHistoryFilePath(histfilepath, state->stoptli, _logSegNo,
+ state->startpoint, wal_segment_size);
fp = AllocateFile(histfilepath, "w");
if (!fp)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create file \"%s\": %m",
histfilepath)));
- fprintf(fp, "START WAL LOCATION: %X/%X (file %s)\n",
- LSN_FORMAT_ARGS(startpoint), startxlogfilename);
- fprintf(fp, "STOP WAL LOCATION: %X/%X (file %s)\n",
- LSN_FORMAT_ARGS(stoppoint), stopxlogfilename);
- /*
- * Transfer remaining lines including label and start timeline to
- * history file.
- */
- fprintf(fp, "%s", remaining);
- fprintf(fp, "STOP TIME: %s\n", strfbuf);
- fprintf(fp, "STOP TIMELINE: %u\n", stoptli);
+ /* construct history file contents */
+ build_backup_content(state, true);
+
+ fprintf(fp, "%s", state->history_file->data);
+
if (fflush(fp) || ferror(fp) || FreeFile(fp))
ereport(ERROR,
(errcode_for_file_access(),
@@ -8833,6 +8768,9 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
CleanupBackupHistory();
}
+ /* construct backup_label contents */
+ build_backup_content(state, false);
+
/*
* If archiving is enabled, wait for all the required WAL files to be
* archived before returning. If archiving isn't enabled, the required WAL
@@ -8855,15 +8793,16 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
*/
if (waitforarchive &&
- ((!backup_started_in_recovery && XLogArchivingActive()) ||
- (backup_started_in_recovery && XLogArchivingAlways())))
+ ((!backup_stopped_in_recovery && XLogArchivingActive()) ||
+ (backup_stopped_in_recovery && XLogArchivingAlways())))
{
- XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size);
- XLogFileName(lastxlogfilename, stoptli, _logSegNo, wal_segment_size);
+ XLByteToPrevSeg(state->stoppoint, _logSegNo, wal_segment_size);
+ XLogFileName(lastxlogfilename, state->stoptli, _logSegNo,
+ wal_segment_size);
- XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
- BackupHistoryFileName(histfilename, stoptli, _logSegNo,
- startpoint, wal_segment_size);
+ XLByteToSeg(state->startpoint, _logSegNo, wal_segment_size);
+ BackupHistoryFileName(histfilename, state->stoptli, _logSegNo,
+ state->startpoint, wal_segment_size);
seconds_before_warning = 60;
waits = 0;
@@ -8904,13 +8843,6 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
else if (waitforarchive)
ereport(NOTICE,
(errmsg("WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup")));
-
- /*
- * We're done. As a convenience, return the ending WAL location.
- */
- if (stoptli_p)
- *stoptli_p = stoptli;
- return stoppoint;
}
diff --git a/src/backend/access/transam/xlogbackup.c b/src/backend/access/transam/xlogbackup.c
new file mode 100644
index 0000000000..3b01a4008b
--- /dev/null
+++ b/src/backend/access/transam/xlogbackup.c
@@ -0,0 +1,98 @@
+/*-------------------------------------------------------------------------
+ * xlogbackup.c
+ *
+ * This file contains backup related code.
+ *
+ * Copyright (c) 2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/access/transam/xlogbackup.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <access/xlogbackup.h>
+
+/*
+ * Allocate backup state.
+ */
+BackupState *
+allocate_backup_state(const char *name)
+{
+ BackupState *state;
+
+ state = (BackupState *) palloc0(sizeof(BackupState));
+ state->name = pstrdup(name);
+ state->backup_label = makeStringInfo();
+ state->tablespace_map = makeStringInfo();
+ state->history_file = makeStringInfo();
+
+ return state;
+}
+
+/*
+ * Deallocate backup state.
+ */
+void
+deallocate_backup_state(BackupState *state)
+{
+ Assert(state != NULL);
+
+ pfree(state->name);
+ pfree(state->backup_label->data);
+ pfree(state->tablespace_map->data);
+ pfree(state->history_file->data);
+ pfree(state);
+}
+
+/*
+ * Construct backup_label file or history file contents
+ *
+ * When ishistoryfile is true, it creates contents for backup history file,
+ * otherwise, it creates contents for backup_label file.
+ */
+void
+build_backup_content(BackupState *state, bool ishistoryfile)
+{
+ StringInfo str;
+ char startstrbuf[128];
+ char stopstrfbuf[128];
+
+ if (ishistoryfile)
+ str = state->history_file;
+ else
+ str = state->backup_label;
+
+ /* Use the log timezone here, not the session timezone */
+ pg_strftime(startstrbuf, sizeof(startstrbuf), "%Y-%m-%d %H:%M:%S %Z",
+ pg_localtime(&state->starttime, log_timezone));
+
+ appendStringInfo(str, "START WAL LOCATION: %X/%X (file %s)\n",
+ LSN_FORMAT_ARGS(state->startpoint),
+ state->startxlogfile);
+
+ if (ishistoryfile)
+ appendStringInfo(str, "STOP WAL LOCATION: %X/%X (file %s)\n",
+ LSN_FORMAT_ARGS(state->startpoint),
+ state->stopxlogfile);
+
+ appendStringInfo(str, "CHECKPOINT LOCATION: %X/%X\n",
+ LSN_FORMAT_ARGS(state->checkpointloc));
+ appendStringInfo(str, "BACKUP METHOD: streamed\n");
+ appendStringInfo(str, "BACKUP FROM: %s\n",
+ state->started_in_recovery ? "standby" : "primary");
+ appendStringInfo(str, "START TIME: %s\n", startstrbuf);
+ appendStringInfo(str, "LABEL: %s\n", state->name);
+ appendStringInfo(str, "START TIMELINE: %u\n", state->starttli);
+
+ if (ishistoryfile)
+ {
+ /* Use the log timezone here, not the session timezone */
+ pg_strftime(stopstrfbuf, sizeof(stopstrfbuf), "%Y-%m-%d %H:%M:%S %Z",
+ pg_localtime(&state->stoptime, log_timezone));
+
+ appendStringInfo(str, "STOP TIME: %s\n", stopstrfbuf);
+ appendStringInfo(str, "STOP TIMELINE: %u\n", state->stoptli);
+ }
+}
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 27aeb6e281..a7b69f7f1b 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -20,6 +20,7 @@
#include "access/htup_details.h"
#include "access/xlog_internal.h"
+#include <access/xlogbackup.h>
#include "access/xlogrecovery.h"
#include "access/xlogutils.h"
#include "catalog/pg_type.h"
@@ -38,11 +39,7 @@
#include "utils/timestamp.h"
#include "utils/tuplestore.h"
-/*
- * Store label file and tablespace map during backups.
- */
-static StringInfo label_file;
-static StringInfo tblspc_map_file;
+static BackupState *backup_state = NULL;
/*
* pg_backup_start: set up for taking an on-line backup dump
@@ -62,7 +59,6 @@ pg_backup_start(PG_FUNCTION_ARGS)
text *backupid = PG_GETARG_TEXT_PP(0);
bool fast = PG_GETARG_BOOL(1);
char *backupidstr;
- XLogRecPtr startpoint;
SessionBackupState status = get_backup_status();
MemoryContext oldcontext;
@@ -74,20 +70,18 @@ pg_backup_start(PG_FUNCTION_ARGS)
errmsg("a backup is already in progress in this session")));
/*
- * Label file and tablespace map file need to be long-lived, since they
- * are read in pg_backup_stop.
+ * backup_state need to be long-lived as its contents are read in
+ * pg_backup_stop, hence allocate in TopMemoryContext.
*/
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
- label_file = makeStringInfo();
- tblspc_map_file = makeStringInfo();
+ backup_state = allocate_backup_state(backupidstr);
MemoryContextSwitchTo(oldcontext);
register_persistent_abort_backup_handler();
- startpoint = do_pg_backup_start(backupidstr, fast, NULL, label_file,
- NULL, tblspc_map_file);
+ do_pg_backup_start(backup_state, fast, NULL);
- PG_RETURN_LSN(startpoint);
+ PG_RETURN_LSN(backup_state->startpoint);
}
@@ -108,9 +102,7 @@ pg_backup_stop(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
Datum values[PG_BACKUP_STOP_V2_COLS] = {0};
bool nulls[PG_BACKUP_STOP_V2_COLS] = {0};
-
bool waitforarchive = PG_GETARG_BOOL(0);
- XLogRecPtr stoppoint;
SessionBackupState status = get_backup_status();
/* Initialize attributes information in the tuple descriptor */
@@ -127,19 +119,14 @@ pg_backup_stop(PG_FUNCTION_ARGS)
* Stop the backup. Return a copy of the backup label and tablespace map
* so they can be written to disk by the caller.
*/
- stoppoint = do_pg_backup_stop(label_file->data, waitforarchive, NULL);
-
- values[0] = LSNGetDatum(stoppoint);
- values[1] = CStringGetTextDatum(label_file->data);
- values[2] = CStringGetTextDatum(tblspc_map_file->data);
-
- /* Free structures allocated in TopMemoryContext */
- pfree(label_file->data);
- pfree(label_file);
- label_file = NULL;
- pfree(tblspc_map_file->data);
- pfree(tblspc_map_file);
- tblspc_map_file = NULL;
+ do_pg_backup_stop(backup_state, waitforarchive);
+
+ values[0] = LSNGetDatum(backup_state->stoppoint);
+ values[1] = CStringGetTextDatum(backup_state->backup_label->data);
+ values[2] = CStringGetTextDatum(backup_state->tablespace_map->data);
+
+ deallocate_backup_state(backup_state);
+ backup_state = NULL;
/* Returns the record as Datum */
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 2bb831a3cd..bc27279359 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -17,6 +17,7 @@
#include <time.h>
#include "access/xlog_internal.h"
+#include <access/xlogbackup.h>
#include "backup/backup_manifest.h"
#include "backup/basebackup.h"
#include "backup/basebackup_sink.h"
@@ -231,9 +232,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
bbsink_state state;
XLogRecPtr endptr;
TimeLineID endtli;
- StringInfo labelfile;
- StringInfo tblspc_map_file;
backup_manifest_info manifest;
+ BackupState *backup_state;
/* Initial backup state, insofar as we know it now. */
state.tablespaces = NIL;
@@ -248,18 +248,18 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
backup_started_in_recovery = RecoveryInProgress();
- labelfile = makeStringInfo();
- tblspc_map_file = makeStringInfo();
InitializeBackupManifest(&manifest, opt->manifest,
opt->manifest_checksum_type);
total_checksum_failures = 0;
+ backup_state = allocate_backup_state(opt->label);
basebackup_progress_wait_checkpoint();
- state.startptr = do_pg_backup_start(opt->label, opt->fastcheckpoint,
- &state.starttli,
- labelfile, &state.tablespaces,
- tblspc_map_file);
+ do_pg_backup_start(backup_state, opt->fastcheckpoint,
+ &state.tablespaces);
+
+ state.startptr = backup_state->startpoint;
+ state.starttli = backup_state->starttli;
/*
* Once do_pg_backup_start has been called, ensure that any failure causes
@@ -316,14 +316,19 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
bbsink_begin_archive(sink, "base.tar");
+ /* construct backup_label contents */
+ build_backup_content(backup_state, false);
+
/* In the main tar, include the backup_label first... */
- sendFileWithContent(sink, BACKUP_LABEL_FILE, labelfile->data,
+ sendFileWithContent(sink, BACKUP_LABEL_FILE,
+ backup_state->backup_label->data,
&manifest);
/* Then the tablespace_map file, if required... */
if (opt->sendtblspcmapfile)
{
- sendFileWithContent(sink, TABLESPACE_MAP, tblspc_map_file->data,
+ sendFileWithContent(sink, TABLESPACE_MAP,
+ backup_state->tablespace_map->data,
&manifest);
sendtblspclinks = false;
}
@@ -374,7 +379,12 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
}
basebackup_progress_wait_wal_archive(&state);
- endptr = do_pg_backup_stop(labelfile->data, !opt->nowait, &endtli);
+ do_pg_backup_stop(backup_state, !opt->nowait);
+
+ endptr = backup_state->stoppoint;
+ endtli = backup_state->stoptli;
+
+ deallocate_backup_state(backup_state);
}
PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 9ebd321ba7..6ca974615e 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -11,6 +11,7 @@
#ifndef XLOG_H
#define XLOG_H
+#include <access/xlogbackup.h>
#include "access/xlogdefs.h"
#include "access/xlogreader.h"
#include "datatype/timestamp.h"
@@ -277,11 +278,9 @@ typedef enum SessionBackupState
SESSION_BACKUP_RUNNING,
} SessionBackupState;
-extern XLogRecPtr do_pg_backup_start(const char *backupidstr, bool fast,
- TimeLineID *starttli_p, StringInfo labelfile,
- List **tablespaces, StringInfo tblspcmapfile);
-extern XLogRecPtr do_pg_backup_stop(char *labelfile, bool waitforarchive,
- TimeLineID *stoptli_p);
+extern void do_pg_backup_start(BackupState *state, bool fast,
+ List **tablespaces);
+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);
diff --git a/src/include/access/xlogbackup.h b/src/include/access/xlogbackup.h
new file mode 100644
index 0000000000..81c9dc2eab
--- /dev/null
+++ b/src/include/access/xlogbackup.h
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ * xlogbackup.h
+ *
+ * Declarations for the backup related code.
+ *
+ * Copyright (c) 2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/include/access/xlogbackup.h
+ *-------------------------------------------------------------------------
+ */
+#ifndef XLOG_BACKUP_H
+#define XLOG_BACKUP_H
+
+#include "access/xlogdefs.h"
+#include "lib/stringinfo.h"
+#include "pgtime.h"
+
+/*
+ * This macro is same as xlog_internals.h's MAXFNAMELEN. It may not be sensible
+ * to include xlog_internals.h just for this macro, hence defined a new one
+ * here.
+ *
+ * XXX: We can remove this macro, if at all, xlog_internals.h is included here
+ * in future.
+ */
+#define MAXFNAMELEN_BACKUP 64
+
+/* Structure to hold backup state. */
+typedef struct BackupState
+{
+ /* Following are the fields captured when the backup starts. */
+ char *name; /* backup label name */
+ XLogRecPtr startpoint; /* backup start WAL location */
+ char startxlogfile[MAXFNAMELEN_BACKUP]; /* backup start WAL file */
+ TimeLineID starttli; /* backup start TLI */
+ XLogRecPtr checkpointloc; /* last checkpoint location */
+ pg_time_t starttime; /* backup start time */
+ bool started_in_recovery; /* backup started in recovery? */
+
+ /* Following are the fields captured during or after the backup stops. */
+ XLogRecPtr stoppoint; /* backup stop WAL location */
+ char stopxlogfile[MAXFNAMELEN_BACKUP]; /* backup stop WAL file */
+ TimeLineID stoptli; /* backup stop TLI */
+ pg_time_t stoptime; /* backup stop time */
+
+ StringInfo backup_label; /* backup_label file contents */
+ StringInfo tablespace_map; /* tablespace_map file contents */
+ StringInfo history_file; /* history file contents */
+} BackupState;
+
+extern BackupState *allocate_backup_state(const char *name);
+extern void deallocate_backup_state(BackupState *state);
+extern void build_backup_content(BackupState *state, bool ishistoryfile);
+
+#endif /* XLOG_BACKUP_H */
--
2.34.1