aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/access/transam/xlog.c29
-rw-r--r--src/test/recovery/t/001_stream_rep.pl5
2 files changed, 33 insertions, 1 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0b991bb91de..f66f940f7e0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -612,11 +612,14 @@ typedef struct XLogCtlData
/*
* During recovery, we keep a copy of the latest checkpoint record here.
- * Used by the background writer when it wants to create a restartpoint.
+ * lastCheckPointRecPtr points to start of checkpoint record and
+ * lastCheckPointEndPtr points to end+1 of checkpoint record. Used by the
+ * background writer when it wants to create a restartpoint.
*
* Protected by info_lck.
*/
XLogRecPtr lastCheckPointRecPtr;
+ XLogRecPtr lastCheckPointEndPtr;
CheckPoint lastCheckPoint;
/*
@@ -8691,6 +8694,7 @@ RecoveryRestartPoint(const CheckPoint *checkPoint)
*/
SpinLockAcquire(&XLogCtl->info_lck);
XLogCtl->lastCheckPointRecPtr = ReadRecPtr;
+ XLogCtl->lastCheckPointEndPtr = EndRecPtr;
XLogCtl->lastCheckPoint = *checkPoint;
SpinLockRelease(&XLogCtl->info_lck);
}
@@ -8710,6 +8714,7 @@ bool
CreateRestartPoint(int flags)
{
XLogRecPtr lastCheckPointRecPtr;
+ XLogRecPtr lastCheckPointEndPtr;
CheckPoint lastCheckPoint;
XLogRecPtr PriorRedoPtr;
TimestampTz xtime;
@@ -8723,6 +8728,7 @@ CreateRestartPoint(int flags)
/* Get a local copy of the last safe checkpoint record. */
SpinLockAcquire(&XLogCtl->info_lck);
lastCheckPointRecPtr = XLogCtl->lastCheckPointRecPtr;
+ lastCheckPointEndPtr = XLogCtl->lastCheckPointEndPtr;
lastCheckPoint = XLogCtl->lastCheckPoint;
SpinLockRelease(&XLogCtl->info_lck);
@@ -8826,6 +8832,27 @@ CreateRestartPoint(int flags)
ControlFile->checkPoint = lastCheckPointRecPtr;
ControlFile->checkPointCopy = lastCheckPoint;
ControlFile->time = (pg_time_t) time(NULL);
+
+ /*
+ * 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
+ * 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
+ * no value in having the minimum recovery point any earlier than this
+ * anyway, because redo will begin just after the checkpoint record.
+ */
+ if (ControlFile->minRecoveryPoint < lastCheckPointEndPtr)
+ {
+ ControlFile->minRecoveryPoint = lastCheckPointEndPtr;
+ ControlFile->minRecoveryPointTLI = lastCheckPoint.ThisTimeLineID;
+
+ /* update local copy */
+ minRecoveryPoint = ControlFile->minRecoveryPoint;
+ minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
+ }
if (flags & CHECKPOINT_IS_SHUTDOWN)
ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY;
UpdateControlFile();
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index fd71095f06a..981c00ba3ec 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,