diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 147fd53..6608666 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -526,6 +526,8 @@ typedef struct XLogCtlData XLogRecPtr RedoRecPtr; /* a recent copy of Insert->RedoRecPtr */ uint32 ckptXidEpoch; /* nextXID & epoch of latest checkpoint */ TransactionId ckptXid; + XLogRecPtr forcedSegSwitchLSN; /* LSN position of last forced segment + * switch */ XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */ XLogRecPtr replicationSlotMinLSN; /* oldest LSN needed by any slot */ @@ -6315,6 +6317,7 @@ StartupXLOG(void) checkPoint.newestCommitTs); XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch; XLogCtl->ckptXid = checkPoint.nextXid; + XLogCtl->forcedSegSwitchLSN = InvalidXLogRecPtr; /* * Initialize replication slots, before there's a chance to remove @@ -8988,6 +8991,10 @@ RequestXLogSwitch(void) XLogBeginInsert(); RecPtr = XLogInsert(RM_XLOG_ID, XLOG_SWITCH); + SpinLockAcquire(&XLogCtl->info_lck); + XLogCtl->forcedSegSwitchLSN = RecPtr; + SpinLockRelease(&XLogCtl->info_lck); + return RecPtr; } @@ -10628,6 +10635,21 @@ GetXLogWriteRecPtr(void) } /* + * Get last WAL position where an XLOG segment has been forcibly switched. + */ +XLogRecPtr +GetXLogLastSwitchPtr(void) +{ + XLogRecPtr last_switch_lsn; + + SpinLockAcquire(&XLogCtl->info_lck); + last_switch_lsn = XLogCtl->forcedSegSwitchLSN; + SpinLockRelease(&XLogCtl->info_lck); + + return last_switch_lsn; +} + +/* * Returns the redo pointer of the last checkpoint or restartpoint. This is * the oldest point in WAL that we still need, if we have to restart recovery. */ diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 65465d6..3abf725 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -315,14 +315,39 @@ BackgroundWriterMain(void) LOG_SNAPSHOT_INTERVAL_MS); /* - * only log if enough time has passed and some xlog record has - * been inserted. + * Only log if enough time has passed and some xlog record has + * been inserted on a new segment. On an idle system where + * segments can be archived in a fast pace with for example a + * low archive_command setting, avoid as well logging a new + * standby snapshot if the current insert position is still + * at the beginning of the segment that has just been switched. + * + * It is possible that GetXLogLastSwitchPtr points to the last + * position of previous segment or to the first position of the + * new segment after the switch, hence take both cases into + * account when deciding if a standby snapshot should be taken. + * (see comments on top of RequestXLogSwitch for more details). */ - if (now >= timeout && - last_snapshot_lsn != GetXLogInsertRecPtr()) + if (now >= timeout) { - last_snapshot_lsn = LogStandbySnapshot(); - last_snapshot_ts = now; + XLogRecPtr insert_lsn = GetXLogInsertRecPtr(); + XLogRecPtr last_forced_switch_lsn = GetXLogLastSwitchPtr(); + XLogSegNo insert_segno; + bool is_switch_current; + + XLByteToSeg(insert_lsn, insert_segno); + + is_switch_current = + XLByteInSeg(last_forced_switch_lsn, insert_segno - 1) || + XLByteInSeg(last_forced_switch_lsn, insert_segno); + + if (last_snapshot_lsn != insert_lsn && + (!is_switch_current || + (insert_lsn % XLOG_SEG_SIZE) != SizeOfXLogLongPHD)) + { + last_snapshot_lsn = LogStandbySnapshot(); + last_snapshot_ts = now; + } } } diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 790ca66..f6eb9c3 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -235,6 +235,7 @@ extern void GetXLogReceiptTime(TimestampTz *rtime, bool *fromStream); extern XLogRecPtr GetXLogReplayRecPtr(TimeLineID *replayTLI); extern XLogRecPtr GetXLogInsertRecPtr(void); extern XLogRecPtr GetXLogWriteRecPtr(void); +extern XLogRecPtr GetXLogLastSwitchPtr(void); extern bool RecoveryIsPaused(void); extern void SetRecoveryPause(bool recoveryPause); extern TimestampTz GetLatestXTime(void);