diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml index 8c24ae2..59ef116 100644 --- a/doc/src/sgml/recovery-config.sgml +++ b/doc/src/sgml/recovery-config.sgml @@ -211,6 +211,11 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows The precise stopping point is also influenced by . + + This parameter also checks if the checkpoint time of the backup being used is earlier than the specified recovery_target_time. + In other words, the recovery will not proceed further if the checkpoint time of the backup is later than the specified recovery_target_time. + + @@ -231,6 +236,11 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows The precise stopping point is also influenced by . + + This parameter also checks if the latest completed xid of the backup is earlier than the specified recovery target xid. + In other words, the recovery will not proceed further if the latest completed xid of the backup is later than the specified target xid. + + @@ -248,6 +258,11 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows parameter is parsed using the system data type pg_lsn. + + This parameter also checks if the current checkpoint's redo position of the backup is earlier than the specified recovery target lsn. + In other words, the recovery will not proceed further if the latest checkpoint's redo position of the backup is later than the specified target lsn. + + diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 8379133..3043864 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -826,6 +826,7 @@ static MemoryContext walDebugCxt = NULL; #endif static void readRecoveryCommandFile(void); +static void recoveryStartsHere(void); static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog); static bool recoveryStopsBefore(XLogReaderState *record); static bool recoveryStopsAfter(XLogReaderState *record); @@ -5449,6 +5450,67 @@ getRecordTimestamp(XLogReaderState *record, TimestampTz *recordXtime) return false; } +/* When performing point-in-time-recovery, this function identifies if + * the specified recovery target (recovery_target_time, recovery_target_lsn and recovery_target_xid) is prior to that of the backup. + * Which means, recovery cannot proceed if the recovery target point is prior to backup start point. + */ + +static void +recoveryStartsHere(void) +{ + /* + * Check if the recovery target xid is older than the latest completed xid + * of the backup + */ + + if (recoveryTarget == RECOVERY_TARGET_XID) + { + if (TransactionIdPrecedes(recoveryTargetXid, + ControlFile->checkPointCopy.latestCompletedXid)) + { + ereport(ERROR, + (errmsg("recovery_target_xid %u is older than the latest Completed Xid %u", recoveryTargetXid, ControlFile->checkPointCopy.latestCompletedXid), + errhint("This means that the backup being used is much later than the recovery target position.\n" + "You might need to use a backup taken prior to the recovery target point."))); + } + } + + /* + * Check if the recovery target lsn is prior to the latest checkpointi's + * redo position of the backup + */ + + if (recoveryTarget == RECOVERY_TARGET_LSN) + { + if (recoveryTargetLSN < ControlFile->checkPointCopy.redo) + { + ereport(ERROR, + (errmsg("recovery_target_lsn \"%X/%X\" is older than the backup start LSN \"%X/%X\"", + (uint32) (recoveryTargetLSN >> 32), + (uint32) recoveryTargetLSN, (uint32) (ControlFile->checkPointCopy.redo >> 32), (uint32) ControlFile->checkPointCopy.redo), + errhint("This means that the backup being used is much later than the recovery target position.\n" + "You might need to use a backup taken prior to the recovery target point."))); + } + } + + /* + * Check if the recovery target time is prior to the current timestamp of + * the backup + */ + + if (recoveryTarget == RECOVERY_TARGET_TIME) + { + if (recoveryTargetTime < ControlFile->checkPointCopy.time) + { + ereport(ERROR, + (errmsg("recovery_target_time %s is older than the backup start time %s", timestamptz_to_str(recoveryTargetTime), + timestamptz_to_str(ControlFile->checkPointCopy.time)), + errhint("This means that the backup being used is much later than the recovery target position.\n" + "You might need to use a backup taken prior to the recovery target point."))); + } + } +} + /* * For point-in-time recovery, this function decides whether we want to * stop applying the XLOG before the current record. @@ -6182,6 +6244,9 @@ StartupXLOG(void) (errmsg("starting archive recovery"))); } + /* Check if archive recovery can start at all */ + recoveryStartsHere(); + /* * Take ownership of the wakeup latch if we're going to sleep during * recovery. @@ -8531,6 +8596,7 @@ CreateCheckPoint(int flags) checkPoint.nextXid = ShmemVariableCache->nextXid; checkPoint.oldestXid = ShmemVariableCache->oldestXid; checkPoint.oldestXidDB = ShmemVariableCache->oldestXidDB; + checkPoint.latestCompletedXid = ShmemVariableCache->latestCompletedXid; LWLockRelease(XidGenLock); LWLockAcquire(CommitTsLock, LW_SHARED); diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 20077a6..f340f1c 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -222,6 +222,8 @@ main(int argc, char *argv[]) printf(_("Latest checkpoint's NextXID: %u:%u\n"), ControlFile->checkPointCopy.nextXidEpoch, ControlFile->checkPointCopy.nextXid); + printf(_("Latest checkpoint's latestCompletedXID: %u\n"), + ControlFile->checkPointCopy.latestCompletedXid); printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile->checkPointCopy.nextOid); printf(_("Latest checkpoint's NextMultiXactId: %u\n"), diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 23731e9..7ed9484 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -21,7 +21,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 960 +#define PG_CONTROL_VERSION 100 /* * Body of CheckPoint XLOG records. This is declared here because we keep @@ -58,6 +58,7 @@ typedef struct CheckPoint * set to InvalidTransactionId. */ TransactionId oldestActiveXid; + TransactionId latestCompletedXid; } CheckPoint; /* XLOG info values for XLOG rmgr */