Hi, On 2021-04-28 17:59:22 -0700, Andres Freund wrote: > I can however say that pg_waldump on the standby's pg_wal does also > fail. The failure as part of the backend is "invalid memory alloc > request size", whereas in pg_waldump I get the much more helpful: > pg_waldump: fatal: error in WAL record at 4/7F5C31C8: record with incorrect > prev-link 416200FF/FF000000 at 4/7F5C3200
There's definitely something broken around continuation records, in XLogFindNextRecord(). Which means that it's not the cause for the server side issue, but obviously still not good. The conversion of XLogFindNextRecord() to be state machine based basically only works in a narrow set of circumstances. Whenever the end of the first record read is on a different page than the start of the record, we'll endlessly loop. We'll go into XLogFindNextRecord(), and return until we've successfully read the page header. Then we'll enter the second loop. Which will try to read until the end of the first record. But after returning the first loop will again ask for page header. Even if that's fixed, the second loop alone has the same problem: As XLogBeginRead() is called unconditionally we'll start read the start of the record, discover that it needs data on a second page, return, and do the same thing again. I think it needs something roughly like the attached. Greetings, Andres Freund
diff --git i/src/include/access/xlogreader.h w/src/include/access/xlogreader.h index 3b8af31a8fe..82a80cf2bf5 100644 --- i/src/include/access/xlogreader.h +++ w/src/include/access/xlogreader.h @@ -297,6 +297,7 @@ struct XLogFindNextRecordState XLogReaderState *reader_state; XLogRecPtr targetRecPtr; XLogRecPtr currRecPtr; + bool found_start; }; /* Report that data is available for decoding. */ diff --git i/src/backend/access/transam/xlogreader.c w/src/backend/access/transam/xlogreader.c index 4277e92d7c9..935c841347f 100644 --- i/src/backend/access/transam/xlogreader.c +++ w/src/backend/access/transam/xlogreader.c @@ -868,7 +868,7 @@ XLogDecodeOneRecord(XLogReaderState *state, bool allow_oversized) /* validate record header if not yet */ if (!state->record_verified && record_len >= SizeOfXLogRecord) { - if (!ValidXLogRecordHeader(state, state->DecodeRecPtr, + if (!ValidXLogRecordHeader(state, state->DecodeRecPtr, state->PrevRecPtr, prec)) goto err; @@ -1516,6 +1516,7 @@ InitXLogFindNextRecord(XLogReaderState *reader_state, XLogRecPtr start_ptr) state->reader_state = reader_state; state->targetRecPtr = start_ptr; state->currRecPtr = start_ptr; + state->found_start = false; return state; } @@ -1545,7 +1546,7 @@ XLogFindNextRecord(XLogFindNextRecordState *state) * skip over potential continuation data, keeping in mind that it may span * multiple pages */ - while (true) + while (!state->found_start) { XLogRecPtr targetPagePtr; int targetRecOff; @@ -1616,7 +1617,12 @@ XLogFindNextRecord(XLogFindNextRecordState *state) * because either we're at the first record after the beginning of a page * or we just jumped over the remaining data of a continuation. */ - XLogBeginRead(state->reader_state, state->currRecPtr); + if (!state->found_start) + { + XLogBeginRead(state->reader_state, state->currRecPtr); + state->found_start = true; + } + while ((result = XLogReadRecord(state->reader_state, &record, &errormsg)) != XLREAD_FAIL) {