Refactor creation of backup_label and backup history files

This change simplifies some of the logic related to the generation and
creation of the backup_label and backup history files, which has become
unnecessarily complicated since the removal of the exclusive backup mode
in commit 39969e2.  The code was previously generating the contents of
these files as a string (start phase for the backup_label and stop phase
for the backup history file), one problem being that the contents of the
backup_label string were scanned to grab some of its internal contents
at the stop phase.

This commit changes the logic so as we store the data required to build
these files in an intermediate structure named BackupState.  The
backup_label file and backup history file strings are generated when
they are ready to be sent back to the client.  Both files are now
generated with the same code path.  While on it, this commit renames
some variables for clarity.

Two new files named xlogbackup.{c,h} are introduced in this commit, to
remove from xlog.c some of the logic around base backups.  Note that
more could be moved to this new set of files.

Author: Bharath Rupireddy, Michael Paquier
Reviewed-by: Fujii Masao
Discussion: https://postgr.es/m/CALj2ACXWwTDgJqCjdaPyfR7djwm6SrybGcrZyrvojzcsmt4FFw@mail.gmail.com
This commit is contained in:
Michael Paquier 2022-09-26 11:15:47 +09:00
parent 216f9c1ab3
commit 7d708093b7
9 changed files with 290 additions and 200 deletions

View File

@ -29,6 +29,7 @@ OBJS = \
xact.o \ xact.o \
xlog.o \ xlog.o \
xlogarchive.o \ xlogarchive.o \
xlogbackup.o \
xlogfuncs.o \ xlogfuncs.o \
xloginsert.o \ xloginsert.o \
xlogprefetcher.o \ xlogprefetcher.o \

View File

