diff options
Diffstat (limited to 'src/backend/access/transam')
-rw-r--r-- | src/backend/access/transam/xlog.c | 519 | ||||
-rw-r--r-- | src/backend/access/transam/xlogfuncs.c | 253 | ||||
-rw-r--r-- | src/backend/access/transam/xlogrecovery.c | 20 |
3 files changed, 130 insertions, 662 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 17a56152f17..8ae0a0ba537 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -386,29 +386,6 @@ typedef union WALInsertLockPadded } WALInsertLockPadded; /* - * State of an exclusive backup, necessary to control concurrent activities - * across sessions when working on exclusive backups. - * - * EXCLUSIVE_BACKUP_NONE means that there is no exclusive backup actually - * running, to be more precise pg_start_backup() is not being executed for - * an exclusive backup and there is no exclusive backup in progress. - * EXCLUSIVE_BACKUP_STARTING means that pg_start_backup() is starting an - * exclusive backup. - * EXCLUSIVE_BACKUP_IN_PROGRESS means that pg_start_backup() has finished - * running and an exclusive backup is in progress. pg_stop_backup() is - * needed to finish it. - * EXCLUSIVE_BACKUP_STOPPING means that pg_stop_backup() is stopping an - * exclusive backup. - */ -typedef enum ExclusiveBackupState -{ - EXCLUSIVE_BACKUP_NONE = 0, - EXCLUSIVE_BACKUP_STARTING, - EXCLUSIVE_BACKUP_IN_PROGRESS, - EXCLUSIVE_BACKUP_STOPPING -} ExclusiveBackupState; - -/* * Session status of running backup, used for sanity checks in SQL-callable * functions to start and stop backups. */ @@ -456,15 +433,12 @@ typedef struct XLogCtlInsert bool fullPageWrites; /* - * exclusiveBackupState indicates the state of an exclusive backup (see - * comments of ExclusiveBackupState for more details). nonExclusiveBackups - * is a counter indicating the number of streaming base backups currently - * in progress. forcePageWrites is set to true when either of these is - * non-zero. lastBackupStart is the latest checkpoint redo location used - * as a starting point for an online backup. + * runningBackups is a counter indicating the number of backups currently in + * progress. forcePageWrites is set to true when runningBackups is non-zero. + * lastBackupStart is the latest checkpoint redo location used as a starting + * point for an online backup. */ - ExclusiveBackupState exclusiveBackupState; - int nonExclusiveBackups; + int runningBackups; XLogRecPtr lastBackupStart; /* @@ -696,8 +670,7 @@ static void ReadControlFile(void); static void UpdateControlFile(void); static char *str_time(pg_time_t tnow); -static void pg_start_backup_callback(int code, Datum arg); -static void pg_stop_backup_callback(int code, Datum arg); +static void pg_backup_start_callback(int code, Datum arg); static int get_sync_bit(int method); @@ -5314,8 +5287,19 @@ StartupXLOG(void) missingContrecPtr = endOfRecoveryInfo->missingContrecPtr; /* - * 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 + * When recovering from a backup (we are in recovery, and archive recovery + * was requested), complain if we did not roll forward far enough to reach + * the point where the database is consistent. For regular online + * backup-from-primary, that means reaching the end-of-backup WAL record (at + * which point we reset backupStartPoint to be Invalid), for + * backup-from-replica (which can't inject records into the WAL stream), + * that point is when we reach the minRecoveryPoint in pg_control (which + * we purposfully copy last when backing up from a replica). For pg_rewind + * (which creates a backup_label with a method of "pg_rewind") or + * snapshot-style backups (which don't), backupEndRequired will be set to + * false. + * + * Note: it is indeed okay to look at the local variable * LocalMinRecoveryPoint here, even though ControlFile->minRecoveryPoint * might be further ahead --- ControlFile->minRecoveryPoint cannot have * been advanced beyond the WAL we processed. @@ -5326,23 +5310,16 @@ StartupXLOG(void) { /* * Ran off end of WAL before reaching end-of-backup WAL record, or - * minRecoveryPoint. That's usually a bad sign, indicating that you - * tried to recover from an online backup but never called - * pg_stop_backup(), or you didn't archive all the WAL up to that - * point. However, this also happens in crash recovery, if the system - * crashes while an online backup is in progress. We must not treat - * that as an error, or the database will refuse to start up. + * minRecoveryPoint. That's a bad sign, indicating that you tried to + * recover from an online backup but never called pg_backup_stop(), + * or you didn't archive all the WAL needed. */ if (ArchiveRecoveryRequested || ControlFile->backupEndRequired) { - if (ControlFile->backupEndRequired) + if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) || ControlFile->backupEndRequired) ereport(FATAL, (errmsg("WAL ends before end of online backup"), errhint("All WAL generated while online backup was taken must be available at recovery."))); - else if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint)) - ereport(FATAL, - (errmsg("WAL ends before end of online backup"), - errhint("Online backup started with pg_start_backup() must be ended with pg_stop_backup(), and all WAL up to that point must be available at recovery."))); else ereport(FATAL, (errmsg("WAL ends before consistent recovery point"))); @@ -7036,7 +7013,7 @@ CreateRestartPoint(int flags) * Ensure minRecoveryPoint is past the checkpoint record. Normally, * this will have happened already while writing out dirty buffers, * but not necessarily - e.g. because no buffers were dirtied. We do - * this because a non-exclusive base backup uses minRecoveryPoint to + * this because a backup performed in recovery uses minRecoveryPoint to * determine which WAL files must be included in the backup, and the * file (or files) containing the checkpoint record must be included, * at a minimum. Note that for an ordinary restart of recovery there's @@ -7840,7 +7817,7 @@ xlog_redo(XLogReaderState *record) /* * Update the LSN of the last replayed XLOG_FPW_CHANGE record so that - * do_pg_start_backup() and do_pg_stop_backup() can check whether + * do_pg_backup_start() and do_pg_backup_stop() can check whether * full_page_writes has been disabled during online backup. */ if (!fpw) @@ -8039,29 +8016,14 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli) } /* - * do_pg_start_backup - * - * Utility function called at the start of an online backup. It creates the - * necessary starting checkpoint and constructs the backup label file. - * - * There are two kind of backups: exclusive and non-exclusive. An exclusive - * backup is started with pg_start_backup(), and there can be only one active - * at a time. The backup and tablespace map files of an exclusive backup are - * written to $PGDATA/backup_label and $PGDATA/tablespace_map, and they are - * removed by pg_stop_backup(). - * - * A non-exclusive backup is used for the streaming base backups (see - * src/backend/replication/basebackup.c). The difference to exclusive backups - * is that the backup label and tablespace map files are not written to disk. - * Instead, their would-be contents are returned in *labelfile and *tblspcmapfile, - * and the caller is responsible for including them in the backup archive as - * 'backup_label' and 'tablespace_map'. There can be many non-exclusive backups - * active at the same time, and they don't conflict with an exclusive backup - * either. - * - * labelfile and tblspcmapfile must be passed as NULL when starting an - * exclusive backup, and as initially-empty StringInfos for a non-exclusive - * backup. + * 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. + * + * The backup label and tablespace map contents are returned in *labelfile and + * *tblspcmapfile, and the caller is responsible for including them in the + * backup archive as 'backup_label' and 'tablespace_map'. There can be many + * backups active at the same time. * * If "tablespaces" isn't NULL, it receives a list of tablespaceinfo structs * describing the cluster's tablespaces. @@ -8073,18 +8035,17 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli) * 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 non-exclusive backup must be stopped by calling - * do_pg_stop_backup() or do_pg_abort_backup(). + * Every successfully started backup must be stopped by calling + * do_pg_backup_stop() or do_pg_abort_backup(). * * It is the responsibility of the caller of this function to verify the * permissions of the calling user! */ XLogRecPtr -do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, +do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p, StringInfo labelfile, List **tablespaces, StringInfo tblspcmapfile) { - bool exclusive = (labelfile == NULL); bool backup_started_in_recovery = false; XLogRecPtr checkpointloc; XLogRecPtr startpoint; @@ -8093,21 +8054,10 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, char strfbuf[128]; char xlogfilename[MAXFNAMELEN]; XLogSegNo _logSegNo; - struct stat stat_buf; - FILE *fp; backup_started_in_recovery = RecoveryInProgress(); /* - * Currently only non-exclusive backup can be taken during recovery. - */ - if (backup_started_in_recovery && exclusive) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("WAL control functions cannot be executed during recovery."))); - - /* * 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. */ @@ -8145,30 +8095,12 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, * XLogInsertRecord(). */ WALInsertLockAcquireExclusive(); - if (exclusive) - { - /* - * At first, mark that we're now starting an exclusive backup, to - * ensure that there are no other sessions currently running - * pg_start_backup() or pg_stop_backup(). - */ - if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_NONE) - { - WALInsertLockRelease(); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("a backup is already in progress"), - errhint("Run pg_stop_backup() and try again."))); - } - XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING; - } - else - XLogCtl->Insert.nonExclusiveBackups++; + XLogCtl->Insert.runningBackups++; XLogCtl->Insert.forcePageWrites = true; WALInsertLockRelease(); /* Ensure we release forcePageWrites if fail below */ - PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive)); + PG_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0); { bool gotUniqueStartpoint = false; DIR *tblspcdir; @@ -8180,7 +8112,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, * Force an XLOG file switch before the checkpoint, to ensure that the * WAL segment the checkpoint is written to doesn't contain pages with * old timeline IDs. That would otherwise happen if you called - * pg_start_backup() right after restoring from a PITR archive: the + * pg_backup_start() right after restoring from a PITR archive: the * first WAL segment containing the startup checkpoint has pages in * the beginning with the old timeline ID. That can cause trouble at * recovery: we won't have a history file covering the old timeline if @@ -8215,7 +8147,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, * means that two successive backup runs can have same checkpoint * positions. * - * Since the fact that we are executing do_pg_start_backup() + * Since the fact that we are executing do_pg_backup_start() * during recovery means that checkpointer is running, we can use * RequestCheckpoint() to establish a restartpoint. * @@ -8416,122 +8348,19 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, LSN_FORMAT_ARGS(startpoint), xlogfilename); appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n", LSN_FORMAT_ARGS(checkpointloc)); - appendStringInfo(labelfile, "BACKUP METHOD: %s\n", - exclusive ? "pg_start_backup" : "streamed"); + 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); - - /* - * Okay, write the file, or return its contents to caller. - */ - if (exclusive) - { - /* - * Check for existing backup label --- implies a backup is already - * running. (XXX given that we checked exclusiveBackupState - * above, maybe it would be OK to just unlink any such label - * file?) - */ - if (stat(BACKUP_LABEL_FILE, &stat_buf) != 0) - { - if (errno != ENOENT) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not stat file \"%s\": %m", - BACKUP_LABEL_FILE))); - } - else - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("a backup is already in progress"), - errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.", - BACKUP_LABEL_FILE))); - - fp = AllocateFile(BACKUP_LABEL_FILE, "w"); - - if (!fp) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not create file \"%s\": %m", - BACKUP_LABEL_FILE))); - if (fwrite(labelfile->data, labelfile->len, 1, fp) != 1 || - fflush(fp) != 0 || - pg_fsync(fileno(fp)) != 0 || - ferror(fp) || - FreeFile(fp)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write file \"%s\": %m", - BACKUP_LABEL_FILE))); - /* Allocated locally for exclusive backups, so free separately */ - pfree(labelfile->data); - pfree(labelfile); - - /* Write backup tablespace_map file. */ - if (tblspcmapfile->len > 0) - { - if (stat(TABLESPACE_MAP, &stat_buf) != 0) - { - if (errno != ENOENT) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not stat file \"%s\": %m", - TABLESPACE_MAP))); - } - else - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("a backup is already in progress"), - errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.", - TABLESPACE_MAP))); - - fp = AllocateFile(TABLESPACE_MAP, "w"); - - if (!fp) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not create file \"%s\": %m", - TABLESPACE_MAP))); - if (fwrite(tblspcmapfile->data, tblspcmapfile->len, 1, fp) != 1 || - fflush(fp) != 0 || - pg_fsync(fileno(fp)) != 0 || - ferror(fp) || - FreeFile(fp)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write file \"%s\": %m", - TABLESPACE_MAP))); - } - - /* Allocated locally for exclusive backups, so free separately */ - pfree(tblspcmapfile->data); - pfree(tblspcmapfile); - } } - PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive)); + PG_END_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0); /* - * Mark that start phase has correctly finished for an exclusive backup. - * Session-level locks are updated as well to reflect that state. - * - * Note that CHECK_FOR_INTERRUPTS() must not occur while updating backup - * counters and session-level lock. Otherwise they can be updated - * inconsistently, and which might cause do_pg_abort_backup() to fail. + * Mark that the start phase has correctly finished for the backup. */ - if (exclusive) - { - WALInsertLockAcquireExclusive(); - XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS; - - /* Set session-level lock */ - sessionBackupState = SESSION_BACKUP_EXCLUSIVE; - WALInsertLockRelease(); - } - else - sessionBackupState = SESSION_BACKUP_NON_EXCLUSIVE; + sessionBackupState = SESSION_BACKUP_RUNNING; /* * We're done. As a convenience, return the starting WAL location. @@ -8541,47 +8370,19 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, return startpoint; } -/* Error cleanup callback for pg_start_backup */ +/* Error cleanup callback for pg_backup_start */ static void -pg_start_backup_callback(int code, Datum arg) +pg_backup_start_callback(int code, Datum arg) { - bool exclusive = DatumGetBool(arg); - /* Update backup counters and forcePageWrites on failure */ WALInsertLockAcquireExclusive(); - if (exclusive) - { - Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STARTING); - XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE; - } - else - { - Assert(XLogCtl->Insert.nonExclusiveBackups > 0); - XLogCtl->Insert.nonExclusiveBackups--; - } - if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && - XLogCtl->Insert.nonExclusiveBackups == 0) - { - XLogCtl->Insert.forcePageWrites = false; - } - WALInsertLockRelease(); -} - -/* - * Error cleanup callback for pg_stop_backup - */ -static void -pg_stop_backup_callback(int code, Datum arg) -{ - bool exclusive = DatumGetBool(arg); + Assert(XLogCtl->Insert.runningBackups > 0); + XLogCtl->Insert.runningBackups--; - /* Update backup status on failure */ - WALInsertLockAcquireExclusive(); - if (exclusive) + if (XLogCtl->Insert.runningBackups == 0) { - Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING); - XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS; + XLogCtl->Insert.forcePageWrites = false; } WALInsertLockRelease(); } @@ -8596,14 +8397,11 @@ get_backup_status(void) } /* - * do_pg_stop_backup + * 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. * - * If labelfile is NULL, this stops an exclusive backup. Otherwise this stops - * the non-exclusive backup specified by 'labelfile'. - * * Returns the last WAL location that must be present to restore from this * backup, and the corresponding timeline ID in *stoptli_p. * @@ -8611,9 +8409,8 @@ get_backup_status(void) * permissions of the calling user! */ XLogRecPtr -do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) +do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) { - bool exclusive = (labelfile == NULL); bool backup_started_in_recovery = false; XLogRecPtr startpoint; XLogRecPtr stoppoint; @@ -8627,7 +8424,6 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) char histfilename[MAXFNAMELEN]; char backupfrom[20]; XLogSegNo _logSegNo; - FILE *lfp; FILE *fp; char ch; int seconds_before_warning; @@ -8641,15 +8437,6 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) backup_started_in_recovery = RecoveryInProgress(); /* - * Currently only non-exclusive backup can be taken during recovery. - */ - if (backup_started_in_recovery && exclusive) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("WAL control functions cannot be executed during recovery."))); - - /* * 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. */ @@ -8659,106 +8446,23 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_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 (exclusive) - { - /* - * At first, mark that we're now stopping an exclusive backup, to - * ensure that there are no other sessions currently running - * pg_start_backup() or pg_stop_backup(). - */ - WALInsertLockAcquireExclusive(); - if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS) - { - WALInsertLockRelease(); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("exclusive backup not in progress"))); - } - XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING; - WALInsertLockRelease(); - - /* - * Remove backup_label. In case of failure, the state for an exclusive - * backup is switched back to in-progress. - */ - PG_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive)); - { - /* - * Read the existing label file into memory. - */ - struct stat statbuf; - int r; - - if (stat(BACKUP_LABEL_FILE, &statbuf)) - { - /* should not happen per the upper checks */ - if (errno != ENOENT) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not stat file \"%s\": %m", - BACKUP_LABEL_FILE))); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("a backup is not in progress"))); - } - - lfp = AllocateFile(BACKUP_LABEL_FILE, "r"); - if (!lfp) - { - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not read file \"%s\": %m", - BACKUP_LABEL_FILE))); - } - labelfile = palloc(statbuf.st_size + 1); - r = fread(labelfile, statbuf.st_size, 1, lfp); - labelfile[statbuf.st_size] = '\0'; - - /* - * Close and remove the backup label file - */ - if (r != 1 || ferror(lfp) || FreeFile(lfp)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not read file \"%s\": %m", - BACKUP_LABEL_FILE))); - durable_unlink(BACKUP_LABEL_FILE, ERROR); - - /* - * Remove tablespace_map file if present, it is created only if - * there are tablespaces. - */ - durable_unlink(TABLESPACE_MAP, DEBUG1); - } - PG_END_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive)); - } - /* - * OK to update backup counters, forcePageWrites and session-level lock. + * OK to update backup counters, forcePageWrites, and session-level lock. * * 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(); - if (exclusive) - { - XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE; - } - else - { - /* - * The user-visible pg_start/stop_backup() functions that operate on - * exclusive backups can be called at any time, but for non-exclusive - * backups, it is expected that each do_pg_start_backup() call is - * matched by exactly one do_pg_stop_backup() call. - */ - Assert(XLogCtl->Insert.nonExclusiveBackups > 0); - XLogCtl->Insert.nonExclusiveBackups--; - } - if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && - XLogCtl->Insert.nonExclusiveBackups == 0) + /* + * 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; } @@ -9016,17 +8720,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) /* * do_pg_abort_backup: abort a running backup * - * This does just the most basic steps of do_pg_stop_backup(), by taking the + * This does just the most basic steps of do_pg_backup_stop(), by taking the * system out of backup mode, thus making it a lot more safe to call from * an error handler. * * The caller can pass 'arg' as 'true' or 'false' to control whether a warning * is emitted. * - * NB: This is only for aborting a non-exclusive backup that doesn't write - * backup_label. A backup started with pg_start_backup() needs to be finished - * with pg_stop_backup(). - * * NB: This gets used as a before_shmem_exit handler, hence the odd-looking * signature. */ @@ -9036,18 +8736,16 @@ do_pg_abort_backup(int code, Datum arg) bool emit_warning = DatumGetBool(arg); /* - * Quick exit if session is not keeping around a non-exclusive backup - * already started. + * Quick exit if session does not have a running backup. */ - if (sessionBackupState != SESSION_BACKUP_NON_EXCLUSIVE) + if (sessionBackupState != SESSION_BACKUP_RUNNING) return; WALInsertLockAcquireExclusive(); - Assert(XLogCtl->Insert.nonExclusiveBackups > 0); - XLogCtl->Insert.nonExclusiveBackups--; + Assert(XLogCtl->Insert.runningBackups > 0); + XLogCtl->Insert.runningBackups--; - if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && - XLogCtl->Insert.nonExclusiveBackups == 0) + if (XLogCtl->Insert.runningBackups == 0) { XLogCtl->Insert.forcePageWrites = false; } @@ -9055,7 +8753,7 @@ do_pg_abort_backup(int code, Datum arg) if (emit_warning) ereport(WARNING, - (errmsg("aborting backup due to backend exiting before pg_stop_backup was called"))); + (errmsg("aborting backup due to backend exiting before pg_backup_stop was called"))); } /* @@ -9115,87 +8813,6 @@ GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli) LWLockRelease(ControlFileLock); } -/* - * BackupInProgress: check if online backup mode is active - * - * This is done by checking for existence of the "backup_label" file. - */ -bool -BackupInProgress(void) -{ - struct stat stat_buf; - - return (stat(BACKUP_LABEL_FILE, &stat_buf) == 0); -} - -/* - * CancelBackup: rename the "backup_label" and "tablespace_map" - * files to cancel backup mode - * - * If the "backup_label" file exists, it will be renamed to "backup_label.old". - * Similarly, if the "tablespace_map" file exists, it will be renamed to - * "tablespace_map.old". - * - * Note that this will render an online backup in progress - * useless. To correctly finish an online backup, pg_stop_backup must be - * called. - */ -void -CancelBackup(void) -{ - struct stat stat_buf; - - /* if the backup_label file is not there, return */ - if (stat(BACKUP_LABEL_FILE, &stat_buf) < 0) - return; - - /* remove leftover file from previously canceled backup if it exists */ - unlink(BACKUP_LABEL_OLD); - - if (durable_rename(BACKUP_LABEL_FILE, BACKUP_LABEL_OLD, DEBUG1) != 0) - { - ereport(WARNING, - (errcode_for_file_access(), - errmsg("online backup mode was not canceled"), - errdetail("File \"%s\" could not be renamed to \"%s\": %m.", - BACKUP_LABEL_FILE, BACKUP_LABEL_OLD))); - return; - } - - /* if the tablespace_map file is not there, return */ - if (stat(TABLESPACE_MAP, &stat_buf) < 0) - { - ereport(LOG, - (errmsg("online backup mode canceled"), - errdetail("File \"%s\" was renamed to \"%s\".", - BACKUP_LABEL_FILE, BACKUP_LABEL_OLD))); - return; - } - - /* remove leftover file from previously canceled backup if it exists */ - unlink(TABLESPACE_MAP_OLD); - - if (durable_rename(TABLESPACE_MAP, TABLESPACE_MAP_OLD, DEBUG1) == 0) - { - ereport(LOG, - (errmsg("online backup mode canceled"), - errdetail("Files \"%s\" and \"%s\" were renamed to " - "\"%s\" and \"%s\", respectively.", - BACKUP_LABEL_FILE, TABLESPACE_MAP, - BACKUP_LABEL_OLD, TABLESPACE_MAP_OLD))); - } - else - { - ereport(WARNING, - (errcode_for_file_access(), - errmsg("online backup mode canceled"), - errdetail("File \"%s\" was renamed to \"%s\", but " - "file \"%s\" could not be renamed to \"%s\": %m.", - BACKUP_LABEL_FILE, BACKUP_LABEL_OLD, - TABLESPACE_MAP, TABLESPACE_MAP_OLD))); - } -} - /* Thin wrapper around ShutdownWalRcv(). */ void XLogShutdownWalRcv(void) diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 2752be63c10..b61ae6c0b4a 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -39,13 +39,13 @@ #include "utils/tuplestore.h" /* - * Store label file and tablespace map during non-exclusive backups. + * Store label file and tablespace map during backups. */ static StringInfo label_file; static StringInfo tblspc_map_file; /* - * pg_start_backup: 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, * where it will be archived as part of the backup dump. The label file @@ -57,105 +57,44 @@ static StringInfo tblspc_map_file; * GRANT system. */ Datum -pg_start_backup(PG_FUNCTION_ARGS) +pg_backup_start(PG_FUNCTION_ARGS) { text *backupid = PG_GETARG_TEXT_PP(0); bool fast = PG_GETARG_BOOL(1); - bool exclusive = PG_GETARG_BOOL(2); char *backupidstr; XLogRecPtr startpoint; SessionBackupState status = get_backup_status(); + MemoryContext oldcontext; backupidstr = text_to_cstring(backupid); - if (status == SESSION_BACKUP_NON_EXCLUSIVE) + if (status == SESSION_BACKUP_RUNNING) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("a backup is already in progress in this session"))); - if (exclusive) - { - startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL, - NULL, NULL); - } - else - { - MemoryContext oldcontext; - - /* - * Label file and tablespace map file need to be long-lived, since - * they are read in pg_stop_backup. - */ - oldcontext = MemoryContextSwitchTo(TopMemoryContext); - label_file = makeStringInfo(); - tblspc_map_file = makeStringInfo(); - MemoryContextSwitchTo(oldcontext); + /* + * Label file and tablespace map file need to be long-lived, since + * they are read in pg_backup_stop. + */ + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + label_file = makeStringInfo(); + tblspc_map_file = makeStringInfo(); + MemoryContextSwitchTo(oldcontext); - register_persistent_abort_backup_handler(); + register_persistent_abort_backup_handler(); - startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file, - NULL, tblspc_map_file); - } + startpoint = do_pg_backup_start(backupidstr, fast, NULL, label_file, + NULL, tblspc_map_file); PG_RETURN_LSN(startpoint); } -/* - * pg_stop_backup: finish taking an on-line backup dump - * - * We write an end-of-backup WAL record, and remove the backup label file - * created by pg_start_backup, creating a backup history file in pg_wal - * instead (whence it will immediately be archived). The backup history file - * contains the same info found in the label file, plus the backup-end time - * and WAL location. Before 9.0, the backup-end time was read from the backup - * history file at the beginning of archive recovery, but we now use the WAL - * record for that and the file is for informational and debug purposes only. - * - * Note: different from CancelBackup which just cancels online backup mode. - * - * Note: this version is only called to stop an exclusive backup. The function - * pg_stop_backup_v2 (overloaded as pg_stop_backup in SQL) is called to - * stop non-exclusive backups. - * - * Permission checking for this function is managed through the normal - * GRANT system. - */ -Datum -pg_stop_backup(PG_FUNCTION_ARGS) -{ - XLogRecPtr stoppoint; - SessionBackupState status = get_backup_status(); - - if (status == SESSION_BACKUP_NON_EXCLUSIVE) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("non-exclusive backup in progress"), - errhint("Did you mean to use pg_stop_backup('f')?"))); - - /* - * Exclusive backups were typically started in a different connection, so - * don't try to verify that status of backup is set to - * SESSION_BACKUP_EXCLUSIVE in this function. Actual verification that an - * exclusive backup is in fact running is handled inside - * do_pg_stop_backup. - */ - stoppoint = do_pg_stop_backup(NULL, true, NULL); - - PG_RETURN_LSN(stoppoint); -} - /* - * pg_stop_backup_v2: finish taking exclusive or nonexclusive on-line backup. - * - * Works the same as pg_stop_backup, except for non-exclusive backups it returns - * the backup label and tablespace map files as text fields in as part of the - * resultset. + * pg_backup_stop: finish taking an on-line backup. * - * The first parameter (variable 'exclusive') allows the user to tell us if - * this is an exclusive or a non-exclusive backup. - * - * The second parameter (variable 'waitforarchive'), which is optional, + * The first parameter (variable 'waitforarchive'), which is optional, * 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. * @@ -163,15 +102,14 @@ pg_stop_backup(PG_FUNCTION_ARGS) * GRANT system. */ Datum -pg_stop_backup_v2(PG_FUNCTION_ARGS) +pg_backup_stop(PG_FUNCTION_ARGS) { #define PG_STOP_BACKUP_V2_COLS 3 TupleDesc tupdesc; Datum values[PG_STOP_BACKUP_V2_COLS]; bool nulls[PG_STOP_BACKUP_V2_COLS]; - bool exclusive = PG_GETARG_BOOL(0); - bool waitforarchive = PG_GETARG_BOOL(1); + bool waitforarchive = PG_GETARG_BOOL(0); XLogRecPtr stoppoint; SessionBackupState status = get_backup_status(); @@ -182,51 +120,29 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS) MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); - if (exclusive) - { - if (status == SESSION_BACKUP_NON_EXCLUSIVE) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("non-exclusive backup in progress"), - errhint("Did you mean to use pg_stop_backup('f')?"))); - - /* - * Stop the exclusive backup, and since we're in an exclusive backup - * return NULL for both backup_label and tablespace_map. - */ - stoppoint = do_pg_stop_backup(NULL, waitforarchive, NULL); - - nulls[1] = true; - nulls[2] = true; - } - else - { - if (status != SESSION_BACKUP_NON_EXCLUSIVE) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("non-exclusive backup is not in progress"), - errhint("Did you mean to use pg_stop_backup('t')?"))); + if (status != SESSION_BACKUP_RUNNING) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("backup is not in progress"), + errhint("Did you call pg_backup_start()?"))); - /* - * Stop the non-exclusive backup. Return a copy of the backup label - * and tablespace map so they can be written to disk by the caller. - */ - stoppoint = do_pg_stop_backup(label_file->data, waitforarchive, NULL); - - 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; - } + /* + * 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); - /* Stoppoint is included on both exclusive and nonexclusive backups */ 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; /* Returns the record as Datum */ PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); @@ -298,7 +214,7 @@ pg_create_restore_point(PG_FUNCTION_ARGS) } /* - * Report the current WAL write location (same format as pg_start_backup etc) + * Report the current WAL write location (same format as pg_backup_start etc) * * This is useful for determining how much of WAL is visible to an external * archiving process. Note that the data before this point is written out @@ -321,7 +237,7 @@ pg_current_wal_lsn(PG_FUNCTION_ARGS) } /* - * Report the current WAL insert location (same format as pg_start_backup etc) + * Report the current WAL insert location (same format as pg_backup_start etc) * * This function is mostly for debugging purposes. */ @@ -342,7 +258,7 @@ pg_current_wal_insert_lsn(PG_FUNCTION_ARGS) } /* - * Report the current WAL flush location (same format as pg_start_backup etc) + * Report the current WAL flush location (same format as pg_backup_start etc) * * This function is mostly for debugging purposes. */ @@ -363,7 +279,7 @@ pg_current_wal_flush_lsn(PG_FUNCTION_ARGS) } /* - * Report the last WAL receive location (same format as pg_start_backup etc) + * Report the last WAL receive location (same format as pg_backup_start etc) * * This is useful for determining how much of WAL is guaranteed to be received * and synced to disk by walreceiver. @@ -382,7 +298,7 @@ pg_last_wal_receive_lsn(PG_FUNCTION_ARGS) } /* - * Report the last WAL replay location (same format as pg_start_backup etc) + * Report the last WAL replay location (same format as pg_backup_start etc) * * This is useful for determining how much of WAL is visible to read-only * connections during recovery. @@ -402,7 +318,7 @@ pg_last_wal_replay_lsn(PG_FUNCTION_ARGS) /* * Compute an xlog file name and decimal byte offset given a WAL location, - * such as is returned by pg_stop_backup() or pg_switch_wal(). + * such as is returned by pg_backup_stop() or pg_switch_wal(). * * Note that a location exactly at a segment boundary is taken to be in * the previous segment. This is usually the right thing, since the @@ -470,7 +386,7 @@ pg_walfile_name_offset(PG_FUNCTION_ARGS) /* * Compute an xlog file name given a WAL location, - * such as is returned by pg_stop_backup() or pg_switch_wal(). + * such as is returned by pg_backup_stop() or pg_switch_wal(). */ Datum pg_walfile_name(PG_FUNCTION_ARGS) @@ -646,81 +562,6 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS) } /* - * Returns bool with current on-line backup mode, a global state. - */ -Datum -pg_is_in_backup(PG_FUNCTION_ARGS) -{ - PG_RETURN_BOOL(BackupInProgress()); -} - -/* - * Returns start time of an online exclusive backup. - * - * When there's no exclusive backup in progress, the function - * returns NULL. - */ -Datum -pg_backup_start_time(PG_FUNCTION_ARGS) -{ - Datum xtime; - FILE *lfp; - char fline[MAXPGPATH]; - char backup_start_time[30]; - - /* - * See if label file is present - */ - lfp = AllocateFile(BACKUP_LABEL_FILE, "r"); - if (lfp == NULL) - { - if (errno != ENOENT) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not read file \"%s\": %m", - BACKUP_LABEL_FILE))); - PG_RETURN_NULL(); - } - - /* - * Parse the file to find the START TIME line. - */ - backup_start_time[0] = '\0'; - while (fgets(fline, sizeof(fline), lfp) != NULL) - { - if (sscanf(fline, "START TIME: %25[^\n]\n", backup_start_time) == 1) - break; - } - - /* Check for a read error. */ - if (ferror(lfp)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not read file \"%s\": %m", BACKUP_LABEL_FILE))); - - /* Close the backup label file. */ - if (FreeFile(lfp)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not close file \"%s\": %m", BACKUP_LABEL_FILE))); - - if (strlen(backup_start_time) == 0) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE))); - - /* - * Convert the time string read from file to TimestampTz form. - */ - xtime = DirectFunctionCall3(timestamptz_in, - CStringGetDatum(backup_start_time), - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(-1)); - - PG_RETURN_DATUM(xtime); -} - -/* * Promotes a standby server. * * A result of "true" means that promotion has been completed if "wait" is diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index 8d2395dae25..1b7bae387a0 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -1183,9 +1183,14 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI, *backupLabelTLI = tli_from_walseg; /* - * BACKUP METHOD and BACKUP FROM lines are new in 9.2. We can't restore - * from an older backup anyway, but since the information on it is not - * strictly required, don't error out if it's missing for some reason. + * BACKUP METHOD lets us know if this was a typical backup ("streamed", + * which could mean either pg_basebackup or the pg_backup_start/stop + * method was used) or if this label came from somewhere else (the only + * other option today being from pg_rewind). If this was a streamed + * backup then we know that we need to play through until we get to the + * end of the WAL which was generated during the backup (at which point + * we will have reached consistency and backupEndRequired will be reset + * to be false). */ if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1) { @@ -1193,6 +1198,11 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI, *backupEndRequired = true; } + /* + * BACKUP FROM lets us know if this was from a primary or a standby. If + * it was from a standby, we'll double-check that the control file state + * matches that of a standby. + */ if (fscanf(lfp, "BACKUP FROM: %19s\n", backupfrom) == 1) { if (strcmp(backupfrom, "standby") == 0) @@ -1970,7 +1980,7 @@ xlogrecovery_redo(XLogReaderState *record, TimeLineID replayTLI) { /* * We have reached the end of base backup, the point where - * pg_stop_backup() was done. The data on disk is now consistent + * pg_backup_stop() was done. The data on disk is now consistent * (assuming we have also reached minRecoveryPoint). Set * backupEndPoint to the current LSN, so that the next call to * CheckRecoveryConsistency() will notice it and do the @@ -2033,7 +2043,7 @@ CheckRecoveryConsistency(void) /* * Have we passed our safe starting point? Note that minRecoveryPoint is - * known to be incorrectly set if ControlFile->backupEndRequired, until + * known to be incorrectly set if recovering from a backup, until * the XLOG_BACKUP_END arrives to advise us of the correct * minRecoveryPoint. All we know prior to that is that we're not * consistent yet. |