Fix O(N^2) stat() calls when recycling WAL segments
authorMichael Paquier <michael@paquier.xyz>
Fri, 15 Jan 2021 01:33:13 +0000 (10:33 +0900)
committerMichael Paquier <michael@paquier.xyz>
Fri, 15 Jan 2021 01:33:13 +0000 (10:33 +0900)
The counter tracking the last segment number recycled was getting
initialized when recycling one single segment, while it should be used
across a full cycle of segments recycled to prevent useless checks
related to entries already recycled.

This performance issue has been introduced by b2a5545, and it was first
implemented in 61b86142.

No backpatch is done per the lack of field complaints.

Reported-by: Andres Freund, Thomas Munro
Author: Michael Paquier
Reviewed-By: Andres Freund
Discussion: https://postgr.es/m/20170621211016.eln6cxxp3jrv7m4m@alap3.anarazel.de
Discussion: https://postgr.es/m/CA+hUKG+DRiF9z1_MU4fWq+RfJMxP7zjoptfcmuCFPeO4JM2iVg@mail.gmail.com

src/backend/access/transam/xlog.c

index b18257c1980784385062de95119ea76875c9d9fd..199d911be76be933171d1cd56fcbb457c8d58797 100644 (file)
@@ -930,7 +930,8 @@ static void XLogFileClose(void);
 static void PreallocXlogFiles(XLogRecPtr endptr);
 static void RemoveTempXlogFiles(void);
 static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr lastredoptr, XLogRecPtr endptr);
-static void RemoveXlogFile(const char *segname, XLogRecPtr lastredoptr, XLogRecPtr endptr);
+static void RemoveXlogFile(const char *segname, XLogSegNo recycleSegNo,
+                                                  XLogSegNo *endlogSegNo);
 static void UpdateLastRemovedPtr(char *filename);
 static void ValidateXLOGDirectoryStructure(void);
 static void CleanupBackupHistory(void);
@@ -4055,6 +4056,12 @@ RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr lastredoptr, XLogRecPtr endptr)
        DIR                *xldir;
        struct dirent *xlde;
        char            lastoff[MAXFNAMELEN];
+       XLogSegNo       endlogSegNo;
+       XLogSegNo       recycleSegNo;
+
+       /* Initialize info about where to try to recycle to */
+       XLByteToSeg(endptr, endlogSegNo, wal_segment_size);
+       recycleSegNo = XLOGfileslop(lastredoptr);
 
        /*
         * Construct a filename of the last segment to be kept. The timeline ID
@@ -4093,7 +4100,7 @@ RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr lastredoptr, XLogRecPtr endptr)
                                /* Update the last removed location in shared memory first */
                                UpdateLastRemovedPtr(xlde->d_name);
 
-                               RemoveXlogFile(xlde->d_name, lastredoptr, endptr);
+                               RemoveXlogFile(xlde->d_name, recycleSegNo, &endlogSegNo);
                        }
                }
        }
@@ -4123,13 +4130,21 @@ RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
        struct dirent *xlde;
        char            switchseg[MAXFNAMELEN];
        XLogSegNo       endLogSegNo;
+       XLogSegNo       switchLogSegNo;
+       XLogSegNo       recycleSegNo;
 
-       XLByteToPrevSeg(switchpoint, endLogSegNo, wal_segment_size);
+       /*
+        * Initialize info about where to begin the work.  This will recycle,
+        * somewhat arbitrarily, 10 future segments.
+        */
+       XLByteToPrevSeg(switchpoint, switchLogSegNo, wal_segment_size);
+       XLByteToSeg(switchpoint, endLogSegNo, wal_segment_size);
+       recycleSegNo = endLogSegNo + 10;
 
        /*
         * Construct a filename of the last segment to be kept.
         */
-       XLogFileName(switchseg, newTLI, endLogSegNo, wal_segment_size);
+       XLogFileName(switchseg, newTLI, switchLogSegNo, wal_segment_size);
 
        elog(DEBUG2, "attempting to remove WAL segments newer than log file %s",
                 switchseg);
@@ -4157,7 +4172,7 @@ RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
                         * - but seems safer to let them be archived and removed later.
                         */
                        if (!XLogArchiveIsReady(xlde->d_name))
-                               RemoveXlogFile(xlde->d_name, InvalidXLogRecPtr, switchpoint);
+                               RemoveXlogFile(xlde->d_name, recycleSegNo, &endLogSegNo);
                }
        }
 
@@ -4167,36 +4182,22 @@ RemoveNonParentXlogFiles(XLogRecPtr switchpoint, TimeLineID newTLI)
 /*
  * Recycle or remove a log file that's no longer needed.
  *
- * endptr is current (or recent) end of xlog, and lastredoptr is the
- * redo pointer of the last checkpoint. These are used to determine
- * whether we want to recycle rather than delete no-longer-wanted log files.
- * If lastredoptr is not known, pass invalid, and the function will recycle,
- * somewhat arbitrarily, 10 future segments.
+ * segname is the name of the segment to recycle or remove.  recycleSegNo
+ * is the segment number to recycle up to.  endlogSegNo is the segment
+ * number of the current (or recent) end of WAL.
+ *
+ * endlogSegNo gets incremented if the segment is recycled so as it is not
+ * checked again with future callers of this function.
  */
 static void
-RemoveXlogFile(const char *segname, XLogRecPtr lastredoptr, XLogRecPtr endptr)
+RemoveXlogFile(const char *segname, XLogSegNo recycleSegNo,
+                          XLogSegNo *endlogSegNo)
 {
        char            path[MAXPGPATH];
 #ifdef WIN32
        char            newpath[MAXPGPATH];
 #endif
        struct stat statbuf;
-       XLogSegNo       endlogSegNo;
-       XLogSegNo       recycleSegNo;
-
-       if (wal_recycle)
-       {
-               /*
-                * Initialize info about where to try to recycle to.
-                */
-               XLByteToSeg(endptr, endlogSegNo, wal_segment_size);
-               if (lastredoptr == InvalidXLogRecPtr)
-                       recycleSegNo = endlogSegNo + 10;
-               else
-                       recycleSegNo = XLOGfileslop(lastredoptr);
-       }
-       else
-               recycleSegNo = 0;               /* keep compiler quiet */
 
        snprintf(path, MAXPGPATH, XLOGDIR "/%s", segname);
 
@@ -4206,9 +4207,9 @@ RemoveXlogFile(const char *segname, XLogRecPtr lastredoptr, XLogRecPtr endptr)
         * symbolic links pointing to a separate archive directory.
         */
        if (wal_recycle &&
-               endlogSegNo <= recycleSegNo &&
+               *endlogSegNo <= recycleSegNo &&
                lstat(path, &statbuf) == 0 && S_ISREG(statbuf.st_mode) &&
-               InstallXLogFileSegment(&endlogSegNo, path,
+               InstallXLogFileSegment(endlogSegNo, path,
                                                           true, recycleSegNo, true))
        {
                ereport(DEBUG2,
@@ -4216,7 +4217,7 @@ RemoveXlogFile(const char *segname, XLogRecPtr lastredoptr, XLogRecPtr endptr)
                                                segname)));
                CheckpointStats.ckpt_segs_recycled++;
                /* Needn't recheck that slot on future iterations */
-               endlogSegNo++;
+               (*endlogSegNo)++;
        }
        else
        {