@ -15,6 +15,7 @@ backend_sources += files(
'xact.c', 'xact.c',
'xlog.c', 'xlog.c',
'xlogarchive.c', 'xlogarchive.c',
'xlogbackup.c',
'xlogfuncs.c', 'xlogfuncs.c',
'xloginsert.c', 'xloginsert.c',
'xlogprefetcher.c', 'xlogprefetcher.c',

View File

@ -8242,24 +8242,23 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
/* /*
* do_pg_backup_start is the workhorse of the user-visible pg_backup_start() * do_pg_backup_start is the workhorse of the user-visible pg_backup_start()
* function. It creates the necessary starting checkpoint and constructs the * function. It creates the necessary starting checkpoint and constructs the
* backup label and tablespace map. * backup state and tablespace map.
* *
* Input parameters are "backupidstr" (the backup label string) and "fast" * Input parameters are "state" (the backup state), "fast" (if true, we do
* (if true, we do the checkpoint in immediate mode to make it faster). * 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 * The tablespace map contents are appended to passed-in parameter
* *tblspcmapfile, and the caller is responsible for including them in the * tablespace_map and the caller is responsible for including it in the backup
* backup archive as 'backup_label' and 'tablespace_map'. * archive as 'tablespace_map'. The tablespace_map file is required mainly for
* tblspcmapfile is required mainly for tar format in windows as native windows * tar format in windows as native windows utilities are not able to create
* utilities are not able to create symlinks while extracting files from tar. * symlinks while extracting files from tar. However for consistency and
* However for consistency and platform-independence, we do it the same way * platform-independence, we do it the same way everywhere.
* everywhere.
* *
* If "tablespaces" isn't NULL, it receives a list of tablespaceinfo structs * It fills in backup_state with the information required for the backup,
* describing the cluster's tablespaces. * such as the minimum WAL location that must be present to restore from
* * this backup (starttli) and the corresponding timeline ID (starttli).
* Returns the minimum WAL location that must be present to restore from this
* backup, and the corresponding timeline ID in *starttli_p.
* *
* Every successfully started backup must be stopped by calling * Every successfully started backup must be stopped by calling
* do_pg_backup_stop() or do_pg_abort_backup(). There can be many * do_pg_backup_stop() or do_pg_abort_backup(). There can be many
@ -8268,20 +8267,13 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
* It is the responsibility of the caller of this function to verify the * It is the responsibility of the caller of this function to verify the
* permissions of the calling user! * permissions of the calling user!
*/ */
XLogRecPtr void
do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p, do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
StringInfo labelfile, List **tablespaces, BackupState *state, StringInfo tblspcmapfile)
StringInfo tblspcmapfile)
{ {
bool backup_started_in_recovery = false; 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(); backup_started_in_recovery = RecoveryInProgress();
/* /*
@ -8300,6 +8292,8 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
errmsg("backup label too long (max %d bytes)", errmsg("backup label too long (max %d bytes)",
MAXPGPATH))); MAXPGPATH)));
memcpy(state->name, backupidstr, strlen(backupidstr));
/* /*
* Mark backup active in shared memory. We must do full-page WAL writes * Mark backup active in shared memory. We must do full-page WAL writes
* during an on-line backup even if not doing so at other times, because * during an on-line backup even if not doing so at other times, because
@ -8391,9 +8385,9 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* pointer. * pointer.
*/ */
LWLockAcquire(ControlFileLock, LW_SHARED); LWLockAcquire(ControlFileLock, LW_SHARED);
checkpointloc = ControlFile->checkPoint; state->checkpointloc = ControlFile->checkPoint;
startpoint = ControlFile->checkPointCopy.redo; state->startpoint = ControlFile->checkPointCopy.redo;
starttli = ControlFile->checkPointCopy.ThisTimeLineID; state->starttli = ControlFile->checkPointCopy.ThisTimeLineID;
checkpointfpw = ControlFile->checkPointCopy.fullPageWrites; checkpointfpw = ControlFile->checkPointCopy.fullPageWrites;
LWLockRelease(ControlFileLock); LWLockRelease(ControlFileLock);
@ -8410,7 +8404,7 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
recptr = XLogCtl->lastFpwDisableRecPtr; recptr = XLogCtl->lastFpwDisableRecPtr;
SpinLockRelease(&XLogCtl->info_lck); SpinLockRelease(&XLogCtl->info_lck);
if (!checkpointfpw || startpoint <= recptr) if (!checkpointfpw || state->startpoint <= recptr)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL generated with full_page_writes=off was replayed " errmsg("WAL generated with full_page_writes=off was replayed "
@ -8442,17 +8436,14 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* either because only few buffers have been dirtied yet. * either because only few buffers have been dirtied yet.
*/ */
WALInsertLockAcquireExclusive(); WALInsertLockAcquireExclusive();
if (XLogCtl->Insert.lastBackupStart < startpoint) if (XLogCtl->Insert.lastBackupStart < state->startpoint)
{ {
XLogCtl->Insert.lastBackupStart = startpoint; XLogCtl->Insert.lastBackupStart = state->startpoint;
gotUniqueStartpoint = true; gotUniqueStartpoint = true;
} }
WALInsertLockRelease(); WALInsertLockRelease();
} while (!gotUniqueStartpoint); } while (!gotUniqueStartpoint);
XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
XLogFileName(xlogfilename, starttli, _logSegNo, wal_segment_size);
/* /*
* Construct tablespace_map file. * Construct tablespace_map file.
*/ */
@ -8538,39 +8529,16 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
} }
FreeDir(tblspcdir); FreeDir(tblspcdir);
/* state->starttime = (pg_time_t) time(NULL);
* 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);
} }
PG_END_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0); 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. * Mark that the start phase has correctly finished for the backup.
*/ */
sessionBackupState = SESSION_BACKUP_RUNNING; 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 */ /* Error cleanup callback for pg_backup_start */
@ -8602,48 +8570,38 @@ get_backup_status(void)
/* /*
* do_pg_backup_stop * do_pg_backup_stop
* *
* Utility function called at the end of an online backup. It cleans up the * Utility function called at the end of an online backup. It creates history
* backup state and can optionally wait for WAL segments to be archived. * 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_state is filled with the information necessary to restore from this
* backup, and the corresponding timeline ID in *stoptli_p. * backup with its stop LSN (stoppoint), its timeline ID (stoptli), etc.
* *
* It is the responsibility of the caller of this function to verify the * It is the responsibility of the caller of this function to verify the
* permissions of the calling user! * permissions of the calling user!
*/ */
XLogRecPtr void
do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) do_pg_backup_stop(BackupState *state, bool waitforarchive)
{ {
bool backup_started_in_recovery = false; bool backup_stopped_in_recovery = false;
XLogRecPtr startpoint;
XLogRecPtr stoppoint;
TimeLineID stoptli;
pg_time_t stamp_time;
char strfbuf[128];
char histfilepath[MAXPGPATH]; char histfilepath[MAXPGPATH];
char startxlogfilename[MAXFNAMELEN];
char stopxlogfilename[MAXFNAMELEN];
char lastxlogfilename[MAXFNAMELEN]; char lastxlogfilename[MAXFNAMELEN];
char histfilename[MAXFNAMELEN]; char histfilename[MAXFNAMELEN];
char backupfrom[20];
XLogSegNo _logSegNo; XLogSegNo _logSegNo;
FILE *fp; FILE *fp;
char ch;
int seconds_before_warning; int seconds_before_warning;
int waits = 0; int waits = 0;
bool reported_waiting = false; 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 * 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. * 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, ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL level not sufficient for making an online backup"), errmsg("WAL level not sufficient for making an online backup"),
@ -8684,29 +8642,10 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
WALInsertLockRelease(); WALInsertLockRelease();
/* /*
* Read and parse the START WAL LOCATION line (this code is pretty crude, * If we are taking an online backup from the standby, we confirm that the
* but we are not expecting any variability in the file format). * standby has not been promoted during the backup.
*/ */
if (sscanf(labelfile, "START WAL LOCATION: %X/%X (file %24s)%c", if (state->started_in_recovery && !backup_stopped_in_recovery)
&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)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("the standby was promoted during online backup"), errmsg("the standby was promoted during online backup"),
@ -8742,7 +8681,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 * an archiver is not invoked. So it doesn't seem worthwhile to write a
* backup history file during recovery. * backup history file during recovery.
*/ */
if (backup_started_in_recovery) if (backup_stopped_in_recovery)
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
@ -8754,7 +8693,7 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
recptr = XLogCtl->lastFpwDisableRecPtr; recptr = XLogCtl->lastFpwDisableRecPtr;
SpinLockRelease(&XLogCtl->info_lck); SpinLockRelease(&XLogCtl->info_lck);
if (startpoint <= recptr) if (state->startpoint <= recptr)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL generated with full_page_writes=off was replayed " errmsg("WAL generated with full_page_writes=off was replayed "
@ -8766,24 +8705,27 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
LWLockAcquire(ControlFileLock, LW_SHARED); LWLockAcquire(ControlFileLock, LW_SHARED);
stoppoint = ControlFile->minRecoveryPoint; state->stoppoint = ControlFile->minRecoveryPoint;
stoptli = ControlFile->minRecoveryPointTLI; state->stoptli = ControlFile->minRecoveryPointTLI;
LWLockRelease(ControlFileLock); LWLockRelease(ControlFileLock);
} }
else else
{ {
StringInfo history_file;
/* /*
* Write the backup-end xlog record * Write the backup-end xlog record
*/ */
XLogBeginInsert(); XLogBeginInsert();
XLogRegisterData((char *) (&startpoint), sizeof(startpoint)); XLogRegisterData((char *) (&state->startpoint),
stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END); 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 * Given that we're not in recovery, InsertTimeLineID is set and can't
* change, so we can read it without a lock. * 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 * Force a switch to a new xlog segment file, so that the backup is
@ -8791,39 +8733,28 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
*/ */
RequestXLogSwitch(false); RequestXLogSwitch(false);
XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size); XLByteToPrevSeg(state->stoppoint, _logSegNo, wal_segment_size);
XLogFileName(stopxlogfilename, stoptli, _logSegNo, wal_segment_size); state->stoptime = (pg_time_t) time(NULL);
/* 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));
/* /*
* Write the backup history file * Write the backup history file
*/ */
XLByteToSeg(startpoint, _logSegNo, wal_segment_size); XLByteToSeg(state->startpoint, _logSegNo, wal_segment_size);
BackupHistoryFilePath(histfilepath, stoptli, _logSegNo, BackupHistoryFilePath(histfilepath, state->stoptli, _logSegNo,
startpoint, wal_segment_size); state->startpoint, wal_segment_size);
fp = AllocateFile(histfilepath, "w"); fp = AllocateFile(histfilepath, "w");
if (!fp) if (!fp)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not create file \"%s\": %m", errmsg("could not create file \"%s\": %m",
histfilepath))); 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);
/* /* Build and save the contents of the backup history file */
* Transfer remaining lines including label and start timeline to history_file = build_backup_content(state, true);
* history file. fprintf(fp, "%s", history_file->data);
*/ pfree(history_file->data);
fprintf(fp, "%s", remaining); pfree(history_file);
fprintf(fp, "STOP TIME: %s\n", strfbuf);
fprintf(fp, "STOP TIMELINE: %u\n", stoptli);
if (fflush(fp) || ferror(fp) || FreeFile(fp)) if (fflush(fp) || ferror(fp) || FreeFile(fp))
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
@ -8861,15 +8792,16 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
*/ */
if (waitforarchive && if (waitforarchive &&
((!backup_started_in_recovery && XLogArchivingActive()) || ((!backup_stopped_in_recovery && XLogArchivingActive()) ||
(backup_started_in_recovery && XLogArchivingAlways()))) (backup_stopped_in_recovery && XLogArchivingAlways())))
{ {
XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size); XLByteToPrevSeg(state->stoppoint, _logSegNo, wal_segment_size);
XLogFileName(lastxlogfilename, stoptli, _logSegNo, wal_segment_size); XLogFileName(lastxlogfilename, state->stoptli, _logSegNo,
wal_segment_size);
XLByteToSeg(startpoint, _logSegNo, wal_segment_size); XLByteToSeg(state->startpoint, _logSegNo, wal_segment_size);
BackupHistoryFileName(histfilename, stoptli, _logSegNo, BackupHistoryFileName(histfilename, state->stoptli, _logSegNo,
startpoint, wal_segment_size); state->startpoint, wal_segment_size);
seconds_before_warning = 60; seconds_before_warning = 60;
waits = 0; waits = 0;
@ -8910,13 +8842,6 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
else if (waitforarchive) else if (waitforarchive)
ereport(NOTICE, 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"))); (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;
} }

