static XLogRecord *nextRecord = NULL;
static TimeLineID lastPageTLI = 0;
static XLogRecPtr minRecoveryPoint; /* local copy of ControlFile->minRecoveryPoint */
-static bool updateMinRecoveryPoint = true;
+static bool updateMinRecoveryPoint = true;
static bool InRedo = false;
static void readRecoveryCommandFile(void);
static void exitArchiveRecovery(TimeLineID endTLI,
uint32 endLogId, uint32 endLogSeg);
-static void exitRecovery(void);
static bool recoveryStopsHere(XLogRecord *record, bool *includeThis);
static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
bool updrqst;
bool doPageWrites;
bool isLogSwitch = (rmid == RM_XLOG_ID && info == XLOG_SWITCH);
- bool isRecoveryEnd = (rmid == RM_XLOG_ID && info == XLOG_RECOVERY_END);
/* cross-check on whether we should be here or not */
- if (IsRecoveryProcessingMode() && !isRecoveryEnd)
+ if (IsRecoveryProcessingMode())
elog(FATAL, "cannot make new WAL entries during recovery");
/* info's high bits are reserved for use by me */
XLogRecPtr WriteRqstPtr;
XLogwrtRqst WriteRqst;
- /* During REDO, we don't try to flush the WAL, but update minRecoveryPoint instead */
+ /*
+ * During REDO, we don't try to flush the WAL, but update minRecoveryPoint
+ * instead.
+ */
if (IsRecoveryProcessingMode())
{
UpdateMinRecoveryPoint(record);
* and so we will not force a restart for a bad LSN on a data page.
*/
if (XLByteLT(LogwrtResult.Flush, record))
- elog(ERROR,
+ elog(InRecovery ? WARNING : ERROR,
"xlog flush request %X/%X is not satisfied --- flushed only to %X/%X",
record.xlogid, record.xrecoff,
LogwrtResult.Flush.xlogid, LogwrtResult.Flush.xrecoff);
snprintf(activitymsg, sizeof(activitymsg), "recovering %s",
xlogfname);
set_ps_display(activitymsg, false);
+
return fd;
}
if (errno != ENOENT) /* unexpected failure? */
*/
if (shutdown_requested && InRedo)
{
- /* XXX: We should update minRecoveryPoint to the exact value here */
+ /* XXX: Is EndRecPtr always the right value? */
UpdateMinRecoveryPoint(EndRecPtr);
proc_exit(0);
}
unlink(recoveryPath); /* ignore any error */
/*
- * As of 8.4 we no longer rename the recovery.conf file out of the
- * way until after we have performed a full checkpoint. This ensures
- * that any crash between now and the end of the checkpoint does not
- * attempt to restart from a WAL file that is no longer available to us.
- * As soon as we remove recovery.conf we lose our recovery_command and
- * cannot reaccess WAL files from the archive.
+ * Rename the config file out of the way, so that we don't accidentally
+ * re-enter archive recovery mode in a subsequent crash.
*/
+ unlink(RECOVERY_COMMAND_DONE);
+ if (rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE) != 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not rename file \"%s\" to \"%s\": %m",
+ RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE)));
ereport(LOG,
(errmsg("archive recovery complete")));
bool wasShutdown;
bool reachedStopPoint = false;
bool reachedMinRecoveryPoint = false;
- bool performedRecovery = false;
bool haveBackupLabel = false;
XLogRecPtr RecPtr,
LastRec,
{
/*
* We were requested to exit without finishing recovery.
- *
- * XXX: We should update minRecoveryPoint to the exact
- * value here.
*/
- UpdateMinRecoveryPoint(EndRecPtr);
+ UpdateMinRecoveryPoint(ReadRecPtr);
proc_exit(0);
}
/*
* Have we reached our safe starting point? If so, we can
- * signal postmaster to enter consistent recovery mode.
- * XXX
- * There are two points in the log we must pass. The first is
- * the minRecoveryPoint, which is the LSN at the time the
- * base backup was taken that we are about to rollfoward from.
- * If recovery has ever crashed or was stopped there is
- * another point also: minSafeStartPoint, which is the
- * latest LSN that recovery could have reached prior to crash.
+ * tell postmaster that the database is consistent now.
*/
if (!reachedMinRecoveryPoint &&
XLByteLE(minRecoveryPoint, EndRecPtr))
* Complain if we did not roll forward far enough to render the backup
* dump consistent.
*/
- if (InRecovery && !reachedMinRecoveryPoint)
+ if (InRecovery && XLByteLT(EndOfLog, minRecoveryPoint))
{
if (reachedStopPoint) /* stopped because of stop request */
ereport(FATAL,
/* Pre-scan prepared transactions to find out the range of XIDs present */
oldestActiveXID = PrescanPreparedTransactions();
+ /*
+ * Allow writing WAL for us. But not for other backends! That's done
+ * after writing the shutdown checkpoint and finishing recovery.
+ */
+ LocalRecoveryProcessingMode = false;
+
if (InRecovery)
{
int rmid;
XLogCheckInvalidPages();
/*
- * Finally exit recovery and mark that in WAL. Pre-8.4 we wrote
- * a shutdown checkpoint here, but we ask bgwriter to do that now.
+ * Perform a checkpoint to update all our recovery activity to disk.
+ *
+ * Note that we write a shutdown checkpoint rather than an on-line
+ * one. This is not particularly critical, but since we may be
+ * assigning a new TLI, using a shutdown checkpoint allows us to have
+ * the rule that TLI only changes in shutdown checkpoints, which
+ * allows some extra error checking in xlog_redo.
*/
- exitRecovery();
-
- performedRecovery = true;
+ CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
}
+ /*
+ * Preallocate additional log files, if wanted.
+ */
+ PreallocXlogFiles(EndOfLog);
+
+ InRecovery = false;
+
+ LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
+ ControlFile->state = DB_IN_PRODUCTION;
+ ControlFile->time = (pg_time_t) time(NULL);
+ UpdateControlFile();
+ LWLockRelease(ControlFileLock);
+
/* start the archive_timeout timer running */
XLogCtl->Write.lastSegSwitchTime = (pg_time_t) time(NULL);
}
/*
- * If we had to replay any WAL records, request a checkpoint. This isn't
- * strictly necessary: if we crash now, the recovery will simply restart
- * from the same point as this time (or from the last restartpoint). The
- * control file is left in DB_IN_*_RECOVERY state; the first checkpoint
- * will change that to DB_IN_PRODUCTION.
+ * All done. Allow others to write WAL.
*/
- if (performedRecovery)
- {
- /*
- * Okay, we can come up now. Allow others to write WAL.
- */
- XLogCtl->SharedRecoveryProcessingMode = false;
-
- RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_IMMEDIATE |
- CHECKPOINT_STARTUP);
- }
- else
- {
- /*
- * No recovery, so let's just get on with it.
- */
- LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
- ControlFile->state = DB_IN_PRODUCTION;
- ControlFile->time = (pg_time_t) time(NULL);
- UpdateControlFile();
- LWLockRelease(ControlFileLock);
-
- /*
- * Okay, we're officially UP.
- */
- XLogCtl->SharedRecoveryProcessingMode = false;
- }
+ XLogCtl->SharedRecoveryProcessingMode = false;
}
/*
elog(LOG, msg,
(flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "",
- (flags & CHECKPOINT_STARTUP) ? " startup" : "",
(flags & CHECKPOINT_IMMEDIATE) ? " immediate" : "",
(flags & CHECKPOINT_FORCE) ? " force" : "",
(flags & CHECKPOINT_WAIT) ? " wait" : "",
uint32 _logSeg;
TransactionId *inCommitXids;
int nInCommit;
- bool leavingArchiveRecovery;
/* shouldn't happen */
if (IsRecoveryProcessingMode())
*/
LWLockAcquire(CheckpointLock, LW_EXCLUSIVE);
- /*
- * Find out if this is the first checkpoint after archive recovery.
- */
- LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
- leavingArchiveRecovery = (ControlFile->state == DB_IN_ARCHIVE_RECOVERY);
- LWLockRelease(ControlFileLock);
-
/*
* Prepare to accumulate statistics.
*
* if this is the first checkpoint after recovery.
*/
LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
- if (shutdown)
- ControlFile->state = DB_SHUTDOWNED;
- else
- ControlFile->state = DB_IN_PRODUCTION;
ControlFile->prevCheckPoint = ControlFile->checkPoint;
ControlFile->checkPoint = ProcLastRecPtr;
ControlFile->checkPointCopy = checkPoint;
UpdateControlFile();
LWLockRelease(ControlFileLock);
- if (leavingArchiveRecovery)
- {
- /*
- * Rename the config file out of the way, so that we don't accidentally
- * re-enter archive recovery mode in a subsequent crash. Prior to
- * 8.4 this step was performed at end of exitArchiveRecovery().
- */
- unlink(RECOVERY_COMMAND_DONE);
- if (rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE) != 0)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not rename file \"%s\" to \"%s\": %m",
- RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE)));
- }
-
/* Update shared-memory copy of checkpoint XID/epoch */
{
/* use volatile pointer to prevent code rearrangement */
return RecPtr;
}
-/*
- * exitRecovery()
- *
- * Exit recovery state and write a XLOG_RECOVERY_END record. This is the
- * only record type that can record a change of timelineID. We assume
- * caller has already set ThisTimeLineID, if appropriate.
- */
-static void
-exitRecovery(void)
-{
- XLogRecData rdata;
-
- rdata.buffer = InvalidBuffer;
- rdata.data = (char *) (&ThisTimeLineID);
- rdata.len = sizeof(TimeLineID);
- rdata.next = NULL;
-
- /*
- * This is the only type of WAL message that can be inserted during
- * recovery. This ensures that we don't allow others to get access
- * until after we have changed state.
- */
- (void) XLogInsert(RM_XLOG_ID, XLOG_RECOVERY_END, &rdata);
-
- /*
- * We don't XLogFlush() here otherwise we'll end up zeroing the WAL
- * file ourselves. So just let bgwriter's forthcoming checkpoint do
- * that for us.
- */
-
- InRecovery = false;
-}
-
/*
* XLOG resource manager's routines
*
RecoveryRestartPoint(&checkPoint);
}
- else if (info == XLOG_RECOVERY_END)
- {
- TimeLineID tli;
-
- memcpy(&tli, XLogRecGetData(record), sizeof(TimeLineID));
-
- /*
- * TLI may change when recovery ends, but it shouldn't decrease.
- *
- * This is the only WAL record that can tell us to change timelineID
- * while we process WAL records.
- *
- * We can *choose* to stop recovery at any point, generating a
- * new timelineID which is recorded using this record type.
- */
- if (tli != ThisTimeLineID)
- {
- if (tli < ThisTimeLineID ||
- !list_member_int(expectedTLIs,
- (int) tli))
- ereport(PANIC,
- (errmsg("unexpected timeline ID %u (after %u) at recovery end record",
- tli, ThisTimeLineID)));
- /* Following WAL records should be run with new TLI */
- ThisTimeLineID = tli;
- }
- }
else if (info == XLOG_CHECKPOINT_ONLINE)
{
CheckPoint checkPoint;