* here. It's used by the background writer when it wants to create
* a restartpoint.
*
- * is info_lck spinlock a bit too light-weight to protect this?
+ * is info_lck spinlock a bit too light-weight to protect these?
*/
XLogRecPtr lastCheckPointRecPtr;
CheckPoint lastCheckPoint;
+ /* end+1 of the last record replayed (or being replayed) */
+ XLogRecPtr replayEndRecPtr;
+
slock_t info_lck; /* locks shared variables shown above */
} XLogCtlData;
/* State information for XLOG reading */
static XLogRecPtr ReadRecPtr; /* start of last record read */
-static XLogRecPtr EndRecPtr; /* end+1 of last record read */
+static XLogRecPtr EndRecPtr; /* end+1 of last record read. Also in shared mem */
static XLogRecord *nextRecord = NULL;
static TimeLineID lastPageTLI = 0;
+static XLogRecPtr minRecoveryPoint; /* local copy of ControlFile->minRecoveryPoint */
+static bool updateMinRecoveryPoint = true;
static bool InRedo = false;
static void RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr);
static void ValidateXLOGDirectoryStructure(void);
static void CleanupBackupHistory(void);
+static void UpdateMinRecoveryPoint(XLogRecPtr lsn);
static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, int emode);
static bool ValidXLOGHeader(XLogPageHeader hdr, int emode);
static XLogRecord *ReadCheckpointRecord(XLogRecPtr RecPtr, int whichChkpt);
SpinLockRelease(&xlogctl->info_lck);
}
+static void
+UpdateMinRecoveryPoint(XLogRecPtr lsn)
+{
+ /* Quick check using our local copy of the variable */
+ 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 */
+ minRecoveryPoint = ControlFile->minRecoveryPoint;
+
+ /*
+ * An invalid minRecoveryPoint means that we need to recover all the WAL,
+ * ie. crash recovery. Don't update the control file in that case.
+ */
+ if (minRecoveryPoint.xlogid == 0 && minRecoveryPoint.xrecoff == 0)
+ updateMinRecoveryPoint = false;
+ else if (XLByteLT(minRecoveryPoint, lsn))
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ 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'
+ * would suffice for correctness.
+ */
+ SpinLockAcquire(&xlogctl->info_lck);
+ minRecoveryPoint = xlogctl->replayEndRecPtr;
+ SpinLockRelease(&xlogctl->info_lck);
+
+ ControlFile->minRecoveryPoint = minRecoveryPoint;
+ UpdateControlFile();
+ }
+ LWLockRelease(ControlFileLock);
+
+ elog(LOG, "updated min recovery point to %X/%X",
+ minRecoveryPoint.xlogid, minRecoveryPoint.xrecoff);
+}
+
/*
* Ensure that all XLOG data through the given position is flushed to disk.
*
XLogRecPtr WriteRqstPtr;
XLogwrtRqst WriteRqst;
- /* Disabled during REDO */
+ /* During REDO, we don't try to flush the WAL, but update minRecoveryPoint instead */
if (IsRecoveryProcessingMode())
+ {
+ UpdateMinRecoveryPoint(record);
return;
+ }
/* Quick exit if already known flushed */
if (XLByteLE(record, LogwrtResult.Flush))
snprintf(activitymsg, sizeof(activitymsg), "recovering %s",
xlogfname);
set_ps_display(activitymsg, false);
-
- /*
- * 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.
- */
- if (InArchiveRecovery)
- {
- XLogRecPtr nextSegRecPtr;
- uint32 nextLog = log;
- uint32 nextSeg = seg;
-
- NextLogSeg(nextLog, nextSeg);
- nextSegRecPtr.xlogid = nextLog;
- nextSegRecPtr.xrecoff = nextSeg * XLogSegSize;
-
- LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
- if (XLByteLT(ControlFile->minSafeStartPoint, nextSegRecPtr))
- {
- ControlFile->minSafeStartPoint = nextSegRecPtr;
- UpdateControlFile();
- }
- LWLockRelease(ControlFileLock);
- }
-
return fd;
}
if (errno != ENOENT) /* unexpected failure? */
*/
if (shutdown_requested && InRedo)
{
- /* XXX: We should update minSafeStartPoint to the exact value here */
+ /* XXX: We should update minRecoveryPoint to the exact value here */
+ UpdateMinRecoveryPoint(EndRecPtr);
proc_exit(0);
}
CheckPoint checkPoint;
bool wasShutdown;
bool reachedStopPoint = false;
- bool reachedSafeStartPoint = false;
+ bool reachedMinRecoveryPoint = false;
bool performedRecovery = false;
bool haveBackupLabel = false;
XLogRecPtr RecPtr,
LastRec,
checkPointLoc,
- minRecoveryLoc,
+ backupStopLoc,
EndOfLog;
uint32 endLogId;
uint32 endLogSeg;
recoveryTargetTLI,
ControlFile->checkPointCopy.ThisTimeLineID)));
- if (read_backup_label(&checkPointLoc, &minRecoveryLoc))
+ if (read_backup_label(&checkPointLoc, &backupStopLoc))
{
/*
* When a backup_label file is present, we want to roll forward from
ControlFile->prevCheckPoint = ControlFile->checkPoint;
ControlFile->checkPoint = checkPointLoc;
ControlFile->checkPointCopy = checkPoint;
- if (minRecoveryLoc.xlogid != 0 || minRecoveryLoc.xrecoff != 0)
- ControlFile->minRecoveryPoint = minRecoveryLoc;
+ if (backupStopLoc.xlogid != 0 || backupStopLoc.xrecoff != 0)
+ {
+ if (XLByteLT(ControlFile->minRecoveryPoint, backupStopLoc))
+ ControlFile->minRecoveryPoint = backupStopLoc;
+ }
ControlFile->time = (pg_time_t) time(NULL);
/* No need to hold ControlFileLock yet, we aren't up far enough */
UpdateControlFile();
+ /* update our local copy of minRecoveryPoint */
+ minRecoveryPoint = ControlFile->minRecoveryPoint;
+
/*
* Reset pgstat data, because it may be invalid after recovery.
*/
bool recoveryContinue = true;
bool recoveryApply = true;
ErrorContextCallback errcontext;
- XLogRecPtr minSafeStartPoint;
+ /* 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);
+
/*
- * Take a local copy of minSafeStartPoint at the beginning of
- * recovery, because it's updated as we go.
+ * Let postmaster know we've started redo now.
+ *
+ * After this point, we can no longer assume that there's no other
+ * processes running concurrently.
*/
- minSafeStartPoint = ControlFile->minSafeStartPoint;
-
- /* Let postmaster know we've started redo now */
if (InArchiveRecovery && IsUnderPostmaster)
SendPostmasterSignal(PMSIGNAL_RECOVERY_STARTED);
/*
* We were requested to exit without finishing recovery.
*
- * XXX: We should update minSafeStartPoint to the exact
+ * XXX: We should update minRecoveryPoint to the exact
* value here.
*/
+ UpdateMinRecoveryPoint(EndRecPtr);
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.
* another point also: minSafeStartPoint, which is the
* latest LSN that recovery could have reached prior to crash.
*/
- if (!reachedSafeStartPoint &&
- XLByteLE(minSafeStartPoint, EndRecPtr) &&
- XLByteLE(ControlFile->minRecoveryPoint, EndRecPtr))
+ if (!reachedMinRecoveryPoint &&
+ XLByteLE(minRecoveryPoint, EndRecPtr))
{
- reachedSafeStartPoint = true;
+ reachedMinRecoveryPoint = true;
if (InArchiveRecovery)
{
ereport(LOG,
TransactionIdAdvance(ShmemVariableCache->nextXid);
}
+ /* Update shared copy of replayEndRecPtr */
+ SpinLockAcquire(&xlogctl->info_lck);
+ xlogctl->replayEndRecPtr = EndRecPtr;
+ SpinLockRelease(&xlogctl->info_lck);
+
RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record);
/* Pop the error context stack */
/* there are no WAL records following the checkpoint */
ereport(LOG,
(errmsg("redo is not required")));
- reachedSafeStartPoint = true;
+ reachedMinRecoveryPoint = true;
}
}
* Complain if we did not roll forward far enough to render the backup
* dump consistent.
*/
- if (InRecovery && !reachedSafeStartPoint)
+ if (InRecovery && !reachedMinRecoveryPoint)
{
if (reachedStopPoint) /* stopped because of stop request */
ereport(FATAL,