View File

@ -0,0 +1,80 @@
/*-------------------------------------------------------------------------
*
* xlogbackup.c
* Internal routines for base backups.
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/access/transam/xlogbackup.c
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xlog.h"
#include "access/xlog_internal.h"
#include "access/xlogbackup.h"
/*
* Build contents for backup_label or backup history file.
*
* When ishistoryfile is true, it creates the contents for a backup history
* file, otherwise it creates contents for a backup_label file.
*
* Returns the result generated as a palloc'd StringInfo.
*/
StringInfo
build_backup_content(BackupState *state, bool ishistoryfile)
{
char startstrbuf[128];
char startxlogfile[MAXFNAMELEN]; /* backup start WAL file */
XLogSegNo startsegno;
StringInfo result = makeStringInfo();
Assert(state != NULL);
/* 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));
XLByteToSeg(state->startpoint, startsegno, wal_segment_size);
XLogFileName(startxlogfile, state->starttli, startsegno, wal_segment_size);
appendStringInfo(result, "START WAL LOCATION: %X/%X (file %s)\n",
LSN_FORMAT_ARGS(state->startpoint), startxlogfile);
if (ishistoryfile)
{
char stopxlogfile[MAXFNAMELEN]; /* backup stop WAL file */
XLogSegNo stopsegno;
XLByteToSeg(state->stoppoint, stopsegno, wal_segment_size);
XLogFileName(stopxlogfile, state->stoptli, stopsegno, wal_segment_size);
appendStringInfo(result, "STOP WAL LOCATION: %X/%X (file %s)\n",
LSN_FORMAT_ARGS(state->stoppoint), stopxlogfile);
}
appendStringInfo(result, "CHECKPOINT LOCATION: %X/%X\n",
LSN_FORMAT_ARGS(state->checkpointloc));
appendStringInfo(result, "BACKUP METHOD: streamed\n");
appendStringInfo(result, "BACKUP FROM: %s\n",
state->started_in_recovery ? "standby" : "primary");
appendStringInfo(result, "START TIME: %s\n", startstrbuf);
appendStringInfo(result, "LABEL: %s\n", state->name);
appendStringInfo(result, "START TIMELINE: %u\n", state->starttli);
if (ishistoryfile)
{
char stopstrfbuf[128];
/* 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(result, "STOP TIME: %s\n", stopstrfbuf);
appendStringInfo(result, "STOP TIMELINE: %u\n", state->stoptli);
}
return result;
}

View File

@ -20,6 +20,7 @@
#include "access/htup_details.h" #include "access/htup_details.h"
#include "access/xlog_internal.h" #include "access/xlog_internal.h"
#include "access/xlogbackup.h"
#include "access/xlogrecovery.h" #include "access/xlogrecovery.h"
#include "access/xlogutils.h" #include "access/xlogutils.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
@ -39,19 +40,16 @@
#include "utils/tuplestore.h" #include "utils/tuplestore.h"
/* /*
* Store label file and tablespace map during backups. * Backup-related variables.
*/ */
static StringInfo label_file; static BackupState *backup_state = NULL;
static StringInfo tblspc_map_file; static StringInfo tablespace_map = NULL;
/* /*
* pg_backup_start: set up for taking an on-line backup dump * pg_backup_start: set up for taking an on-line backup dump
* *
* Essentially what this does is to create a backup label file in $PGDATA, * Essentially what this does is to create the contents required for the
* where it will be archived as part of the backup dump. The label file * backup_label file and the tablespace map.
* contains the user-supplied label string (typically this would be used
* to tell where the backup dump will be stored) and the starting time and
* starting WAL location for the dump.
* *
* Permission checking for this function is managed through the normal * Permission checking for this function is managed through the normal
* GRANT system. * GRANT system.
@ -62,7 +60,6 @@ pg_backup_start(PG_FUNCTION_ARGS)
text *backupid = PG_GETARG_TEXT_PP(0); text *backupid = PG_GETARG_TEXT_PP(0);
bool fast = PG_GETARG_BOOL(1); bool fast = PG_GETARG_BOOL(1);
char *backupidstr; char *backupidstr;
XLogRecPtr startpoint;
SessionBackupState status = get_backup_status(); SessionBackupState status = get_backup_status();
MemoryContext oldcontext; MemoryContext oldcontext;
@ -74,20 +71,35 @@ pg_backup_start(PG_FUNCTION_ARGS)
errmsg("a backup is already in progress in this session"))); errmsg("a backup is already in progress in this session")));
/* /*
* Label file and tablespace map file need to be long-lived, since they * backup_state and tablespace_map need to be long-lived as they are used
* are read in pg_backup_stop. * in pg_backup_stop().
*/ */
oldcontext = MemoryContextSwitchTo(TopMemoryContext); oldcontext = MemoryContextSwitchTo(TopMemoryContext);
label_file = makeStringInfo();
tblspc_map_file = makeStringInfo(); /* Allocate backup state or reset it, if it comes from a previous run */
if (backup_state == NULL)
backup_state = (BackupState *) palloc0(sizeof(BackupState));
else
MemSet(backup_state, 0, sizeof(BackupState));
/*
* tablespace_map may have been created in a previous backup, so take this
* occasion to clean it.
*/
if (tablespace_map != NULL)
{
pfree(tablespace_map->data);
pfree(tablespace_map);
tablespace_map = NULL;
}
tablespace_map = makeStringInfo();
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
register_persistent_abort_backup_handler(); register_persistent_abort_backup_handler();
do_pg_backup_start(backupidstr, fast, NULL, backup_state, tablespace_map);
startpoint = do_pg_backup_start(backupidstr, fast, NULL, label_file, PG_RETURN_LSN(backup_state->startpoint);
NULL, tblspc_map_file);
PG_RETURN_LSN(startpoint);
} }
@ -98,6 +110,15 @@ pg_backup_start(PG_FUNCTION_ARGS)
* allows the user to choose if they want to wait for the WAL to be archived * allows the user to choose if they want to wait for the WAL to be archived
* or if we should just return as soon as the WAL record is written. * or if we should just return as soon as the WAL record is written.
* *
* This function stops an in-progress backup, creates backup_label contents and
* it returns the backup stop LSN, backup_label and tablespace_map contents.
*
* The backup_label contains the user-supplied label string (typically this
* would be used to tell where the backup dump will be stored), the starting
* time, starting WAL location for the dump and so on. It is the caller's
* responsibility to write the backup_label and tablespace_map files in the
* data folder that will be restored from this backup.
*
* Permission checking for this function is managed through the normal * Permission checking for this function is managed through the normal
* GRANT system. * GRANT system.
*/ */
@ -108,9 +129,8 @@ pg_backup_stop(PG_FUNCTION_ARGS)
TupleDesc tupdesc; TupleDesc tupdesc;
Datum values[PG_BACKUP_STOP_V2_COLS] = {0}; Datum values[PG_BACKUP_STOP_V2_COLS] = {0};
bool nulls[PG_BACKUP_STOP_V2_COLS] = {0}; bool nulls[PG_BACKUP_STOP_V2_COLS] = {0};
bool waitforarchive = PG_GETARG_BOOL(0); bool waitforarchive = PG_GETARG_BOOL(0);
XLogRecPtr stoppoint; StringInfo backup_label;
SessionBackupState status = get_backup_status(); SessionBackupState status = get_backup_status();
/* Initialize attributes information in the tuple descriptor */ /* Initialize attributes information in the tuple descriptor */
@ -123,23 +143,27 @@ pg_backup_stop(PG_FUNCTION_ARGS)
errmsg("backup is not in progress"), errmsg("backup is not in progress"),
errhint("Did you call pg_backup_start()?"))); errhint("Did you call pg_backup_start()?")));
/* Assert(backup_state != NULL);
* Stop the backup. Return a copy of the backup label and tablespace map Assert(tablespace_map != NULL);
* so they can be written to disk by the caller.
*/
stoppoint = do_pg_backup_stop(label_file->data, waitforarchive, NULL);
values[0] = LSNGetDatum(stoppoint); /* Stop the backup */
values[1] = CStringGetTextDatum(label_file->data); do_pg_backup_stop(backup_state, waitforarchive);
values[2] = CStringGetTextDatum(tblspc_map_file->data);
/* Free structures allocated in TopMemoryContext */ /* Build the contents of backup_label */
pfree(label_file->data); backup_label = build_backup_content(backup_state, false);
pfree(label_file);
label_file = NULL; values[0] = LSNGetDatum(backup_state->stoppoint);
pfree(tblspc_map_file->data); values[1] = CStringGetTextDatum(backup_label->data);
pfree(tblspc_map_file); values[2] = CStringGetTextDatum(tablespace_map->data);
tblspc_map_file = NULL;
/* Deallocate backup-related variables */
pfree(backup_state);
backup_state = NULL;
pfree(tablespace_map->data);
pfree(tablespace_map);
tablespace_map = NULL;
pfree(backup_label->data);
pfree(backup_label);
/* Returns the record as Datum */ /* Returns the record as Datum */
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));

