]> granicus.if.org Git - postgresql/commitdiff
Fix calculation for WAL segment recycling and removal
authorMichael Paquier <michael@paquier.xyz>
Tue, 24 Jul 2018 01:33:07 +0000 (10:33 +0900)
committerMichael Paquier <michael@paquier.xyz>
Tue, 24 Jul 2018 01:33:07 +0000 (10:33 +0900)
Commit 4b0d28de06 has removed the prior checkpoint and related
facilities but has left WAL recycling based on the LSN of the prior
checkpoint, which causes incorrect calculations for WAL removal and
recycling for max_wal_size and min_wal_size.  This commit changes things
so as the base calculation point is the last checkpoint generated.

Reported-by: Kyotaro Horiguchi
Author: Kyotaro Horiguchi
Reviewed-by: Michael Paquier
Discussion: https://postgr.es/m/20180723.135748.42558387.horiguchi.kyotaro@lab.ntt.co.jp
Backpatch: 11-, where the prior checkpoint has been removed.

src/backend/access/transam/xlog.c

index 56c68775dfbfbc1a139c69900c7e4ca7e96b6fe7..cfef58d69dfa5475ef00607cf6deff9f413f6060 100644 (file)
@@ -887,8 +887,8 @@ static bool WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 static int     emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
 static void XLogFileClose(void);
 static void PreallocXlogFiles(XLogRecPtr endptr);
-static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr);
-static void RemoveXlogFile(const char *segname, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr);
+static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr RedoRecPtr, XLogRecPtr endptr);
+static void RemoveXlogFile(const char *segname, XLogRecPtr RedoRecPtr, XLogRecPtr endptr);
 static void UpdateLastRemovedPtr(char *filename);
 static void ValidateXLOGDirectoryStructure(void);
 static void CleanupBackupHistory(void);
@@ -2286,7 +2286,7 @@ assign_checkpoint_completion_target(double newval, void *extra)
  * XLOG segments? Returns the highest segment that should be preallocated.
  */
 static XLogSegNo
-XLOGfileslop(XLogRecPtr PriorRedoPtr)
+XLOGfileslop(XLogRecPtr RedoRecPtr)
 {
        XLogSegNo       minSegNo;
        XLogSegNo       maxSegNo;
@@ -2298,9 +2298,9 @@ XLOGfileslop(XLogRecPtr PriorRedoPtr)
         * correspond to. Always recycle enough segments to meet the minimum, and
         * remove enough segments to stay below the maximum.
         */
-       minSegNo = PriorRedoPtr / wal_segment_size +
+       minSegNo = RedoRecPtr / wal_segment_size +
                ConvertToXSegs(min_wal_size_mb, wal_segment_size) - 1;
-       maxSegNo = PriorRedoPtr / wal_segment_size +
+       maxSegNo = RedoRecPtr / wal_segment_size +
                ConvertToXSegs(max_wal_size_mb, wal_segment_size) - 1;
 
        /*
@@ -2315,7 +2315,7 @@ XLOGfileslop(XLogRecPtr PriorRedoPtr)
        /* add 10% for good measure. */
        distance *= 1.10;
 
-       recycleSegNo = (XLogSegNo) ceil(((double) PriorRedoPtr + distance) /
+       recycleSegNo = (XLogSegNo) ceil(((double) RedoRecPtr + distance) /
                                                                        wal_segment_size);
 
        if (recycleSegNo < minSegNo)
@@ -3866,12 +3866,12 @@ UpdateLastRemovedPtr(char *filename)
 /*
  * Recycle or remove all log files older or equal to passed segno.
  *
- * endptr is current (or recent) end of xlog, and PriorRedoRecPtr is the
- * redo pointer of the previous checkpoint. These are used to determine
+ * endptr is current (or recent) end of xlog, and RedoRecPtr 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.
  */
 static void
-RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr)
+RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr RedoRecPtr, XLogRecPtr endptr)
 {
        DIR                *xldir;
        struct dirent *xlde;
@@ -3914,7 +3914,7 @@ RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr)
                                /* Update the last removed location in shared memory first */
                                UpdateLastRemovedPtr(xlde->d_name);
 
