From f1be36ff8099ea2eed2460cb4de94ce13e5435dc Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 29 Sep 2023 16:43:38 -0400 Subject: [PATCH v7 3/3] WIP: Insert XLOG_CHECKPOINT_REDO at the redo point. Merge this into previous commit. --- src/backend/access/transam/xlog.c | 167 +++++++++++++++++++++--------- src/tools/pgindent/typedefs.list | 1 + 2 files changed, 118 insertions(+), 50 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 64bf3d7eea..12ba6cdaa6 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -559,6 +559,16 @@ typedef struct XLogCtlData slock_t info_lck; /* locks shared variables shown above */ } XLogCtlData; +/* + * Classification of XLogRecordInsert operations. + */ +typedef enum +{ + WALINSERT_NORMAL, + WALINSERT_SPECIAL_SWITCH, + WALINSERT_SPECIAL_CHECKPOINT +} WalInsertClass; + static XLogCtlData *XLogCtl = NULL; /* a private copy of XLogCtl->Insert.WALInsertLocks, for convenience */ @@ -739,13 +749,21 @@ XLogInsertRecord(XLogRecData *rdata, 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); + WalInsertClass class = WALINSERT_NORMAL; XLogRecPtr StartPos; XLogRecPtr EndPos; bool prevDoPageWrites = doPageWrites; TimeLineID insertTLI; + /* Does this record type require special handling? */ + if (rechdr->xl_rmid == RM_XLOG_ID) + { + if (info == XLOG_SWITCH) + class = WALINSERT_SPECIAL_SWITCH; + else if (info == XLOG_CHECKPOINT_REDO) + class = WALINSERT_SPECIAL_CHECKPOINT; + } + /* we assume that all of the record header is in the first chunk */ Assert(rdata->len >= SizeOfXLogRecord); @@ -793,24 +811,7 @@ XLogInsertRecord(XLogRecData *rdata, */ START_CRIT_SECTION(); - if (isLogSwitch) - { - /* - * In order to insert an XLOG_SWITCH record, we need to hold all of - * the WAL insertion locks, not just one, so that no one else can - * begin inserting a record until we've figured out how much space - * remains in the current WAL segment and claimed all of it. - * - * Nonetheless, this case is simpler than the normal cases handled - * below, which must check for changes in doPageWrites and RedoRecPtr. - * Those checks are only needed for records that can contain - * full-pages images, and an XLOG_SWITCH record never does. - */ - Assert(fpw_lsn == InvalidXLogRecPtr); - WALInsertLockAcquireExclusive(); - inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev); - } - else + if (likely(class == WALINSERT_NORMAL)) { WALInsertLockAcquire(); @@ -860,6 +861,41 @@ XLogInsertRecord(XLogRecData *rdata, /* Normal records are always inserted. */ inserted = true; } + else if (class == WALINSERT_SPECIAL_SWITCH) + { + /* + * In order to insert an XLOG_SWITCH record, we need to hold all of + * the WAL insertion locks, not just one, so that no one else can + * begin inserting a record until we've figured out how much space + * remains in the current WAL segment and claimed all of it. + * + * Nonetheless, this case is simpler than the normal cases handled + * below, which must check for changes in doPageWrites and RedoRecPtr. + * Those checks are only needed for records that can contain buffer + * references, and an XLOG_SWITCH record never does. + */ + Assert(fpw_lsn == InvalidXLogRecPtr); + WALInsertLockAcquireExclusive(); + inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev); + } + else + { + Assert(class == WALINSERT_SPECIAL_CHECKPOINT); + + /* + * We need to update both the local and shared copies of RedoRecPtr, + * which means that we need to hold all the WAL insertion locks. + * However, there can't be any buffer references, so as above, we need + * not check RedoRecPtr before inserting the record; we just need to + * update it afterwards. + */ + Assert(fpw_lsn == InvalidXLogRecPtr); + WALInsertLockAcquireExclusive(); + ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos, + &rechdr->xl_prev); + RedoRecPtr = Insert->RedoRecPtr = StartPos; + inserted = true; + } if (inserted) { @@ -876,7 +912,8 @@ XLogInsertRecord(XLogRecData *rdata, * 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, + CopyXLogRecordToWAL(rechdr->xl_tot_len, + class == WALINSERT_SPECIAL_SWITCH, rdata, StartPos, EndPos, insertTLI); /* @@ -935,7 +972,7 @@ XLogInsertRecord(XLogRecData *rdata, * padding space that fills the rest of the segment, and perform * end-of-segment actions (eg, notifying archiver). */ - if (isLogSwitch) + if (class == WALINSERT_SPECIAL_SWITCH) { TRACE_POSTGRESQL_WAL_SWITCH(); XLogFlush(EndPos); @@ -6486,6 +6523,8 @@ update_checkpoint_display(int flags, bool restartpoint, bool reset) * All of this mechanism allows us to continue working while we checkpoint. * As a result, timing of actions is critical here and be careful to note that * this function will likely take minutes to execute on a busy system. + * + * XXX FIX ABOVE COMMENTS */ void CreateCheckPoint(int flags) @@ -6608,36 +6647,38 @@ CreateCheckPoint(int flags) checkPoint.fullPageWrites = Insert->fullPageWrites; - /* - * Compute new REDO record ptr = location of next XLOG record. - * - * NB: this is NOT necessarily where the checkpoint record itself will be, - * since other backends may insert more XLOG records while we're off doing - * the buffer flush work. Those XLOG records are logically after the - * checkpoint, even though physically before it. Got that? - */ - freespace = INSERT_FREESPACE(curInsert); - if (freespace == 0) + if (shutdown) { - if (XLogSegmentOffset(curInsert, wal_segment_size) == 0) - curInsert += SizeOfXLogLongPHD; - else - curInsert += SizeOfXLogShortPHD; - } - checkPoint.redo = curInsert; + /* + * Compute new REDO record ptr = location of next XLOG record. + * + * Since this is a shutdown checkpoint, there can't be any concurrent + * WAL insertion. + */ + freespace = INSERT_FREESPACE(curInsert); + if (freespace == 0) + { + if (XLogSegmentOffset(curInsert, wal_segment_size) == 0) + curInsert += SizeOfXLogLongPHD; + else + curInsert += SizeOfXLogShortPHD; + } + checkPoint.redo = curInsert; - /* - * Here we update the shared RedoRecPtr for future XLogInsert calls; this - * must be done while holding all the insertion locks. - * - * Note: if we fail to complete the checkpoint, RedoRecPtr will be left - * pointing past where it really needs to point. This is okay; the only - * consequence is that XLogInsert might back up whole buffers that it - * didn't really need to. We can't postpone advancing RedoRecPtr because - * XLogInserts that happen while we are dumping buffers must assume that - * their buffer changes are not included in the checkpoint. - */ - RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo; + /* + * Here we update the shared RedoRecPtr for future XLogInsert calls; + * this must be done while holding all the insertion locks. + * + * Note: if we fail to complete the checkpoint, RedoRecPtr will be + * left pointing past where it really needs to point. This is okay; + * the only consequence is that XLogInsert might back up whole buffers + * that it didn't really need to. We can't postpone advancing + * RedoRecPtr because XLogInserts that happen while we are dumping + * buffers must assume that their buffer changes are not included in + * the checkpoint. + */ + RedoRecPtr = XLogCtl->Insert.RedoRecPtr = checkPoint.redo; + } /* * Now we can release the WAL insertion locks, allowing other xacts to @@ -6645,6 +6686,32 @@ CreateCheckPoint(int flags) */ WALInsertLockRelease(); + /* + * If this is an online checkpoint, we have not yet determined the redo + * point. We do so now by inserting the special XLOG_CHECKPOINT_REDO + * record; the LSN at which it starts becomes the new redo pointer. We + * don't do this for a shutdown checkpoint, because in that case no WAL + * can be written between the redo point and the insertion of the + * checkpoint record itself, so the checkpoint record itself services to + * mark the redo point. + */ + if (!shutdown) + { + int dummy = 0; + + /* Record must have payload to avoid assertion failure. */ + XLogBeginInsert(); + XLogRegisterData((char *) &dummy, sizeof(dummy)); + (void) XLogInsert(RM_XLOG_ID, XLOG_CHECKPOINT_REDO); + + /* + * XLogInsertRecord will have updated RedoRecPtr, but we need to copy + * that into the record that will be inserted when the checkpoint is + * complete. + */ + checkPoint.redo = RedoRecPtr; + } + /* Update the info_lck-protected copy of RedoRecPtr as well */ SpinLockAcquire(&XLogCtl->info_lck); XLogCtl->RedoRecPtr = checkPoint.redo; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 8de90c4958..3252b3aedd 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2971,6 +2971,7 @@ VolatileFunctionStatus Vsrt WAIT_ORDER WALAvailability +WalInsertClass WALInsertLock WALInsertLockPadded WALOpenSegment -- 2.37.1 (Apple Git-137.1)