fix-segment-reread-after-tli-switch-1.patch
text/x-diff
Filename: fix-segment-reread-after-tli-switch-1.patch
Type: text/x-diff
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 23 | 9 |
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 8614907..045d21d 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -572,6 +572,7 @@ static uint32 readRecordBufSize = 0;
static XLogRecPtr ReadRecPtr; /* start of last record read */
static XLogRecPtr EndRecPtr; /* end+1 of last record read */
static TimeLineID lastPageTLI = 0;
+static TimeLineID lastSegmentTLI = 0;
static XLogRecPtr minRecoveryPoint; /* local copy of
* ControlFile->minRecoveryPoint */
@@ -655,7 +656,7 @@ static void CleanupBackupHistory(void);
static void UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force);
static XLogRecord *ReadRecord(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt);
static void CheckRecoveryConsistency(void);
-static bool ValidXLOGHeader(XLogPageHeader hdr, int emode);
+static bool ValidXLOGHeader(XLogPageHeader hdr, int emode, bool segmentonly);
static XLogRecord *ReadCheckpointRecord(XLogRecPtr RecPtr, int whichChkpt);
static List *readTimeLineHistory(TimeLineID targetTLI);
static bool existsTimeLineHistory(TimeLineID probeTLI);
@@ -3927,7 +3928,7 @@ ReadRecord(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt)
* to go backwards (but we can't reset that variable right here, since
* we might not change files at all).
*/
- lastPageTLI = 0; /* see comment in ValidXLOGHeader */
+ lastPageTLI = lastSegmentTLI = 0; /* see comment in ValidXLOGHeader */
randAccess = true; /* allow curFileTLI to go backwards too */
}
@@ -4190,7 +4191,7 @@ next_record_is_invalid:
* ReadRecord. It's not intended for use from anywhere else.
*/
static bool
-ValidXLOGHeader(XLogPageHeader hdr, int emode)
+ValidXLOGHeader(XLogPageHeader hdr, int emode, bool segmentonly)
{
XLogRecPtr recaddr;
@@ -4285,18 +4286,31 @@ ValidXLOGHeader(XLogPageHeader hdr, int emode)
* successive pages of a consistent WAL sequence.
*
* Of course this check should only be applied when advancing sequentially
- * across pages; therefore ReadRecord resets lastPageTLI to zero when
- * going to a random page.
+ * across pages; therefore ReadRecord resets lastPageTLI and
+ * lastSegmentTLI to zero when going to a random page.
+ *
+ * Sometimes we re-open a segment that's already been partially replayed.
+ * In that case we cannot perform the normal TLI check: if there is a
+ * timeline switch within the segment, the first page has a smaller TLI
+ * than later pages following the timeline switch, and we might've read
+ * them already. As a weaker test, we still check that it's not smaller
+ * than the TLI we last saw at the beginning of a segment. Pass
+ * segmentonly = true when re-validating the first page like that, and the
+ * page you're actually interested in comes later.
*/
- if (hdr->xlp_tli < lastPageTLI)
+ if (hdr->xlp_tli < (segmentonly ? lastSegmentTLI : lastPageTLI))
{
ereport(emode_for_corrupt_record(emode, recaddr),
(errmsg("out-of-sequence timeline ID %u (after %u) in log file %u, segment %u, offset %u",
- hdr->xlp_tli, lastPageTLI,
+ hdr->xlp_tli,
+ segmentonly ? lastSegmentTLI : lastPageTLI,
readId, readSeg, readOff)));
return false;
}
lastPageTLI = hdr->xlp_tli;
+ if (readOff == 0)
+ lastSegmentTLI = hdr->xlp_tli;
+
return true;
}
@@ -10440,7 +10454,7 @@ retry:
readId, readSeg, readOff)));
goto next_record_is_invalid;
}
- if (!ValidXLOGHeader((XLogPageHeader) readBuf, emode))
+ if (!ValidXLOGHeader((XLogPageHeader) readBuf, emode, true))
goto next_record_is_invalid;
}
@@ -10462,7 +10476,7 @@ retry:
readId, readSeg, readOff)));
goto next_record_is_invalid;
}
- if (!ValidXLOGHeader((XLogPageHeader) readBuf, emode))
+ if (!ValidXLOGHeader((XLogPageHeader) readBuf, emode, false))
goto next_record_is_invalid;
Assert(targetId == readId);