From 5375b7fd82ffa56eda1af516a2a5ec5f000910e8 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Date: Wed, 2 Sep 2026 14:26:07 +0000
Subject: [PATCH v3 1/2] Batch fsyncs when removing old WAL segments

RemoveOldXlogFiles() uses durable_unlink() for every old WAL file it
cannot recycle.  Each call fsyncs pg_wal, forcing one filesystem
journal flush per file.

Unlink all such files first, fsync pg_wal once after the scan, then
remove their archive-status files.  If a crash precedes the directory
fsync, obsolete files may reappear; retaining the status files lets a
later checkpoint remove them again.

Keep timeline-switch cleanup unbatched, since it is not part of the
normal checkpoint removal pass.
---
 src/backend/access/transam/xlog.c | 45 ++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 9 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 2e3f177100b..204c36a18c8 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -724,7 +724,7 @@ static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr lastredoptr,
 							   XLogRecPtr endptr, TimeLineID insertTLI);
 static void RemoveXlogFile(const struct dirent *segment_de,
 						   XLogSegNo recycleSegNo, XLogSegNo *endlogSegNo,
-						   TimeLineID insertTLI);
+						   TimeLineID insertTLI, List **cleanup_names);
 static void UpdateLastRemovedPtr(char *filename);
 static void ValidateXLOGDirectoryStructure(void);
 static void CleanupBackupHistory(void);
@@ -3928,6 +3928,8 @@ RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr lastredoptr, XLogRecPtr endptr,
 	char		lastoff[MAXFNAMELEN];
 	XLogSegNo	endlogSegNo;
 	XLogSegNo	recycleSegNo;
+	List	   *cleanup_names = NIL;
+	ListCell   *lc;
 
 	/* Initialize info about where to try to recycle to */
 	XLByteToSeg(endptr, endlogSegNo, wal_segment_size);
@@ -3970,12 +3972,23 @@ RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr lastredoptr, XLogRecPtr endptr,
 				/* Update the last removed location in shared memory first */
 				UpdateLastRemovedPtr(xlde->d_name);
 
-				RemoveXlogFile(xlde, recycleSegNo, &endlogSegNo, insertTLI);
+				RemoveXlogFile(xlde, recycleSegNo, &endlogSegNo, insertTLI,
+							   &cleanup_names);
 			}
 		}
 	}
 
 	FreeDir(xldir);
+
+	/* Make all removals durable with one directory fsync. */
+	if (cleanup_names != NIL)
+		fsync_fname(XLOGDIR, true);
+
+	/* Remove archive status only after the WAL-file removals are durable. */
+	foreach(lc, cleanup_names)
+		XLogArchiveCleanup((char *) lfirst(lc));
+
+	list_free_deep(cleanup_names);
 }
 
 /*
@@ -4043,7 +4056,7 @@ RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
 			 * - but seems safer to let them be archived and removed later.
 			 */
 			if (!XLogArchiveIsReady(xlde->d_name))
-				RemoveXlogFile(xlde, recycleSegNo, &endLogSegNo, newTLI);
+				RemoveXlogFile(xlde, recycleSegNo, &endLogSegNo, newTLI, NULL);
 		}
 	}
 
@@ -4066,15 +4079,18 @@ RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
 static void
 RemoveXlogFile(const struct dirent *segment_de,
 			   XLogSegNo recycleSegNo, XLogSegNo *endlogSegNo,
-			   TimeLineID insertTLI)
+			   TimeLineID insertTLI, List **cleanup_names)
 {
 	char		path[MAXPGPATH];
+	bool		removed = false;
+	const char *unlinkpath;
 #ifdef WIN32
 	char		newpath[MAXPGPATH];
 #endif
 	const char *segname = segment_de->d_name;
 
 	snprintf(path, MAXPGPATH, XLOGDIR "/%s", segname);
+	unlinkpath = path;
 
 	/*
 	 * Before deleting the file, see if it can be recycled as a future log
@@ -4125,19 +4141,30 @@ RemoveXlogFile(const struct dirent *segment_de,
 							path)));
 			return;
 		}
-		rc = durable_unlink(newpath, LOG);
-#else
-		rc = durable_unlink(path, LOG);
+		unlinkpath = newpath;
 #endif
+
+		if (cleanup_names != NULL)
+			rc = unlink(unlinkpath);
+		else
+			rc = durable_unlink(unlinkpath, LOG);
 		if (rc != 0)
 		{
-			/* Message already logged by durable_unlink() */
+			if (cleanup_names != NULL)
+				ereport(LOG,
+						(errcode_for_file_access(),
+						 errmsg("could not remove file \"%s\": %m",
+								unlinkpath)));
 			return;
 		}
 		CheckpointStats.ckpt_segs_removed++;
+		removed = true;
 	}
 
-	XLogArchiveCleanup(segname);
+	if (removed && cleanup_names != NULL)
+		*cleanup_names = lappend(*cleanup_names, pstrdup(segname));
+	else
+		XLogArchiveCleanup(segname);
 }
 
 /*
-- 
2.43.0