View File

@ -17,6 +17,7 @@
#include <time.h> #include <time.h>
#include "access/xlog_internal.h" #include "access/xlog_internal.h"
#include "access/xlogbackup.h"
#include "backup/backup_manifest.h" #include "backup/backup_manifest.h"
#include "backup/basebackup.h" #include "backup/basebackup.h"
#include "backup/basebackup_sink.h" #include "backup/basebackup_sink.h"
@ -231,9 +232,9 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
bbsink_state state; bbsink_state state;
XLogRecPtr endptr; XLogRecPtr endptr;
TimeLineID endtli; TimeLineID endtli;
StringInfo labelfile;
StringInfo tblspc_map_file;
backup_manifest_info manifest; backup_manifest_info manifest;
BackupState *backup_state;
StringInfo tablespace_map;
/* Initial backup state, insofar as we know it now. */ /* Initial backup state, insofar as we know it now. */
state.tablespaces = NIL; state.tablespaces = NIL;
@ -248,18 +249,21 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
backup_started_in_recovery = RecoveryInProgress(); backup_started_in_recovery = RecoveryInProgress();
labelfile = makeStringInfo();
tblspc_map_file = makeStringInfo();
InitializeBackupManifest(&manifest, opt->manifest, InitializeBackupManifest(&manifest, opt->manifest,
opt->manifest_checksum_type); opt->manifest_checksum_type);
total_checksum_failures = 0; total_checksum_failures = 0;
/* Allocate backup related varilables. */
backup_state = (BackupState *) palloc0(sizeof(BackupState));
tablespace_map = makeStringInfo();
basebackup_progress_wait_checkpoint(); basebackup_progress_wait_checkpoint();
state.startptr = do_pg_backup_start(opt->label, opt->fastcheckpoint, do_pg_backup_start(opt->label, opt->fastcheckpoint, &state.tablespaces,
&state.starttli, backup_state, tablespace_map);
labelfile, &state.tablespaces,
tblspc_map_file); state.startptr = backup_state->startpoint;
state.starttli = backup_state->starttli;
/* /*
* Once do_pg_backup_start has been called, ensure that any failure causes * Once do_pg_backup_start has been called, ensure that any failure causes
@ -313,18 +317,22 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
{ {
struct stat statbuf; struct stat statbuf;
bool sendtblspclinks = true; bool sendtblspclinks = true;
StringInfo backup_label;
bbsink_begin_archive(sink, "base.tar"); bbsink_begin_archive(sink, "base.tar");
/* In the main tar, include the backup_label first... */ /* In the main tar, include the backup_label first... */
sendFileWithContent(sink, BACKUP_LABEL_FILE, labelfile->data, backup_label = build_backup_content(backup_state, false);
&manifest); sendFileWithContent(sink, BACKUP_LABEL_FILE,
backup_label->data, &manifest);
pfree(backup_label->data);
pfree(backup_label);
/* Then the tablespace_map file, if required... */ /* Then the tablespace_map file, if required... */
if (opt->sendtblspcmapfile) if (opt->sendtblspcmapfile)
{ {
sendFileWithContent(sink, TABLESPACE_MAP, tblspc_map_file->data, sendFileWithContent(sink, TABLESPACE_MAP,
&manifest); tablespace_map->data, &manifest);
sendtblspclinks = false; sendtblspclinks = false;
} }
@ -374,7 +382,15 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
} }
basebackup_progress_wait_wal_archive(&state); 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-related variables. */
pfree(tablespace_map->data);
pfree(tablespace_map);
pfree(backup_state);
} }
PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false)); PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));

