diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index e4645a3..c2ec9d3 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -635,6 +635,15 @@ typedef struct XLogCtlData TimeLineID RecoveryTargetTLI; /* + * Set of fields indicating the position to return to callers of + * do_pg_stop_backup. This field gets updated when the minimum + * recovery point is refreshed and when a new restart point is + * created even if the minimum recovery point is not. + */ + XLogRecPtr stopBackupLSN; + TimeLineID stopBackupTLI; + + /* * timestamp of when we started replaying the current chunk of WAL data, * only relevant for replication or archive recovery */ @@ -2529,10 +2538,19 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force) * value as the min recovery point would prevent us from coming up at * all. Instead, we just log a warning and continue with recovery. * (See also the comments about corrupt LSNs in XLogFlush.) + * + * Refresh at the same time the LSN and timeline positions for the + * stop positions of backups. This is done here to not have to take + * info_lck more than necessary, and those values are related to + * the minimum recovery point, except that they get updated even + * if minRecoveryPoint is not refreshed when creating a restart + * point, but those are. */ SpinLockAcquire(&XLogCtl->info_lck); newMinRecoveryPoint = XLogCtl->replayEndRecPtr; newMinRecoveryPointTLI = XLogCtl->replayEndTLI; + XLogCtl->stopBackupLSN = XLogCtl->replayEndRecPtr; + XLogCtl->stopBackupTLI = XLogCtl->replayEndTLI; SpinLockRelease(&XLogCtl->info_lck); if (!force && newMinRecoveryPoint < lsn) @@ -6657,6 +6675,8 @@ StartupXLOG(void) XLogCtl->recoveryLastXTime = 0; XLogCtl->currentChunkStartTime = 0; XLogCtl->recoveryPause = false; + XLogCtl->stopBackupLSN = XLogCtl->replayEndRecPtr; + XLogCtl->stopBackupTLI = XLogCtl->replayEndTLI; SpinLockRelease(&XLogCtl->info_lck); /* Also ensure XLogReceiptTime has a sane value */ @@ -8779,6 +8799,8 @@ CreateRestartPoint(int flags) /* Also update the info_lck-protected copy */ SpinLockAcquire(&XLogCtl->info_lck); XLogCtl->RedoRecPtr = lastCheckPoint.redo; + XLogCtl->stopBackupLSN = XLogCtl->replayEndRecPtr; + XLogCtl->stopBackupTLI = XLogCtl->replayEndTLI;; SpinLockRelease(&XLogCtl->info_lck); /* @@ -10397,11 +10419,15 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) /* * During recovery, we don't write an end-of-backup record. We assume that * pg_control was backed up last and its minimum recovery point can be - * available as the backup end location. Since we don't have an - * end-of-backup record, we use the pg_control value to check whether - * we've reached the end of backup when starting recovery from this - * backup. We have no way of checking if pg_control wasn't backed up last - * however. + * available as the backup end location. Since we don't have an + * end-of-backup record, we use the in-memory state of XLOG to decide + * what are the end location using stopBackupLSN and stopBackupTLI + * which are set when the minimum recovery LSN is bumped or when a + * restart point is created the minimum recovery LSN was not updated + * because no data has been flushed when replaying records. This + * prevents cases where the start location of a backup is newer than + * its end location because the minimum recovery point may not be + * updated. * * 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 @@ -10409,10 +10435,9 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) * required files are available, a user should wait for them to be * archived, or include them into the backup. * - * We return the current minimum recovery point as the backup end - * location. Note that it can be greater than the exact backup end - * location if the minimum recovery point is updated after the backup of - * pg_control. This is harmless for current uses. + * Note that the backup end location can be greater than the exact backup + * end location if the minimum recovery point is updated after the backup + * of 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, @@ -10430,6 +10455,8 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) */ SpinLockAcquire(&XLogCtl->info_lck); recptr = XLogCtl->lastFpwDisableRecPtr; + stoppoint = XLogCtl->stopBackupLSN; + stoptli = XLogCtl->stopBackupTLI; SpinLockRelease(&XLogCtl->info_lck); if (startpoint <= recptr) @@ -10442,12 +10469,6 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) "Enable full_page_writes and run CHECKPOINT on the master, " "and then try an online backup again."))); - - LWLockAcquire(ControlFileLock, LW_SHARED); - stoppoint = ControlFile->minRecoveryPoint; - stoptli = ControlFile->minRecoveryPointTLI; - LWLockRelease(ControlFileLock); - if (stoptli_p) *stoptli_p = stoptli; return stoppoint; diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl index 7b42f21..cee4768 100644 --- a/src/test/recovery/t/001_stream_rep.pl +++ b/src/test/recovery/t/001_stream_rep.pl @@ -24,6 +24,11 @@ $node_standby_1->start; # pg_basebackup works on a standby). $node_standby_1->backup($backup_name); +# Take a second backup of the standby while the master is offline. +$node_master->stop; +$node_standby_1->backup('my_backup_2'); +$node_master->start; + # Create second standby node linking to standby 1 my $node_standby_2 = get_new_node('standby_2'); $node_standby_2->init_from_backup($node_standby_1, $backup_name,