aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/transam/xlogrecovery.c
diff options
context:
space:
mode:
authorMichael Paquier <michael@paquier.xyz>2025-01-20 09:30:37 +0900
committerMichael Paquier <michael@paquier.xyz>2025-01-20 09:30:37 +0900
commit26554faccc975fbd23b7cc04c0b10079e5c839c9 (patch)
tree6998d284ae516e409f3d5703eb010f9fbc03e52f /src/backend/access/transam/xlogrecovery.c
parent9b136b0f2e531e41d1949fba3f618405adfcef02 (diff)
downloadpostgresql-26554faccc975fbd23b7cc04c0b10079e5c839c9.tar.gz
postgresql-26554faccc975fbd23b7cc04c0b10079e5c839c9.zip
Fix header check for continuation records where standbys could be stuck
XLogPageRead() checks immediately for an invalid WAL record header on a standby, to be able to handle the case of continuation records that need to be read across two different sources. As written, the check was too generic, applying to any target LSN. Based on an analysis by Kyotaro Horiguchi, what really matters is to make sure that the page header is checked when attempting to read a LSN at the boundary of a segment, to handle the case of a continuation record that spawns across multiple pages when dealing with multiple segments, as WAL receivers are spawned they request WAL from the beginning of a segment. This fix has been proposed by Kyotaro Horiguchi. This could cause standbys to loop infinitely when dealing with a continuation record during a timeline jump, in the case where the contents of the record in the follow-up page are invalid. Some regression tests are added to check such scenarios, able to reproduce the original problem. In the test, the contents of a continuation record are overwritten with junk zeros on its follow-up page, and replayed on standbys. This is inspired by 039_end_of_wal.pl, and is enough to show how standbys should react on promotion by not being stuck. Without the fix, the test would fail with a timeout. The test to reproduce the problem has been written by Alexander Kukushkin. The original check has been introduced in 066871980183, for a similar problem. Author: Kyotaro Horiguchi, Alexander Kukushkin Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/CAFh8B=mozC+e1wGJq0H=0O65goZju+6ab5AU7DEWCSUA2OtwDg@mail.gmail.com Backpatch-through: 13
Diffstat (limited to 'src/backend/access/transam/xlogrecovery.c')
-rw-r--r--src/backend/access/transam/xlogrecovery.c13
1 files changed, 7 insertions, 6 deletions
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 1503b216713..bbc19df1921 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -3361,12 +3361,12 @@ retry:
* validates the page header anyway, and would propagate the failure up to
* ReadRecord(), which would retry. However, there's a corner case with
* continuation records, if a record is split across two pages such that
- * we would need to read the two pages from different sources. For
- * example, imagine a scenario where a streaming replica is started up,
- * and replay reaches a record that's split across two WAL segments. The
- * first page is only available locally, in pg_wal, because it's already
- * been recycled on the primary. The second page, however, is not present
- * in pg_wal, and we should stream it from the primary. There is a
+ * we would need to read the two pages from different sources across two
+ * WAL segments.
+ *
+ * The first page is only available locally, in pg_wal, because it's
+ * already been recycled on the primary. The second page, however, is not
+ * present in pg_wal, and we should stream it from the primary. There is a
* recycled WAL segment present in pg_wal, with garbage contents, however.
* We would read the first page from the local WAL segment, but when
* reading the second page, we would read the bogus, recycled, WAL
@@ -3388,6 +3388,7 @@ retry:
* responsible for the validation.
*/
if (StandbyMode &&
+ (targetPagePtr % wal_segment_size) == 0 &&
!XLogReaderValidatePageHeader(xlogreader, targetPagePtr, readBuf))
{
/*