View File

@ -11,6 +11,7 @@
#ifndef XLOG_H #ifndef XLOG_H
#define XLOG_H #define XLOG_H
#include "access/xlogbackup.h"
#include "access/xlogdefs.h" #include "access/xlogdefs.h"
#include "access/xlogreader.h" #include "access/xlogreader.h"
#include "datatype/timestamp.h" #include "datatype/timestamp.h"
@ -277,11 +278,10 @@ typedef enum SessionBackupState
SESSION_BACKUP_RUNNING, SESSION_BACKUP_RUNNING,
} SessionBackupState; } SessionBackupState;
extern XLogRecPtr do_pg_backup_start(const char *backupidstr, bool fast, extern void do_pg_backup_start(const char *backupidstr, bool fast,
TimeLineID *starttli_p, StringInfo labelfile, List **tablespaces, BackupState *state,
List **tablespaces, StringInfo tblspcmapfile); StringInfo tblspcmapfile);
extern XLogRecPtr do_pg_backup_stop(char *labelfile, bool waitforarchive, extern void do_pg_backup_stop(BackupState *state, bool waitforarchive);
TimeLineID *stoptli_p);
extern void do_pg_abort_backup(int code, Datum arg); extern void do_pg_abort_backup(int code, Datum arg);
extern void register_persistent_abort_backup_handler(void); extern void register_persistent_abort_backup_handler(void);
extern SessionBackupState get_backup_status(void); extern SessionBackupState get_backup_status(void);

View File

@ -0,0 +1,42 @@
/*-------------------------------------------------------------------------
*
* xlogbackup.h
* Definitions for internals of base backups.
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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"
/* Structure to hold backup state. */
typedef struct BackupState
{
/* Fields saved at backup start */
/* Backup label name one extra byte for null-termination */
char name[MAXPGPATH + 1];
XLogRecPtr startpoint; /* backup start WAL location */
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? */
/* Fields saved at the end of backup */
XLogRecPtr stoppoint; /* backup stop WAL location */
TimeLineID stoptli; /* backup stop TLI */
pg_time_t stoptime; /* backup stop time */
} BackupState;
extern StringInfo build_backup_content(BackupState *state,
bool ishistoryfile);
#endif /* XLOG_BACKUP_H */

View File

@ -233,6 +233,7 @@ BackgroundWorker
BackgroundWorkerArray BackgroundWorkerArray
BackgroundWorkerHandle BackgroundWorkerHandle
BackgroundWorkerSlot BackgroundWorkerSlot
BackupState
Barrier Barrier
BaseBackupCmd BaseBackupCmd
BaseBackupTargetHandle BaseBackupTargetHandle