Skip to content

Commit ef18cb7

Browse files
committed
Fix possible pg_basebackup failure on standby with "include WAL".
If a restartpoint flushed no dirty buffers, it could fail to update the minimum recovery point, leading to a minimum recovery point prior to the starting REDO location. perform_base_backup() would interpret that as meaning that no WAL files at all needed to be included in the backup, failing an internal sanity check. To fix, have restartpoints always update the minimum recovery point to just after the checkpoint record itself, so that the file (or files) containing the checkpoint record will always be included in the backup. Code by Amit Kapila, per a design suggestion by me, with some additional work on the code comment by me. Test case by Michael Paquier. Report by Kyotaro Horiguchi.
1 parent b53c841 commit ef18cb7

File tree

1 file changed

+28
-1
lines changed
  • src/backend/access/transam

1 file changed

+28
-1
lines changed

src/backend/access/transam/xlog.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,11 +610,14 @@ typedef struct XLogCtlData
610610

611611
/*
612612
* During recovery, we keep a copy of the latest checkpoint record here.
613-
* Used by the background writer when it wants to create a restartpoint.
613+
* lastCheckPointRecPtr points to start of checkpoint record and
614+
* lastCheckPointEndPtr points to end+1 of checkpoint record. Used by the
615+
* background writer when it wants to create a restartpoint.
614616
*
615617
* Protected by info_lck.
616618
*/
617619
XLogRecPtr lastCheckPointRecPtr;
620+
XLogRecPtr lastCheckPointEndPtr;
618621
CheckPoint lastCheckPoint;
619622

620623
/*
@@ -8644,6 +8647,7 @@ RecoveryRestartPoint(const CheckPoint *checkPoint)
86448647
*/
86458648
SpinLockAcquire(&XLogCtl->info_lck);
86468649
XLogCtl->lastCheckPointRecPtr = ReadRecPtr;
8650+
XLogCtl->lastCheckPointEndPtr = EndRecPtr;
86478651
XLogCtl->lastCheckPoint = *checkPoint;
86488652
SpinLockRelease(&XLogCtl->info_lck);
86498653
}
@@ -8663,6 +8667,7 @@ bool
86638667
CreateRestartPoint(int flags)
86648668
{
86658669
XLogRecPtr lastCheckPointRecPtr;
8670+
XLogRecPtr lastCheckPointEndPtr;
86668671
CheckPoint lastCheckPoint;
86678672
XLogRecPtr PriorRedoPtr;
86688673
TimestampTz xtime;
@@ -8676,6 +8681,7 @@ CreateRestartPoint(int flags)
86768681
/* Get a local copy of the last safe checkpoint record. */
86778682
SpinLockAcquire(&XLogCtl->info_lck);
86788683
lastCheckPointRecPtr = XLogCtl->lastCheckPointRecPtr;
8684+
lastCheckPointEndPtr = XLogCtl->lastCheckPointEndPtr;
86798685
lastCheckPoint = XLogCtl->lastCheckPoint;
86808686
SpinLockRelease(&XLogCtl->info_lck);
86818687

@@ -8779,6 +8785,27 @@ CreateRestartPoint(int flags)
87798785
ControlFile->checkPoint = lastCheckPointRecPtr;
87808786
ControlFile->checkPointCopy = lastCheckPoint;
87818787
ControlFile->time = (pg_time_t) time(NULL);
8788+
8789+
/*
8790+
* Ensure minRecoveryPoint is past the checkpoint record. Normally,
8791+
* this will have happened already while writing out dirty buffers,
8792+
* but not necessarily - e.g. because no buffers were dirtied. We do
8793+
* this because a non-exclusive base backup uses minRecoveryPoint to
8794+
* determine which WAL files must be included in the backup, and the
8795+
* file (or files) containing the checkpoint record must be included,
8796+
* at a minimum. Note that for an ordinary restart of recovery there's
8797+
* no value in having the minimum recovery point any earlier than this
8798+
* anyway, because redo will begin just after the checkpoint record.
8799+
*/
8800+
if (ControlFile->minRecoveryPoint < lastCheckPointEndPtr)
8801+
{
8802+
ControlFile->minRecoveryPoint = lastCheckPointEndPtr;
8803+
ControlFile->minRecoveryPointTLI = lastCheckPoint.ThisTimeLineID;
8804+
8805+
/* update local copy */
8806+
minRecoveryPoint = ControlFile->minRecoveryPoint;
8807+
minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
8808+
}
87828809
if (flags & CHECKPOINT_IS_SHUTDOWN)
87838810
ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY;
87848811
UpdateControlFile();

0 commit comments

Comments
 (0)