diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6044c1a6a09..3d6046eacfd 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); @@ -3949,7 +3950,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 */ } @@ -4212,7 +4213,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; @@ -4307,18 +4308,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; } @@ -10462,7 +10476,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; } @@ -10484,7 +10498,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);