Commit 5ae15729 authored by Michael Paquier's avatar Michael Paquier

Fix O(N^2) stat() calls when recycling WAL segments

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