From 781306ce190509749d4b45b76894ae1790b7c892 Mon Sep 17 00:00:00 2001 From: Anthonin Bonnefoy Date: Wed, 2 Jul 2025 09:58:52 +0200 Subject: Don't keep closed WAL segments in page cache after replay On a standby, the recovery process reads the WAL segments, applies changes and closes the segment. When closed, the segments will still be in page cache memory until they are evicted due to inactivity. The segments may be re-read if archive_mode is set to always, wal_summarizer is enabled or if the standby is used for replication and has an active walsender. Outside of those circumstances, the WAL segments won't be re-read and keeping them in the page cache generates unnecessary memory pressure. If the standby doesn't archive wal, doesn't have wal_summarize and doesn't have an active walsender, a POSIX_FADV_DONTNEED is sent before closing a replayed WAL segment to immediately free any cached pages. --- src/backend/access/transam/xlogrecovery.c | 16 ++++++++++++++++ src/backend/replication/walsender.c | 23 +++++++++++++++++++++++ src/include/replication/walsender.h | 1 + 3 files changed, 40 insertions(+) diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index c0c2744d45b..762b517e8ef 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -49,6 +49,7 @@ #include "pgstat.h" #include "postmaster/bgwriter.h" #include "postmaster/startup.h" +#include "postmaster/walsummarizer.h" #include "replication/slot.h" #include "replication/slotsync.h" #include "replication/walreceiver.h" @@ -3369,6 +3370,21 @@ XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen, } } + /* + * Once replayed, WAL segment files may be re-read in several cases: + * archive_mode is set to always, summarize_wal is enabled or the + * standby acts as a walsender for either logical or physical + * replication. Outside of those conditions, the WAL segment files + * shouldn't be re-read and we can signal the kernel to release any + * cached pages. + */ +#if defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_DONTNEED) + { + if (StandbyMode && XLogArchiveMode != ARCHIVE_MODE_ALWAYS && + !summarize_wal && !WalSndRunning()) + (void) posix_fadvise(readFile, 0, 0, POSIX_FADV_DONTNEED); + } +#endif close(readFile); readFile = -1; readSource = XLOG_FROM_ANY; diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 2cde8ebc729..8ae3ef155d9 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -3697,6 +3697,29 @@ WalSndRqstFileReload(void) } } +/* + * Return true if there's at least one active walsender process + */ +bool +WalSndRunning(void) +{ + int i; + + for (i = 0; i < max_wal_senders; i++) + { + WalSnd *walsnd = &WalSndCtl->walsnds[i]; + + SpinLockAcquire(&walsnd->mutex); + if (walsnd->pid > 0) + { + SpinLockRelease(&walsnd->mutex); + return true; + } + SpinLockRelease(&walsnd->mutex); + } + return false; +} + /* * Handle PROCSIG_WALSND_INIT_STOPPING signal. */ diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h index a4df3b8e0ae..7c197e71392 100644 --- a/src/include/replication/walsender.h +++ b/src/include/replication/walsender.h @@ -48,6 +48,7 @@ extern void WalSndInitStopping(void); extern void WalSndWaitStopping(void); extern void HandleWalSndInitStopping(void); extern void WalSndRqstFileReload(void); +extern bool WalSndRunning(void); /* * Remember that we want to wakeup walsenders later -- 2.52.0