*/
static int LocalXLogInsertAllowed = -1;
-/* Are we recovering using offline XLOG archives? */
+/*
+ * When ArchiveRecoveryRequested is set, archive recovery was requested,
+ * ie. recovery.conf file was present. When InArchiveRecovery is set, we are
+ * currently recovering using offline XLOG archives. These variables are only
+ * valid in the startup process.
+ *
+ * When ArchiveRecoveryRequested is true, but InArchiveRecovery is false, we're
+ * currently performing crash recovery using only XLOG files in pg_xlog, but
+ * will switch to using offline XLOG archives as soon as we reach the end of
+ * WAL in pg_xlog.
+*/
+static bool ArchiveRecoveryRequested = false;
static bool InArchiveRecovery = false;
/* Was the last xlog file restored from archive, or local? */
static char *recoveryTargetName;
/* options taken from recovery.conf for XLOG streaming */
-static bool StandbyMode = false;
+static bool StandbyModeRequested = false;
static char *PrimaryConnInfo = NULL;
static char *TriggerFile = NULL;
+/* are we currently in standby mode? */
+bool StandbyMode = false;
+
/* if recoveryStopsHere returns true, it saves actual stop xid/time/name here */
static TransactionId recoveryStopXid;
static TimestampTz recoveryStopTime;
readFile = -1;
}
+ /*
+ * If archive recovery was requested, but we were still doing crash
+ * recovery, switch to archive recovery and retry using the offline
+ * archive. We have now replayed all the valid WAL in pg_xlog, so
+ * we are presumably now consistent.
+ *
+ * We require that there's at least some valid WAL present in
+ * pg_xlog, however (!fetch_ckpt). We could recover using the WAL
+ * from the archive, even if pg_xlog is completely empty, but we'd
+ * have no idea how far we'd have to replay to reach consistency.
+ * So err on the safe side and give up.
+ */
+ if (!InArchiveRecovery && ArchiveRecoveryRequested && !fetching_ckpt)
+ {
+ ereport(DEBUG1,
+ (errmsg_internal("reached end of WAL in pg_xlog, entering archive recovery")));
+ InArchiveRecovery = true;
+ if (StandbyModeRequested)
+ StandbyMode = true;
+
+ /* initialize minRecoveryPoint to this record */
+ LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
+ ControlFile->state = DB_IN_ARCHIVE_RECOVERY;
+ if (XLByteLT(ControlFile->minRecoveryPoint, EndRecPtr))
+ ControlFile->minRecoveryPoint = EndRecPtr;
+
+ /* update local copy */
+ minRecoveryPoint = ControlFile->minRecoveryPoint;
+
+ UpdateControlFile();
+ LWLockRelease(ControlFileLock);
+
+ CheckRecoveryConsistency();
+
+ goto retry;
+ }
+
/* In standby-mode, keep trying */
if (StandbyMode)
goto retry;
}
else if (strcmp(item->name, "standby_mode") == 0)
{
- if (!parse_bool(item->value, &StandbyMode))
+ if (!parse_bool(item->value, &StandbyModeRequested))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" requires a Boolean value",
/*
* Check for compulsory parameters
*/
- if (StandbyMode)
+ if (StandbyModeRequested)
{
if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
ereport(WARNING,
}
/* Enable fetching from archive recovery area */
- InArchiveRecovery = true;
+ ArchiveRecoveryRequested = true;
/*
* If user specified recovery_target_timeline, validate it or compute the
*/
if (rtliGiven)
{
+ /*
+ * Temporarily set InArchiveRecovery, so that existsTimeLineHistory
+ * or findNewestTimeLine below will check the archive.
+ */
+ InArchiveRecovery = true;
if (rtli)
{
/* Timeline 1 does not have a history file, all else should */
recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
recoveryTargetIsLatest = true;
}
+ InArchiveRecovery = false;
}
FreeConfigVariables(head);
archiveCleanupCommand ? archiveCleanupCommand : "",
sizeof(XLogCtl->archiveCleanupCommand));
- if (InArchiveRecovery)
+ if (ArchiveRecoveryRequested)
{
- if (StandbyMode)
+ if (StandbyModeRequested)
ereport(LOG,
(errmsg("entering standby mode")));
else if (recoveryTarget == RECOVERY_TARGET_XID)
* Take ownership of the wakeup latch if we're going to sleep during
* recovery.
*/
- if (StandbyMode)
+ if (StandbyModeRequested)
OwnLatch(&XLogCtl->recoveryWakeupLatch);
if (read_backup_label(&checkPointLoc, &backupEndRequired,
&backupFromStandby))
{
+ /*
+ * Archive recovery was requested, and thanks to the backup label file,
+ * we know how far we need to replay to reach consistency. Enter
+ * archive recovery directly.
+ */
+ InArchiveRecovery = true;
+ if (StandbyModeRequested)
+ StandbyMode = true;
+
/*
* When a backup_label file is present, we want to roll forward from
* the checkpoint it identifies, rather than using pg_control.
}
else
{
+ /*
+ * It's possible that archive recovery was requested, but we don't
+ * know how far we need to replay the WAL before we reach consistency.
+ * This can happen for example if a base backup is taken from a running
+ * server using an atomic filesystem snapshot, without calling
+ * pg_start/stop_backup. Or if you just kill a running master server
+ * and put it into archive recovery by creating a recovery.conf file.
+ *
+ * Our strategy in that case is to perform crash recovery first,
+ * replaying all the WAL present in pg_xlog, and only enter archive
+ * recovery after that.
+ *
+ * But usually we already know how far we need to replay the WAL (up to
+ * minRecoveryPoint, up to backupEndPoint, or until we see an
+ * end-of-backup record), and we can enter archive recovery directly.
+ */
+ if (ArchiveRecoveryRequested &&
+ (!XLByteEQ(ControlFile->minRecoveryPoint, InvalidXLogRecPtr) ||
+ ControlFile->backupEndRequired ||
+ !XLByteEQ(ControlFile->backupEndPoint, InvalidXLogRecPtr) ||
+ ControlFile->state == DB_SHUTDOWNED))
+ {
+ InArchiveRecovery = true;
+ if (StandbyModeRequested)
+ StandbyMode = true;
+ }
+
/*
* Get the last valid checkpoint record. If the latest one according
* to pg_control is broken, try the next-to-last one.
}
else if (ControlFile->state != DB_SHUTDOWNED)
InRecovery = true;
- else if (InArchiveRecovery)
+ else if (ArchiveRecoveryRequested)
{
/* force recovery due to presence of recovery.conf */
InRecovery = true;
ControlFile->prevCheckPoint = ControlFile->checkPoint;
ControlFile->checkPoint = checkPointLoc;
ControlFile->checkPointCopy = checkPoint;
- if (InArchiveRecovery)
- {
- /* initialize minRecoveryPoint if not set yet */
- if (XLByteLT(ControlFile->minRecoveryPoint, checkPoint.redo))
- ControlFile->minRecoveryPoint = checkPoint.redo;
- }
/*
* Set backupStartPoint if we're starting recovery from a base backup.
* control file and we've established a recovery snapshot from a
* running-xacts WAL record.
*/
- if (InArchiveRecovery && EnableHotStandby)
+ if (ArchiveRecoveryRequested && EnableHotStandby)
{
TransactionId *xids;
int nxids;
* process in addition to postmaster! Also, fsync requests are
* subsequently to be handled by the checkpointer, not locally.
*/
- if (InArchiveRecovery && IsUnderPostmaster)
+ if (ArchiveRecoveryRequested && IsUnderPostmaster)
{
PublishStartupProcessInformation();
SetForwardFsyncRequests();
* We don't need the latch anymore. It's not strictly necessary to disown
* it, but let's do it for the sake of tidiness.
*/
- if (StandbyMode)
+ if (StandbyModeRequested)
DisownLatch(&XLogCtl->recoveryWakeupLatch);
/*
* crashes while an online backup is in progress. We must not treat
* that as an error, or the database will refuse to start up.
*/
- if (InArchiveRecovery || ControlFile->backupEndRequired)
+ if (ArchiveRecoveryRequested || ControlFile->backupEndRequired)
{
if (ControlFile->backupEndRequired)
ereport(FATAL,
*
* In a normal crash recovery, we can just extend the timeline we were in.
*/
- if (InArchiveRecovery)
+ if (ArchiveRecoveryRequested)
{
+ Assert(InArchiveRecovery);
+
ThisTimeLineID = findNewestTimeLine(recoveryTargetTLI) + 1;
ereport(LOG,
(errmsg("selected new timeline ID: %u", ThisTimeLineID)));
* that we also have a copy of the last block of the old WAL in readBuf;
* we will use that below.)
*/
- if (InArchiveRecovery)
+ if (ArchiveRecoveryRequested)
exitArchiveRecovery(curFileTLI, endLogId, endLogSeg);
/*
* record, the backup was canceled and the end-of-backup record will
* never arrive.
*/
- if (InArchiveRecovery &&
+ if (ArchiveRecoveryRequested &&
!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) &&
XLogRecPtrIsInvalid(ControlFile->backupEndPoint))
ereport(PANIC,
* Request a restartpoint if we've replayed too much xlog since the
* last one.
*/
- if (StandbyMode && bgwriterLaunched)
+ if (StandbyModeRequested && bgwriterLaunched)
{
if (XLogCheckpointNeeded(readId, readSeg))
{