diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a2846c4..fe4ef8d 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -507,6 +507,13 @@ typedef struct XLogCtlInsert XLogRecPtr lastBackupStart; /* + * WAL insertion progress positions. Each entry is protected by a single + * WAL insert lock, and should be updated only while holding its + * respective lock. + */ + XLogRecPtr progressLSN[NUM_XLOGINSERT_LOCKS]; + + /* * WAL insertion locks. */ WALInsertLockPadded *WALInsertLocks; @@ -527,6 +534,8 @@ typedef struct XLogCtlData TransactionId ckptXid; XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */ XLogRecPtr replicationSlotMinLSN; /* oldest LSN needed by any slot */ + XLogRecPtr lastProgressLSN; /* LSN tracking progress of checkpoint + * and standby snapshots */ XLogSegNo lastRemovedSegNo; /* latest removed/recycled XLOG * segment */ @@ -897,6 +906,7 @@ XLogInsertRecord(XLogRecData *rdata, XLogRecPtr fpw_lsn) XLogRecord *rechdr = (XLogRecord *) rdata->data; bool isLogSwitch = (rechdr->xl_rmid == RM_XLOG_ID && rechdr->xl_info == XLOG_SWITCH); + bool isStandbySnapshot = (rechdr->xl_rmid == RM_STANDBY_ID); XLogRecPtr StartPos; XLogRecPtr EndPos; @@ -988,6 +998,27 @@ XLogInsertRecord(XLogRecData *rdata, XLogRecPtr fpw_lsn) inserted = true; } + /* + * Update the progress LSN positions, WAL insertion locks are already + * taken appropriately before doing that. + */ + if (!isStandbySnapshot) + { + if (holdingAllLocks) + { + int i; + + for (i = 0; i < NUM_XLOGINSERT_LOCKS; i++) + if (Insert->progressLSN[i] < EndPos) + Insert->progressLSN[i] = EndPos; + } + else + { + if (Insert->progressLSN[MyLockNo] < EndPos) + Insert->progressLSN[MyLockNo] = EndPos; + } + } + if (inserted) { /* @@ -4684,6 +4715,7 @@ XLOGShmemInit(void) { LWLockInitialize(&WALInsertLocks[i].l.lock, LWTRANCHE_WAL_INSERT); WALInsertLocks[i].l.insertingAt = InvalidXLogRecPtr; + XLogCtl->Insert.progressLSN[i] = InvalidXLogRecPtr; } /* @@ -6314,6 +6346,7 @@ StartupXLOG(void) checkPoint.newestCommitTsXid); XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch; XLogCtl->ckptXid = checkPoint.nextXid; + XLogCtl->lastProgressLSN = checkPoint.redo; /* * Initialize replication slots, before there's a chance to remove @@ -7869,6 +7902,49 @@ GetFlushRecPtr(void) } /* + * XLOGHasActivity -- Determine if any WAL activity has occurred since + * the last time this routine has been used by looking at the progress + * of WAL. This routine should be used to decide if checkpoints should + * happen or if standby activity should be logged or not. + */ +bool +XLOGHasActivity(bool islock) +{ + XLogRecPtr progress_lsn = InvalidXLogRecPtr; + int i; + XLogCtlInsert *Insert = &XLogCtl->Insert; + bool res = false; + + /* + * Look at the latest LSN position referring to the activity done by + * WAL insertion. First fetch all the LSN position that are tracked + * by each WAL insertion slot and compare it to the last progress + * position found to decide if any activity has run on the server or + * not. + */ + if (islock) + WALInsertLockAcquireExclusive(); + for (i = 0; i < NUM_XLOGINSERT_LOCKS; i++) + { + if (progress_lsn < Insert->progressLSN[i]) + progress_lsn = Insert->progressLSN[i]; + } + if (islock) + WALInsertLockRelease(); + + /* Update the progress_lsn if some activity has occured */ + SpinLockAcquire(&XLogCtl->info_lck); + if (XLogCtl->lastProgressLSN < progress_lsn) + { + XLogCtl->lastProgressLSN = progress_lsn; + res = true; + } + SpinLockRelease(&XLogCtl->info_lck); + + return res; +} + +/* * Get the time of the last xlog segment switch */ pg_time_t @@ -8132,7 +8208,6 @@ CreateCheckPoint(int flags) uint32 freespace; XLogRecPtr PriorRedoPtr; XLogRecPtr curInsert; - XLogRecPtr prevPtr; VirtualTransactionId *vxids; int nvxids; @@ -8218,17 +8293,15 @@ CreateCheckPoint(int flags) */ WALInsertLockAcquireExclusive(); curInsert = XLogBytePosToRecPtr(Insert->CurrBytePos); - prevPtr = XLogBytePosToRecPtr(Insert->PrevBytePos); /* - * If this isn't a shutdown or forced checkpoint, and we have not inserted - * any XLOG records since the start of the last checkpoint, skip the - * checkpoint. The idea here is to avoid inserting duplicate checkpoints - * when the system is idle. That wastes log space, and more importantly it - * exposes us to possible loss of both current and previous checkpoint - * records if the machine crashes just as we're writing the update. - * (Perhaps it'd make even more sense to checkpoint only when the previous - * checkpoint record is in a different xlog page?) + * If this isn't a shutdown or forced checkpoint, and if there has been no + * WAL activity, skip the checkpoint. The idea here is to avoid inserting + * duplicate checkpoints when the system is idle. That wastes log space, + * and more importantly it exposes us to possible loss of both current and + * previous checkpoint records if the machine crashes just as we're writing + * the update. (Perhaps it'd make even more sense to checkpoint only when + * the previous checkpoint record is in a different xlog page?) * * If the previous checkpoint crossed a WAL segment, however, we create * the checkpoint anyway, to have the latest checkpoint fully contained in @@ -8239,9 +8312,12 @@ CreateCheckPoint(int flags) if ((flags & (CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_END_OF_RECOVERY | CHECKPOINT_FORCE)) == 0) { - if (prevPtr == ControlFile->checkPointCopy.redo && - prevPtr / XLOG_SEG_SIZE == curInsert / XLOG_SEG_SIZE) + elog(LOG, "Not a forced or shutdown checkpoint"); + if (!XLOGHasActivity(false) && + ControlFile->checkPointCopy.redo / XLOG_SEG_SIZE == + curInsert / XLOG_SEG_SIZE) { + elog(LOG, "Checkpoint is skipped"); WALInsertLockRelease(); LWLockRelease(CheckpointLock); END_CRIT_SECTION(); @@ -8408,7 +8484,11 @@ CreateCheckPoint(int flags) * recovery we don't need to write running xact data. */ if (!shutdown && XLogStandbyInfoActive()) - LogStandbySnapshot(); + { + XLogRecPtr lsn = LogStandbySnapshot(); + elog(LOG, "snapshot taken by checkpoint %X/%X", + (uint32) (lsn >> 32), (uint32) lsn); + } START_CRIT_SECTION(); diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 4ff4caf..0388fc7 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -78,12 +78,10 @@ int BgWriterDelay = 200; #define LOG_SNAPSHOT_INTERVAL_MS 15000 /* - * LSN and timestamp at which we last issued a LogStandbySnapshot(), to avoid - * doing so too often or repeatedly if there has been no other write activity - * in the system. + * Timestamp at which we last issued a LogStandbySnapshot(), to avoid doing + * so too often. */ static TimestampTz last_snapshot_ts; -static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr; /* * Flags set by interrupt handlers for later service in the main loop. @@ -301,7 +299,7 @@ BackgroundWriterMain(void) * check whether there has been any WAL inserted since the last time * we've logged a running xacts. * - * We do this logging in the bgwriter as its the only process that is + * We do this logging in the bgwriter as it is the only process that is * run regularly and returns to its mainloop all the time. E.g. * Checkpointer, when active, is barely ever in its mainloop and thus * makes it hard to log regularly. @@ -315,13 +313,13 @@ 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. */ - if (now >= timeout && - last_snapshot_lsn != GetXLogInsertRecPtr()) + if (now >= timeout && XLOGHasActivity(true)) { - last_snapshot_lsn = LogStandbySnapshot(); + XLogRecPtr lsn = LogStandbySnapshot(); + elog(LOG, "snapshot taken by bgwriter %X/%X", + (uint32) (lsn >> 32), (uint32) lsn); last_snapshot_ts = now; } } diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index ecd30ce..43fe026 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -261,6 +261,7 @@ extern void GetFullPageWriteInfo(XLogRecPtr *RedoRecPtr_p, bool *doPageWrites_p) extern XLogRecPtr GetRedoRecPtr(void); extern XLogRecPtr GetInsertRecPtr(void); extern XLogRecPtr GetFlushRecPtr(void); +extern bool XLOGHasActivity(bool islock); extern void GetNextXidAndEpoch(TransactionId *xid, uint32 *epoch); extern void RemovePromoteSignalFiles(void);