-                               RemoveXlogFile(xlde->d_name, PriorRedoPtr, endptr);
+                               RemoveXlogFile(xlde->d_name, RedoRecPtr, endptr);
                        }
                }
        }
@@ -3988,14 +3988,14 @@ 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 PriorRedoRecPtr is the
- * redo pointer of the previous checkpoint. These are used to determine
+ * endptr is current (or recent) end of xlog, and RedoRecPtr 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 PriorRedoRecPtr is not known, pass invalid, and the function will
- * recycle, somewhat arbitrarily, 10 future segments.
+ * If RedoRecPtr is not known, pass invalid, and the function will recycle,
+ * somewhat arbitrarily, 10 future segments.
  */
 static void
-RemoveXlogFile(const char *segname, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr)
+RemoveXlogFile(const char *segname, XLogRecPtr RedoRecPtr, XLogRecPtr endptr)
 {
        char            path[MAXPGPATH];
 #ifdef WIN32
@@ -4009,10 +4009,10 @@ RemoveXlogFile(const char *segname, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr)
         * Initialize info about where to try to recycle to.
         */
        XLByteToSeg(endptr, endlogSegNo, wal_segment_size);
-       if (PriorRedoPtr == InvalidXLogRecPtr)
+       if (RedoRecPtr == InvalidXLogRecPtr)
                recycleSegNo = endlogSegNo + 10;
        else
-               recycleSegNo = XLOGfileslop(PriorRedoPtr);
+               recycleSegNo = XLOGfileslop(RedoRecPtr);
 
        snprintf(path, MAXPGPATH, XLOGDIR "/%s", segname);
 
@@ -8639,6 +8639,7 @@ CreateCheckPoint(int flags)
        bool            shutdown;
        CheckPoint      checkPoint;
        XLogRecPtr      recptr;
+       XLogSegNo       _logSegNo;
        XLogCtlInsert *Insert = &XLogCtl->Insert;
        uint32          freespace;
        XLogRecPtr      PriorRedoPtr;
@@ -9006,21 +9007,20 @@ CreateCheckPoint(int flags)
        smgrpostckpt();
 
        /*
-        * Delete old log files and recycle them
+        * Update the average distance between checkpoints if the prior checkpoint
+        * exists.
         */
        if (PriorRedoPtr != InvalidXLogRecPtr)
-       {
-               XLogSegNo       _logSegNo;
-
-               /* Update the average distance between checkpoints. */
                UpdateCheckPointDistanceEstimate(RedoRecPtr - PriorRedoPtr);
 
-               /* Trim from the last checkpoint, not the last - 1 */
-               XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
-               KeepLogSeg(recptr, &_logSegNo);
-               _logSegNo--;
-               RemoveOldXlogFiles(_logSegNo, PriorRedoPtr, recptr);
-       }
+       /*
+        * Delete old log files, those no longer needed for last checkpoint to
+        * prevent the disk holding the xlog from growing full.
+        */
+       XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
+       KeepLogSeg(recptr, &_logSegNo);
+       _logSegNo--;
+       RemoveOldXlogFiles(_logSegNo, RedoRecPtr, recptr);
 
        /*
         * Make more log segments if needed.  (Do this after recycling old log
@@ -9186,6 +9186,11 @@ CreateRestartPoint(int flags)
        XLogRecPtr      lastCheckPointEndPtr;
        CheckPoint      lastCheckPoint;
        XLogRecPtr      PriorRedoPtr;
+       XLogRecPtr      receivePtr;
+       XLogRecPtr      replayPtr;
+       TimeLineID      replayTLI;
+       XLogRecPtr      endptr;
+       XLogSegNo       _logSegNo;
        TimestampTz xtime;
 
        /*
@@ -9328,68 +9333,60 @@ CreateRestartPoint(int flags)
        LWLockRelease(ControlFileLock);
 
        /*
-        * Delete old log files (those no longer needed even for previous
-        * checkpoint/restartpoint) to prevent the disk holding the xlog from
-        * growing full.
+        * Update the average distance between checkpoints/restartpoints if the
+        * prior checkpoint exists.
         */
        if (PriorRedoPtr != InvalidXLogRecPtr)
-       {
-               XLogRecPtr      receivePtr;
-               XLogRecPtr      replayPtr;
-               TimeLineID      replayTLI;
-               XLogRecPtr      endptr;
-               XLogSegNo       _logSegNo;
-
-               /* Update the average distance between checkpoints/restartpoints. */
                UpdateCheckPointDistanceEstimate(RedoRecPtr - PriorRedoPtr);
 
-               XLByteToSeg(PriorRedoPtr, _logSegNo, wal_segment_size);
-
-               /*
-                * Get the current end of xlog replayed or received, whichever is
-                * later.
-                */
-               receivePtr = GetWalRcvWriteRecPtr(NULL, NULL);
-               replayPtr = GetXLogReplayRecPtr(&replayTLI);
-               endptr = (receivePtr < replayPtr) ? replayPtr : receivePtr;
+       /*
+        * Delete old log files, those no longer needed for last restartpoint to
+        * prevent the disk holding the xlog from growing full.
+        */
+       XLByteToSeg(RedoRecPtr, _logSegNo, wal_segment_size);
 
-               KeepLogSeg(endptr, &_logSegNo);
-               _logSegNo--;
+       /*
+        * Retreat _logSegNo using the current end of xlog replayed or received,
+        * whichever is later.
+        */
+       receivePtr = GetWalRcvWriteRecPtr(NULL, NULL);
+       replayPtr = GetXLogReplayRecPtr(&replayTLI);
+       endptr = (receivePtr < replayPtr) ? replayPtr : receivePtr;
+       KeepLogSeg(endptr, &_logSegNo);
+       _logSegNo--;
 
-               /*
-                * Try to recycle segments on a useful timeline. If we've been
-                * promoted since the beginning of this restartpoint, use the new
-                * timeline chosen at end of recovery (RecoveryInProgress() sets
-                * ThisTimeLineID in that case). If we're still in recovery, use the
-                * timeline we're currently replaying.
-                *
-                * There is no guarantee that the WAL segments will be useful on the
-                * current timeline; if recovery proceeds to a new timeline right
-                * after this, the pre-allocated WAL segments on this timeline will
-                * not be used, and will go wasted until recycled on the next
-                * restartpoint. We'll live with that.
-                */
-               if (RecoveryInProgress())
-                       ThisTimeLineID = replayTLI;
+       /*
+        * Try to recycle segments on a useful timeline. If we've been promoted
+        * since the beginning of this restartpoint, use the new timeline chosen
+        * at end of recovery (RecoveryInProgress() sets ThisTimeLineID in that
+        * case). If we're still in recovery, use the timeline we're currently
+        * replaying.
+        *
+        * There is no guarantee that the WAL segments will be useful on the
+        * current timeline; if recovery proceeds to a new timeline right after
+        * this, the pre-allocated WAL segments on this timeline will not be used,
+        * and will go wasted until recycled on the next restartpoint. We'll live
+        * with that.
+        */
+       if (RecoveryInProgress())
+               ThisTimeLineID = replayTLI;
 
-               RemoveOldXlogFiles(_logSegNo, PriorRedoPtr, endptr);
+       RemoveOldXlogFiles(_logSegNo, RedoRecPtr, endptr);
 
-               /*
-                * Make more log segments if needed.  (Do this after recycling old log
-                * segments, since that may supply some of the needed files.)
-                */
-               PreallocXlogFiles(endptr);
+       /*
+        * Make more log segments if needed.  (Do this after recycling old log
+        * segments, since that may supply some of the needed files.)
+        */
+       PreallocXlogFiles(endptr);
 
-               /*
-                * ThisTimeLineID is normally not set when we're still in recovery.
-                * However, recycling/preallocating segments above needed
-                * ThisTimeLineID to determine which timeline to install the segments
-                * on. Reset it now, to restore the normal state of affairs for
-                * debugging purposes.
-                */
-               if (RecoveryInProgress())
-                       ThisTimeLineID = 0;
-       }
+       /*
+        * ThisTimeLineID is normally not set when we're still in recovery.
+        * However, recycling/preallocating segments above needed ThisTimeLineID
+        * to determine which timeline to install the segments on. Reset it now,
+        * to restore the normal state of affairs for debugging purposes.
+        */
+       if (RecoveryInProgress())
+               ThisTimeLineID = 0;
 
        /*
         * Truncate pg_subtrans if possible.  We can throw away all data before