*/
static int LocalXLogInsertAllowed = -1;
-/* Are we recovering using offline XLOG archives? (only valid in the startup process) */
+/*
+ * 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;
bool InArchiveRecovery = false;
/* Was the last xlog file restored from archive, or local? */
static char *recoveryTargetName;
/* options taken from recovery.conf for XLOG streaming */
-bool StandbyMode = false;
+static bool StandbyModeRequested = false;
static char *PrimaryConnInfo = NULL;
static char *TriggerFile = NULL;
+/* are we currently in standby mode? */
+bool StandbyMode = false;
+
/* whether request for fast promotion has been made yet */
static bool fast_promote = false;
private->emode = emode;
private->randAccess = (RecPtr != InvalidXLogRecPtr);
- /* This is the first try to read this page. */
+ /* This is the first attempt to read this page. */
lastSourceFailed = false;
- do
+ for (;;)
{
char *errormsg;
EndRecPtr = xlogreader->EndRecPtr;
if (record == NULL)
{
- lastSourceFailed = true;
-
if (readFile >= 0)
{
close(readFile);
ereport(emode_for_corrupt_record(emode,
RecPtr ? RecPtr : EndRecPtr),
(errmsg_internal("%s", errormsg) /* already translated */));
-
- /* Give up, or retry if we're in standby mode. */
- continue;
}
-
/*
* Check page TLI is one of the expected values.
*/
- if (!tliInHistory(xlogreader->latestPageTLI, expectedTLEs))
+ else if (!tliInHistory(xlogreader->latestPageTLI, expectedTLEs))
{
char fname[MAXFNAMELEN];
XLogSegNo segno;
int32 offset;
- lastSourceFailed = true;
-
XLByteToSeg(xlogreader->latestPagePtr, segno);
offset = xlogreader->latestPagePtr % XLogSegSize;
XLogFileName(fname, xlogreader->readPageTLI, segno);
fname,
offset)));
record = NULL;
- continue;
}
- } while (StandbyMode && record == NULL && !CheckForStandbyTrigger());
- return record;
+ if (record)
+ {
+ /* Great, got a record */
+ return record;
+ }
+ else
+ {
+ /* No valid record available from this source */
+ lastSourceFailed = true;
+
+ /*
+ * 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 (ControlFile->minRecoveryPoint < EndRecPtr)
+ {
+ ControlFile->minRecoveryPoint = EndRecPtr;
+ ControlFile->minRecoveryPointTLI = ThisTimeLineID;
+ }
+ /* update local copy */
+ minRecoveryPoint = ControlFile->minRecoveryPoint;
+ minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
+
+ UpdateControlFile();
+ LWLockRelease(ControlFileLock);
+
+ CheckRecoveryConsistency();
+
+ /*
+ * Before we retry, reset lastSourceFailed and currentSource
+ * so that we will check the archive next.
+ */
+ lastSourceFailed = false;
+ currentSource = 0;
+
+ continue;
+ }
+
+ /* In standby mode, loop back to retry. Otherwise, give up. */
+ if (StandbyMode && !CheckForStandbyTrigger())
+ continue;
+ else
+ return NULL;
+ }
+ }
}
/*
}
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);
/* Set up XLOG reader facility */
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 &&
+ (ControlFile->minRecoveryPoint != InvalidXLogRecPtr ||
+ ControlFile->backupEndRequired ||
+ 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 (ControlFile->minRecoveryPoint < checkPoint.redo)
- {
- ControlFile->minRecoveryPoint = checkPoint.redo;
- ControlFile->minRecoveryPointTLI = checkPoint.ThisTimeLineID;
- }
- }
/*
* 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.
*/
PrevTimeLineID = ThisTimeLineID;
- if (InArchiveRecovery)
+ if (ArchiveRecoveryRequested)
{
char reason[200];
+ 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(xlogreader->readPageTLI, endLogSegNo);
/*
* 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(readSegNo))
{
(readSource == XLOG_FROM_STREAM &&
receivedUpto < targetPagePtr + reqLen))
{
- if (StandbyMode)
- {
- if (!WaitForWALToBecomeAvailable(targetPagePtr + reqLen,
- private->randAccess,
- private->fetching_ckpt,
- targetRecPtr))
- goto triggered;
- }
- /* In archive or crash recovery. */
- else if (readFile < 0)
+ if (!WaitForWALToBecomeAvailable(targetPagePtr + reqLen,
+ private->randAccess,
+ private->fetching_ckpt,
+ targetRecPtr))
{
- int source;
-
- if (InArchiveRecovery)
- source = XLOG_FROM_ANY;
- else
- source = XLOG_FROM_PG_XLOG;
+ if (readFile >= 0)
+ close(readFile);
+ readFile = -1;
+ readLen = 0;
+ readSource = 0;
- readFile = XLogFileReadAnyTLI(readSegNo, emode, source);
- if (readFile < 0)
- return -1;
+ return -1;
}
}
goto retry;
else
return -1;
-
-triggered:
- if (readFile >= 0)
- close(readFile);
- readFile = -1;
- readLen = 0;
- readSource = 0;
-
- return -1;
}
/*
- * In standby mode, wait for WAL at position 'RecPtr' to become available, either
- * via restore_command succeeding to restore the segment, or via walreceiver
- * having streamed the record (or via someone copying the segment directly to
- * pg_xlog, but that is not documented or recommended).
+ * Open the WAL segment containing WAL position 'RecPtr'.
+ *
+ * The segment can be fetched via restore_command, or via walreceiver having
+ * streamed the record, or it can already be present in pg_xlog. Checking
+ * pg_xlog is mainly for crash recovery, but it will be polled in standby mode
+ * too, in case someone copies a new segment directly to pg_xlog. That is not
+ * documented or recommended, though.
*
* If 'fetching_ckpt' is true, we're fetching a checkpoint record, and should
* prepare to read WAL starting from RedoStartLSN after this.
* 'tliRecPtr' is the position of the WAL record we're interested in. It is
* used to decide which timeline to stream the requested WAL from.
*
+ * If the the record is not immediately available, the function returns false
+ * if we're not in standby mode. In standby mode, waits for it to become
+ * available.
+ *
* When the requested record becomes available, the function opens the file
* containing it (if not open already), and returns true. When end of standby
* mode is triggered by the user, and there is no more WAL available, returns
* part of advancing to the next state.
*-------
*/
- if (currentSource == 0)
+ if (!InArchiveRecovery)
+ currentSource = XLOG_FROM_PG_XLOG;
+ else if (currentSource == 0)
currentSource = XLOG_FROM_ARCHIVE;
for (;;)
*/
if (lastSourceFailed)
{
-
switch (currentSource)
{
case XLOG_FROM_ARCHIVE:
* file, we still finish replaying as much as we can from
* archive and pg_xlog before failover.
*/
- if (CheckForStandbyTrigger())
+ if (StandbyMode && CheckForStandbyTrigger())
{
ShutdownWalRcv();
return false;
}
+ /*
+ * Not in standby mode, and we've now tried the archive and
+ * pg_xlog.
+ */
+ if (!StandbyMode)
+ return false;
+
/*
* If primary_conninfo is set, launch walreceiver to try to
* stream the missing WAL.
* in the archive over ones in pg_xlog, so try the next file
* again from the archive first.
*/
- currentSource = XLOG_FROM_ARCHIVE;
+ if (InArchiveRecovery)
+ currentSource = XLOG_FROM_ARCHIVE;
}
if (currentSource != oldSource)
* process.
*/
HandleStartupProcInterrupts();
- }
+ } while (StandbyMode);
- return false; /* not reached */
+ return false;
}
/*