diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml index 03c0dbf1cd..882d255829 100644 --- a/doc/src/sgml/backup.sgml +++ b/doc/src/sgml/backup.sgml @@ -1001,10 +1001,12 @@ SELECT pg_start_backup('label', true); SELECT pg_stop_backup(); - This terminates the backup mode and performs an automatic switch to - the next WAL segment. The reason for the switch is to arrange for - the last WAL segment file written during the backup interval to be - ready to archive. + When called on a primary it terminates the backup mode and performs + an automatic switch to the next WAL segment. The reason for the switch + is to arrange for the last WAL segment written during the backup + interval to be ready to archive. When called on a standby the WAL + segment switch must be performed manually on the primary if it does + not happen with normal write activity. diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 12bbc391e0..681d6465e3 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -18041,7 +18041,16 @@ postgres=# select pg_start_backup('label_goes_here'); pg_start_backup. In a non-exclusive backup, the contents of the backup_label and tablespace_map are returned in the result of the function, and should be written to files in the - backup (and not in the data directory). + backup (and not in the data directory). + If the backup is taken from a standby, pg_stop_backup waits for + WAL to be archived when archive_mode = always. + Enforcing manually a WAL segment switch to happen with for example + pg_switch_wal may be necessary if the primary has low activity + to allow the backup to complete. Using statement_timeout to + limit the amount of time to wait will control the wait time, though all the + WAL segments necessary to recover into a consistent state using the backup + taken may not be archived at the time pg_stop_backup returns + its status to the caller. diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 68109e2541..6285b96771 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -10030,10 +10030,9 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, /* * During recovery, since we don't use the end-of-backup WAL - * record and don't write the backup history file, the - * starting WAL location doesn't need to be unique. This means - * that two base backups started at the same time might use - * the same checkpoint as starting locations. + * record, the starting WAL location doesn't need to be unique. + * This means that two base backups started at the same time + * might use the same checkpoint as starting locations. */ gotUniqueStartpoint = true; } @@ -10550,11 +10549,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) * backup. We have no way of checking if pg_control wasn't backed up last * however. * - * We don't force a switch to new WAL file and wait for all the required - * files to be archived. This is okay if we use the backup to start the - * standby. But, if it's for an archive recovery, to ensure all the - * required files are available, a user should wait for them to be - * archived, or include them into the backup. + * We don't force a switch to new WAL file but it is still possible to + * wait for all the required files to be archived if waitforarchive is + * true. This is okay if we use the backup to start a standby and fetch + * the missing WAL using streaming replication. But in the case of an + * archive recovery, a user should set waitforarchive to true and wait + * for them to be archived to ensure that all the required files are + * available. * * We return the current minimum recovery point as the backup end * location. Note that it can be greater than the exact backup end @@ -10562,10 +10563,7 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) * pg_control. This is harmless for current uses. * * XXX currently a backup history file is for informational and debug - * purposes only. It's not essential for an online backup. Furthermore, - * even if it's created, it will not be archived during recovery because - * an archiver is not invoked. So it doesn't seem worthwhile to write a - * backup history file during recovery. + * purposes only. It's not essential for an online backup. */ if (backup_started_in_recovery) { @@ -10594,28 +10592,26 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) stoppoint = ControlFile->minRecoveryPoint; stoptli = ControlFile->minRecoveryPointTLI; LWLockRelease(ControlFileLock); - - if (stoptli_p) - *stoptli_p = stoptli; - return stoppoint; } + else + { + /* + * Write the backup-end xlog record + */ + XLogBeginInsert(); + XLogRegisterData((char *) (&startpoint), sizeof(startpoint)); + stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END); + stoptli = ThisTimeLineID; - /* - * Write the backup-end xlog record - */ - XLogBeginInsert(); - XLogRegisterData((char *) (&startpoint), sizeof(startpoint)); - stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END); - stoptli = ThisTimeLineID; - - /* - * Force a switch to a new xlog segment file, so that the backup is valid - * as soon as archiver moves out the current segment file. - */ - RequestXLogSwitch(); + /* + * Force a switch to a new xlog segment file, so that the backup is valid + * as soon as archiver moves out the current segment file. + */ + RequestXLogSwitch(); + } XLByteToPrevSeg(stoppoint, _logSegNo); - XLogFileName(stopxlogfilename, ThisTimeLineID, _logSegNo); + XLogFileName(stopxlogfilename, stoptli, _logSegNo); /* Use the log timezone here, not the session timezone */ stamp_time = (pg_time_t) time(NULL); @@ -10627,7 +10623,7 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) * Write the backup history file */ XLByteToSeg(startpoint, _logSegNo); - BackupHistoryFilePath(histfilepath, ThisTimeLineID, _logSegNo, + BackupHistoryFilePath(histfilepath, stoptli, _logSegNo, (uint32) (startpoint % XLogSegSize)); fp = AllocateFile(histfilepath, "w"); if (!fp) @@ -10674,13 +10670,16 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) * wish to wait, you can set statement_timeout. Also, some notices are * issued to clue in anyone who might be doing this interactively. */ - if (waitforarchive && XLogArchivingActive()) + + if (waitforarchive && + ((!backup_started_in_recovery && XLogArchivingActive()) || + (backup_started_in_recovery && XLogArchivingAlways()))) { XLByteToPrevSeg(stoppoint, _logSegNo); - XLogFileName(lastxlogfilename, ThisTimeLineID, _logSegNo); + XLogFileName(lastxlogfilename, stoptli, _logSegNo); XLByteToSeg(startpoint, _logSegNo); - BackupHistoryFileName(histfilename, ThisTimeLineID, _logSegNo, + BackupHistoryFileName(histfilename, stoptli, _logSegNo, (uint32) (startpoint % XLogSegSize)); seconds_before_warning = 60; @@ -10706,9 +10705,10 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) ereport(WARNING, (errmsg("pg_stop_backup still waiting for all required WAL segments to be archived (%d seconds elapsed)", waits), - errhint("Check that your archive_command is executing properly. " - "pg_stop_backup can be canceled safely, " - "but the database backup will not be usable without all the WAL segments."))); + errhint("Either archive_command is failing or not enough WAL has been " + "generated to require a segment switch. Run pg_switch_wal() to " + "request a WAL switch and monitor your logs to check that your " + "archive_command is executing properly."))); } } diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 9e07ad7e8a..0d90119178 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -286,7 +286,8 @@ pg_switch_xlog(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("recovery is in progress"), - errhint("WAL control functions cannot be executed during recovery."))); + errhint("WAL control functions cannot be executed during recovery; " + "they should be executed on the primary instead"))); switchpoint = RequestXLogSwitch();