StartupXLOG:
do{
bool switchedTLI = false;
...
/*
在回放记录前,检查该wal记录是否会引起时间线的改变。
该记录被视为新时间线的一部分,因此需要在回放前更新时间线。
*/
if (record->xl_rmid == RM_XLOG_ID){//该记录是检查点信息
TimeLineID newTLI = ThisTimeLineID;
TimeLineID prevTLI = ThisTimeLineID;
uint8 info = record->xl_info & ~XLR_INFO_MASK;
if (info == XLOG_CHECKPOINT_SHUTDOWN){
CheckPoint checkPoint;
memcpy(&checkPoint, XLogRecGetData(xlogreader), sizeof(CheckPoint));
newTLI = checkPoint.ThisTimeLineID;
prevTLI = checkPoint.PrevTimeLineID;
}else if (info == XLOG_END_OF_RECOVERY){
xl_end_of_recovery xlrec;
memcpy(&xlrec, XLogRecGetData(xlogreader), sizeof(xl_end_of_recovery));
newTLI = xlrec.ThisTimeLineID;
prevTLI = xlrec.PrevTimeLineID;
}
if (newTLI != ThisTimeLineID){//需要切换时间线
/* Check that it's OK to switch to this TLI */
checkTimeLineSwitch(EndRecPtr, newTLI, prevTLI);
/* Following WAL records should be run with new TLI */
ThisTimeLineID = newTLI;
switchedTLI = true;
}
}
/*
* Update shared replayEndRecPtr before replaying this record,
* so that XLogFlush will update minRecoveryPoint correctly.
*/
SpinLockAcquire(&XLogCtl->info_lck);
XLogCtl->replayEndRecPtr = EndRecPtr;//上一次读取的WAL记录末尾
XLogCtl->replayEndTLI = ThisTimeLineID;
SpinLockRelease(&XLogCtl->info_lck);
...
//回放
RmgrTable[record->xl_rmid].rm_redo(xlogreader);
...
if ((record->xl_info & XLR_CHECK_CONSISTENCY) != 0)
checkXLogConsistency(xlogreader);//检查是否到达一致性位置
/*
* Update lastReplayedEndRecPtr after this record has been successfully replayed.
*/
SpinLockAcquire(&XLogCtl->info_lck);
XLogCtl->lastReplayedEndRecPtr = EndRecPtr;
XLogCtl->lastReplayedTLI = ThisTimeLineID;
SpinLockRelease(&XLogCtl->info_lck);
...
/* Remember this record as the last-applied one */
LastRec = ReadRecPtr;
/* 如果到达一致性位置,允许只读连接 */
CheckRecoveryConsistency();
/* Is this a timeline switch? */
if (switchedTLI){
/*
* Before we continue on the new timeline, clean up any
* (possibly bogus) future WAL segments on the old
* timeline.
*/
RemoveNonParentXlogFiles(EndRecPtr, ThisTimeLineID);
...
}
/* Else, try to fetch the next WAL record */
record = ReadRecord(xlogreader, InvalidXLogRecPtr, LOG, false);
}
/*
* WAL文件不在时间线历史里面,删除这样的文件。
* 恢复过程中,回放wal到下一个时间线时以及恢复结束创建一个新时间线时,调用该函数
* We wouldn't otherwise care about extra WAL files lying in pg_wal, but they
* might be leftover pre-allocated or recycled WAL segments on the old timeline
* that we haven't used yet, and contain garbage. If we just leave them in
* pg_wal, they will eventually be archived, and we can't let that happen.
* Files that belong to our timeline history are valid, because we have
* successfully replayed them, but from others we can't be sure.
*
* 'switchpoint' is the current point in WAL where we switch to new timeline,
* and 'newTLI' is the new timeline we switch to.
*/
static void
RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
{
XLByteToPrevSeg(switchpoint, endLogSegNo);
xldir = AllocateDir(XLOGDIR);
if (xldir == NULL)
ereport(ERROR,(errcode_for_file_access(), errmsg("could not open write-ahead log directory \"%s\": %m",XLOGDIR)));
/*
* Construct a filename of the last segment to be kept.
*/
XLogFileName(switchseg, newTLI, endLogSegNo);
elog(DEBUG2, "attempting to remove WAL segments newer than log file %s",
switchseg);
while ((xlde = ReadDir(xldir, XLOGDIR)) != NULL){
/* Ignore files that are not XLOG segments */
if (!IsXLogFileName(xlde->d_name))
continue;
/*
* Remove files that are on a timeline older than the new one we're
* switching to, but with a segment number >= the first segment on the
* new timeline.
*/
if (strncmp(xlde->d_name, switchseg, 8) < 0 && strcmp(xlde->d_name + 8, switchseg + 8) > 0){
/*
* If the file has already been marked as .ready, however, don't
* remove it yet. It should be OK to remove it - files that are
* not part of our timeline history are not required for recovery
* - but seems safer to let them be archived and removed later.
*/
if (!XLogArchiveIsReady(xlde->d_name))
RemoveXlogFile(xlde->d_name, InvalidXLogRecPtr, switchpoint);
}
}
FreeDir(xldir);
}
本文暂时没有评论,来添加一个吧(●'◡'●)