XLogRecPtr XLogInsertRecord(XLogRecData *rdata, XLogRecPtr fpw_lsn, uint8 flags) {
XLogCtlInsert *Insert = &XLogCtl->Insert;
pg_crc32c rdata_crc; bool inserted;
XLogRecord *rechdr = (XLogRecord *) rdata->data;
uint8 info = rechdr->xl_info & ~XLR_INFO_MASK;
bool isLogSwitch = (rechdr->xl_rmid == RM_XLOG_ID && info == XLOG_SWITCH);
XLogRecPtr StartPos; XLogRecPtr EndPos; bool prevDoPageWrites = doPageWrites;
/* cross-check on whether we should be here or not */
if (!XLogInsertAllowed()) elog(ERROR, "cannot make new WAL entries during recovery");
// 如果进行WAL日志段的切换,则此时其他进程就不能预订空间
START_CRIT_SECTION();
if (isLogSwitch) WALInsertLockAcquireExclusive();
else WALInsertLockAcquire();
if (RedoRecPtr != Insert->RedoRecPtr) {
RedoRecPtr = Insert->RedoRecPtr;
}
doPageWrites = (Insert->fullPageWrites || Insert->forcePageWrites);
if (doPageWrites &&(!prevDoPageWrites || (fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr))) {
/* Oops, some buffer now needs to be backed up that the caller didn't back up. Start over. */
WALInsertLockRelease();
END_CRIT_SECTION();
return InvalidXLogRecPtr;
}
// 预订空间
/* Reserve space for the record in the WAL. This also sets the xl_prev pointer. */
if (isLogSwitch) // 如果是日志切换记录,恰好需要做日志切换,则可能StartPos和EndPos相同,也就是说不需要记录这个WAL日志记录
inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev);
else {
ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos, &rechdr->xl_prev);
inserted = true;
}
if (inserted) {
/* Now that xl_prev has been filled in, calculate CRC of the record header. 将WAL日志记录复制到WAL Buffer */
rdata_crc = rechdr->xl_crc;
COMP_CRC32C(rdata_crc, rechdr, offsetof(XLogRecord, xl_crc));
FIN_CRC32C(rdata_crc);
rechdr->xl_crc = rdata_crc;
/* All the record data, including the header, is now ready to be inserted. Copy the record in the space reserved. */
CopyXLogRecordToWAL(rechdr->xl_tot_len, isLogSwitch, rdata, StartPos, EndPos);
/* Unless record is flagged as not important, update LSN of last important record in the current slot. When holding all locks, just update the first one. */
if ((flags & XLOG_MARK_UNIMPORTANT) == 0) {
int lockno = holdingAllLocks ? 0 : MyLockNo;
WALInsertLocks[lockno].l.lastImportantAt = StartPos;
}
}else{
/* This was an xlog-switch record, but the current insert location was
* already exactly at the beginning of a segment, so there was no need
* to do anything. */
}
/* Done! Let others know that we're finished. */
WALInsertLockRelease();
MarkCurrentTransactionIdLoggedIfAny();
END_CRIT_SECTION();
/* Update shared LogwrtRqst.Write, if we crossed page boundary. */
if (StartPos / XLOG_BLCKSZ != EndPos / XLOG_BLCKSZ)
{
SpinLockAcquire(&XLogCtl->info_lck);
/* advance global request to include new block(s) */
if (XLogCtl->LogwrtRqst.Write < EndPos)
XLogCtl->LogwrtRqst.Write = EndPos;
/* update local result copy while I have the chance */
LogwrtResult = XLogCtl->LogwrtResult;
SpinLockRelease(&XLogCtl->info_lck);
}
/*
* If this was an XLOG_SWITCH record, flush the record and the empty
* padding space that fills the rest of the segment, and perform
* end-of-segment actions (eg, notifying archiver).
*/
if (isLogSwitch)
{
TRACE_POSTGRESQL_WAL_SWITCH();
XLogFlush(EndPos);
/*
* Even though we reserved the rest of the segment for us, which is
* reflected in EndPos, we return a pointer to just the end of the
* xlog-switch record.
*/
if (inserted)
{
EndPos = StartPos + SizeOfXLogRecord;
if (StartPos / XLOG_BLCKSZ != EndPos / XLOG_BLCKSZ)
{
uint64 offset = XLogSegmentOffset(EndPos, wal_segment_size);
if (offset == EndPos % XLOG_BLCKSZ)
EndPos += SizeOfXLogLongPHD;
else
EndPos += SizeOfXLogShortPHD;
}
}
}
/* Update our global variables */
ProcLastRecPtr = StartPos;
XactLastRecEnd = EndPos;
return EndPos;
}