Prevent archive recovery from scanning non-existent WAL files.
authorFujii Masao <fujii@postgresql.org>
Tue, 7 Apr 2020 15:49:29 +0000 (00:49 +0900)
committerFujii Masao <fujii@postgresql.org>
Sat, 9 May 2020 03:19:47 +0000 (12:19 +0900)
Previously when there were multiple timelines listed in the history file
of the recovery target timeline, archive recovery searched all of them,
starting from the newest timeline to the oldest one, to find the segment
to read. That is, archive recovery had to continuously fail scanning
the segment until it reached the timeline that the segment belonged to.
These scans for non-existent segment could be harmful on the recovery
performance especially when archival area was located on the remote
storage and each scan could take a long time.

To address the issue, this commit changes archive recovery so that
it skips scanning the timeline that the segment to read doesn't belong to.

Per discussion, back-patch to all supported versions.

Author: Kyotaro Horiguchi, tweaked a bit by Fujii Masao
Reviewed-by: David Steele, Pavel Suderevsky, Grigory Smolkin
Discussion: https://postgr.es/m/16159-f5a34a3a04dc67e0@postgresql.org
Discussion: https://postgr.es/m/20200129.120222.1476610231001551715.horikyota.ntt@gmail.com

src/backend/access/transam/xlog.c

index a6cdb0024fa0209c70c96b7da9ac98543ae905c7..b3270c70e2f714d49af5af8ce5ab93f4bb113160 100644 (file)
@@ -3476,11 +3476,36 @@ XLogFileReadAnyTLI(XLogSegNo segno, int emode, int source)
 
    foreach(cell, tles)
    {
-       TimeLineID  tli = ((TimeLineHistoryEntry *) lfirst(cell))->tli;
+       TimeLineHistoryEntry *hent = (TimeLineHistoryEntry *) lfirst(cell);
+       TimeLineID  tli = hent->tli;
 
        if (tli < curFileTLI)
            break;              /* don't bother looking at too-old TLIs */
 
+       /*
+        * Skip scanning the timeline ID that the logfile segment to read
+        * doesn't belong to
+        */
+       if (hent->begin != InvalidXLogRecPtr)
+       {
+           XLogSegNo   beginseg = 0;
+
+           XLByteToSeg(hent->begin, beginseg);
+
+           /*
+            * The logfile segment that doesn't belong to the timeline is
+            * older or newer than the segment that the timeline started or
+            * ended at, respectively. It's sufficient to check only the
+            * starting segment of the timeline here. Since the timelines are
+            * scanned in descending order in this loop, any segments newer
+            * than the ending segment should belong to newer timeline and
+            * have already been read before. So it's not necessary to check
+            * the ending segment of the timeline here.
+            */
+           if (segno < beginseg)
+               continue;
+       }
+
        if (source == XLOG_FROM_ANY || source == XLOG_FROM_ARCHIVE)
        {
            fd = XLogFileRead(segno, emode, tli,