diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6cec027..ba417c4 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -252,6 +252,7 @@ static char *archiveCleanupCommand = NULL; static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; static bool recoveryTargetInclusive = true; static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; +static RecoveryTargetIncomplete recoveryTargetIncomplete = RECOVERY_TARGET_INCOMPLETE_PROMOTE; static TransactionId recoveryTargetXid; static TimestampTz recoveryTargetTime; static char *recoveryTargetName; @@ -792,10 +793,12 @@ 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); static void recoveryPausesHere(void); +static void IncompleteRecoveryPause(void); static bool recoveryApplyDelay(XLogReaderState *record); static void SetLatestXTime(TimestampTz xtime); static void SetCurrentChunkStartTime(TimestampTz xtime); @@ -5018,6 +5021,26 @@ readRecoveryCommandFile(void) recoveryTargetActionSet = true; } + else if (strcmp(item->name, "recovery_target_incomplete") == 0) + { + if (strcmp(item->value, "pause") == 0) + recoveryTargetIncomplete = RECOVERY_TARGET_INCOMPLETE_PAUSE; + else if (strcmp(item->value, "promote") == 0) + recoveryTargetIncomplete = RECOVERY_TARGET_INCOMPLETE_PROMOTE; + else if (strcmp(item->value, "shutdown") == 0) + recoveryTargetIncomplete = RECOVERY_TARGET_INCOMPLETE_SHUTDOWN; + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid value for recovery parameter \"%s\": \"%s\"", + "recovery_target_incomplete", + item->value), + errhint("Valid values are \"pause\", \"promote\", and \"shutdown\"."))); + + ereport(DEBUG2, + (errmsg_internal("recovery_target_incomplete = '%s'", + item->value))); + } else if (strcmp(item->name, "recovery_target_timeline") == 0) { rtliGiven = true; @@ -5399,6 +5422,66 @@ 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(FATAL, + (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." + "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(FATAL, + (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." + "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(FATAL, + (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." + "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. @@ -5744,6 +5827,23 @@ SetRecoveryPause(bool recoveryPause) SpinLockRelease(&XLogCtl->info_lck); } +static void +IncompleteRecoveryPause(void) +{ + /* Pause recovery at end-of-the-wal when recovery target is not reached */ + ereport(LOG, + (errmsg("recovery has paused"), + errhint("Execute pg_xlog_replay_resume() to continue."))); + + while (RecoveryIsPaused()) + { + pg_usleep(1000000L); /* 1000 ms */ + HandleStartupProcInterrupts(); + } +} + + + /* * When recovery_min_apply_delay is set, we wait long enough to make sure * certain record types are applied at least that interval behind the master. @@ -6132,6 +6232,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. @@ -7044,6 +7147,44 @@ StartupXLOG(void) break; } } + else + { + ereport(LOG, + (errmsg("recovery has reached end-of-the-wal and has not reached the recovery target yet"), + errhint("This could be due to corrupt or missing WAL files.\n" + "All the WAL files needed for the recovery must be available to proceed to the recovery target " + "Or you might need to choose an earlier recovery target."))); + + /* + * This is the position where we can choose to shutdown, pause + * or promote at the end-of-the-wal if the intended recovery + * target is not reached + */ + switch (recoveryTargetIncomplete) + { + + case RECOVERY_TARGET_INCOMPLETE_SHUTDOWN: + + /* + * exit with special return code to request shutdown + * of postmaster. Log messages issued from postmaster. + */ + + ereport(LOG, + (errmsg("shutdown at end-of-the-wal"))); + proc_exit(2); + + case RECOVERY_TARGET_INCOMPLETE_PAUSE: + + SetRecoveryPause(true); + IncompleteRecoveryPause(); + + /* drop into promote */ + + case RECOVERY_TARGET_INCOMPLETE_PROMOTE: + break; + } + } /* Allow resource managers to do any required cleanup. */ for (rmid = 0; rmid <= RM_MAX_ID; rmid++) @@ -8450,6 +8591,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..d6d0852 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -192,114 +192,116 @@ main(int argc, char *argv[]) snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT, ControlFile->system_identifier); - printf(_("pg_control version number: %u\n"), + printf(_("pg_control version number: %u\n"), ControlFile->pg_control_version); - printf(_("Catalog version number: %u\n"), + printf(_("Catalog version number: %u\n"), ControlFile->catalog_version_no); - printf(_("Database system identifier: %s\n"), + printf(_("Database system identifier: %s\n"), sysident_str); - printf(_("Database cluster state: %s\n"), + printf(_("Database cluster state: %s\n"), dbState(ControlFile->state)); - printf(_("pg_control last modified: %s\n"), + printf(_("pg_control last modified: %s\n"), pgctime_str); - printf(_("Latest checkpoint location: %X/%X\n"), + printf(_("Latest checkpoint location: %X/%X\n"), (uint32) (ControlFile->checkPoint >> 32), (uint32) ControlFile->checkPoint); - printf(_("Prior checkpoint location: %X/%X\n"), + printf(_("Prior checkpoint location: %X/%X\n"), (uint32) (ControlFile->prevCheckPoint >> 32), (uint32) ControlFile->prevCheckPoint); - printf(_("Latest checkpoint's REDO location: %X/%X\n"), + printf(_("Latest checkpoint's REDO location: %X/%X\n"), (uint32) (ControlFile->checkPointCopy.redo >> 32), (uint32) ControlFile->checkPointCopy.redo); - printf(_("Latest checkpoint's REDO WAL file: %s\n"), + printf(_("Latest checkpoint's REDO WAL file: %s\n"), xlogfilename); - printf(_("Latest checkpoint's TimeLineID: %u\n"), + printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile->checkPointCopy.ThisTimeLineID); - printf(_("Latest checkpoint's PrevTimeLineID: %u\n"), + printf(_("Latest checkpoint's PrevTimeLineID: %u\n"), ControlFile->checkPointCopy.PrevTimeLineID); - printf(_("Latest checkpoint's full_page_writes: %s\n"), + printf(_("Latest checkpoint's full_page_writes: %s\n"), ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off")); - printf(_("Latest checkpoint's NextXID: %u:%u\n"), + printf(_("Latest checkpoint's latestCompletedXID: %u\n"), + ControlFile->checkPointCopy.latestCompletedXid); + printf(_("Latest checkpoint's NextXID: %u:%u\n"), ControlFile->checkPointCopy.nextXidEpoch, ControlFile->checkPointCopy.nextXid); - printf(_("Latest checkpoint's NextOID: %u\n"), + printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile->checkPointCopy.nextOid); - printf(_("Latest checkpoint's NextMultiXactId: %u\n"), + printf(_("Latest checkpoint's NextMultiXactId: %u\n"), ControlFile->checkPointCopy.nextMulti); - printf(_("Latest checkpoint's NextMultiOffset: %u\n"), + printf(_("Latest checkpoint's NextMultiOffset: %u\n"), ControlFile->checkPointCopy.nextMultiOffset); - printf(_("Latest checkpoint's oldestXID: %u\n"), + printf(_("Latest checkpoint's oldestXID: %u\n"), ControlFile->checkPointCopy.oldestXid); - printf(_("Latest checkpoint's oldestXID's DB: %u\n"), + printf(_("Latest checkpoint's oldestXID's DB: %u\n"), ControlFile->checkPointCopy.oldestXidDB); - printf(_("Latest checkpoint's oldestActiveXID: %u\n"), + printf(_("Latest checkpoint's oldestActiveXID: %u\n"), ControlFile->checkPointCopy.oldestActiveXid); - printf(_("Latest checkpoint's oldestMultiXid: %u\n"), + printf(_("Latest checkpoint's oldestMultiXid: %u\n"), ControlFile->checkPointCopy.oldestMulti); - printf(_("Latest checkpoint's oldestMulti's DB: %u\n"), + printf(_("Latest checkpoint's oldestMulti's DB: %u\n"), ControlFile->checkPointCopy.oldestMultiDB); - printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"), + printf(_("Latest checkpoint's oldestCommitTsXid: %u\n"), ControlFile->checkPointCopy.oldestCommitTsXid); - printf(_("Latest checkpoint's newestCommitTsXid:%u\n"), + printf(_("Latest checkpoint's newestCommitTsXid: %u\n"), ControlFile->checkPointCopy.newestCommitTsXid); - printf(_("Time of latest checkpoint: %s\n"), + printf(_("Time of latest checkpoint: %s\n"), ckpttime_str); - printf(_("Fake LSN counter for unlogged rels: %X/%X\n"), + printf(_("Fake LSN counter for unlogged rels: %X/%X\n"), (uint32) (ControlFile->unloggedLSN >> 32), (uint32) ControlFile->unloggedLSN); - printf(_("Minimum recovery ending location: %X/%X\n"), + printf(_("Minimum recovery ending location: %X/%X\n"), (uint32) (ControlFile->minRecoveryPoint >> 32), (uint32) ControlFile->minRecoveryPoint); - printf(_("Min recovery ending loc's timeline: %u\n"), + printf(_("Min recovery ending loc's timeline: %u\n"), ControlFile->minRecoveryPointTLI); - printf(_("Backup start location: %X/%X\n"), + printf(_("Backup start location: %X/%X\n"), (uint32) (ControlFile->backupStartPoint >> 32), (uint32) ControlFile->backupStartPoint); - printf(_("Backup end location: %X/%X\n"), + printf(_("Backup end location: %X/%X\n"), (uint32) (ControlFile->backupEndPoint >> 32), (uint32) ControlFile->backupEndPoint); - printf(_("End-of-backup record required: %s\n"), + printf(_("End-of-backup record required: %s\n"), ControlFile->backupEndRequired ? _("yes") : _("no")); - printf(_("wal_level setting: %s\n"), + printf(_("wal_level setting: %s\n"), wal_level_str(ControlFile->wal_level)); - printf(_("wal_log_hints setting: %s\n"), + printf(_("wal_log_hints setting: %s\n"), ControlFile->wal_log_hints ? _("on") : _("off")); - printf(_("max_connections setting: %d\n"), + printf(_("max_connections setting: %d\n"), ControlFile->MaxConnections); - printf(_("max_worker_processes setting: %d\n"), + printf(_("max_worker_processes setting: %d\n"), ControlFile->max_worker_processes); - printf(_("max_prepared_xacts setting: %d\n"), + printf(_("max_prepared_xacts setting: %d\n"), ControlFile->max_prepared_xacts); - printf(_("max_locks_per_xact setting: %d\n"), + printf(_("max_locks_per_xact setting: %d\n"), ControlFile->max_locks_per_xact); - printf(_("track_commit_timestamp setting: %s\n"), + printf(_("track_commit_timestamp setting: %s\n"), ControlFile->track_commit_timestamp ? _("on") : _("off")); - printf(_("Maximum data alignment: %u\n"), + printf(_("Maximum data alignment: %u\n"), ControlFile->maxAlign); /* we don't print floatFormat since can't say much useful about it */ - printf(_("Database block size: %u\n"), + printf(_("Database block size: %u\n"), ControlFile->blcksz); - printf(_("Blocks per segment of large relation: %u\n"), + printf(_("Blocks per segment of large relation: %u\n"), ControlFile->relseg_size); - printf(_("WAL block size: %u\n"), + printf(_("WAL block size: %u\n"), ControlFile->xlog_blcksz); - printf(_("Bytes per WAL segment: %u\n"), + printf(_("Bytes per WAL segment: %u\n"), ControlFile->xlog_seg_size); - printf(_("Maximum length of identifiers: %u\n"), + printf(_("Maximum length of identifiers: %u\n"), ControlFile->nameDataLen); - printf(_("Maximum columns in an index: %u\n"), + printf(_("Maximum columns in an index: %u\n"), ControlFile->indexMaxKeys); - printf(_("Maximum size of a TOAST chunk: %u\n"), + printf(_("Maximum size of a TOAST chunk: %u\n"), ControlFile->toast_max_chunk_size); - printf(_("Size of a large-object chunk: %u\n"), + printf(_("Size of a large-object chunk: %u\n"), ControlFile->loblksize); - printf(_("Date/time type storage: %s\n"), + printf(_("Date/time type storage: %s\n"), (ControlFile->enableIntTimes ? _("64-bit integers") : _("floating-point numbers"))); - printf(_("Float4 argument passing: %s\n"), + printf(_("Float4 argument passing: %s\n"), (ControlFile->float4ByVal ? _("by value") : _("by reference"))); - printf(_("Float8 argument passing: %s\n"), + printf(_("Float8 argument passing: %s\n"), (ControlFile->float8ByVal ? _("by value") : _("by reference"))); - printf(_("Data page checksum version: %u\n"), + printf(_("Data page checksum version: %u\n"), ControlFile->data_checksum_version); return 0; } diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index ceb0462..35eabf2 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -256,6 +256,18 @@ typedef enum } RecoveryTargetAction; /* + * Recovery target incomplete. + */ + +typedef enum +{ + RECOVERY_TARGET_INCOMPLETE_PAUSE, + RECOVERY_TARGET_INCOMPLETE_PROMOTE, + RECOVERY_TARGET_INCOMPLETE_SHUTDOWN +} RecoveryTargetIncomplete; + + +/* * Method table for resource managers. * * This struct must be kept in sync with the PG_RMGR definition in diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 0bc41ab..20da26b 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 */