postgres.git / summary / log / commit / refs

commit    d1e9c8269bb1b8eae595c48b67f11e3fd4658170
Author:   Robert Haas <rhaas@postgresql.org>
Date:     Thu Oct 27 15:19:51 2016 +0000

    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.


src/backend/access/transam/xlog.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 4afa991ad66..0f000d52a9e 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -571,11 +571,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; /* @@ -8630,6 +8633,7 @@ RecoveryRestartPoint(const CheckPoint *checkPoint) */ SpinLockAcquire(&xlogctl->info_lck); xlogctl->lastCheckPointRecPtr = ReadRecPtr; + xlogctl->lastCheckPointEndPtr = EndRecPtr; xlogctl->lastCheckPoint = *checkPoint; SpinLockRelease(&xlogctl->info_lck); } @@ -8649,6 +8653,7 @@ bool CreateRestartPoint(int flags) { XLogRecPtr lastCheckPointRecPtr; + XLogRecPtr lastCheckPointEndPtr; CheckPoint lastCheckPoint; XLogSegNo _logSegNo; TimestampTz xtime; @@ -8665,6 +8670,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); @@ -8768,6 +8774,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(); [parent: 3a9a8c408378]