SpinLockRelease(&xlogctl->info_lck);
}
+/*
+ * Advance minRecoveryPoint in control file.
+ *
+ * If we crash during reocvery, we must reach this point again before
+ * the database is consistent. If minRecoveryPoint is already greater than
+ * or equal to 'lsn', it is not updated.
+ */
static void
UpdateMinRecoveryPoint(XLogRecPtr lsn)
{
if (!updateMinRecoveryPoint || XLByteLE(lsn, minRecoveryPoint))
return;
- /* XXX
- * Calculate and write out a new safeStartPoint. This defines
- * the latest LSN that might appear on-disk while we apply
- * the WAL records in this file. If we crash during recovery
- * we must reach this point again before we can prove
- * database consistency. Not a restartpoint! Restart points
- * define where we should start recovery from, if we crash.
- */
LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
/* update local copy */
volatile XLogCtlData *xlogctl = XLogCtl;
/*
- * We need to update the control file. To avoid having to update it
- * too often, we update it all the way to EndRecPtr, even though 'lsn'
+ * To avoid having to update the control file too often, we update
+ * it all the way to the last record being replayed, even though 'lsn'
* would suffice for correctness.
*/
SpinLockAcquire(&xlogctl->info_lck);
minRecoveryPoint = xlogctl->replayEndRecPtr;
SpinLockRelease(&xlogctl->info_lck);
+ /* update control file */
ControlFile->minRecoveryPoint = minRecoveryPoint;
UpdateControlFile();
+
+ elog(DEBUG2, "updated min recovery point to %X/%X",
+ minRecoveryPoint.xlogid, minRecoveryPoint.xrecoff);
}
LWLockRelease(ControlFileLock);
-
- elog(LOG, "updated min recovery point to %X/%X",
- minRecoveryPoint.xlogid, minRecoveryPoint.xrecoff);
}
/*
unlink(recoveryPath); /* ignore any error */
/*
- * Rename the config file out of the way, so that we don't accidentally
- * re-enter archive recovery mode in a subsequent crash. We have already
- * restored all the WAL segments we need from the archive, and we trust
- * that they are not going to go away even if we crash. (XXX: should
- * we fsync() them all to ensure that?)
+ * 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.
*/
- 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")));
/* use volatile pointer to prevent code rearrangement */
volatile XLogCtlData *xlogctl = XLogCtl;
- InRedo = true;
- ereport(LOG,
- (errmsg("redo starts at %X/%X",
- ReadRecPtr.xlogid, ReadRecPtr.xrecoff)));
-
/* Update shared copy of replayEndRecPtr */
SpinLockAcquire(&xlogctl->info_lck);
xlogctl->replayEndRecPtr = ReadRecPtr;
SpinLockRelease(&xlogctl->info_lck);
+ InRedo = true;
+
+ if (minRecoveryPoint.xlogid == 0 && minRecoveryPoint.xrecoff == 0)
+ ereport(LOG,
+ (errmsg("redo starts at %X/%X",
+ ReadRecPtr.xlogid, ReadRecPtr.xrecoff)));
+ else
+ ereport(LOG,
+ (errmsg("redo starts at %X/%X, consistency will be reached at %X/%X",
+ ReadRecPtr.xlogid, ReadRecPtr.xrecoff,
+ minRecoveryPoint.xlogid, minRecoveryPoint.xrecoff)));
+
/*
* Let postmaster know we've started redo now.
*
if (InArchiveRecovery)
{
ereport(LOG,
- (errmsg("consistent recovery state reached at %X/%X",
- EndRecPtr.xlogid, EndRecPtr.xrecoff)));
+ (errmsg("consistent recovery state reached")));
if (IsUnderPostmaster)
SendPostmasterSignal(PMSIGNAL_RECOVERY_CONSISTENT);
}
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.
*
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 */
* Truncate pg_subtrans if possible. We can throw away all data before
* the oldest XMIN of any running transaction. No future transaction will
* attempt to reference any pg_subtrans entry older than that (see Asserts
- * in subtrans.c). During recovery, though, we mustn't do this because
- * StartupSUBTRANS hasn't been called yet.
+ * in subtrans.c).
*/
TruncateSUBTRANS(GetOldestXmin(true, false));