v1-0001-Fix-promoted-standby-reads-of-unlogged-sequences.patch
application/octet-stream
Filename: v1-0001-Fix-promoted-standby-reads-of-unlogged-sequences.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v1-0001
Subject: Fix promoted-standby reads of unlogged sequences
| File | + | − |
|---|---|---|
| src/backend/access/hash/hash_xlog.c | 3 | 26 |
| src/backend/access/transam/xlogutils.c | 25 | 1 |
| src/backend/commands/sequence_xlog.c | 1 | 0 |
| src/include/access/xlogutils.h | 2 | 0 |
From b91ca1dfef3b6b7f4fbbe87ddd86d1f2939ef7f8 Mon Sep 17 00:00:00 2001
From: Fujii Masao <fujii@postgresql.org>
Date: Wed, 24 Jun 2026 18:42:43 +0900
Subject: [PATCH v1] Fix promoted-standby reads of unlogged sequences
---
src/backend/access/hash/hash_xlog.c | 29 +++-----------------------
src/backend/access/transam/xlogutils.c | 26 ++++++++++++++++++++++-
src/backend/commands/sequence_xlog.c | 1 +
src/include/access/xlogutils.h | 2 ++
4 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/src/backend/access/hash/hash_xlog.c b/src/backend/access/hash/hash_xlog.c
index 2060620c7de..e9a2b9aa9a7 100644
--- a/src/backend/access/hash/hash_xlog.c
+++ b/src/backend/access/hash/hash_xlog.c
@@ -29,7 +29,6 @@ hash_xlog_init_meta_page(XLogReaderState *record)
XLogRecPtr lsn = record->EndRecPtr;
Page page;
Buffer metabuf;
- ForkNumber forknum;
xl_hash_init_meta_page *xlrec = (xl_hash_init_meta_page *) XLogRecGetData(record);
@@ -41,16 +40,7 @@ hash_xlog_init_meta_page(XLogReaderState *record)
page = BufferGetPage(metabuf);
PageSetLSN(page, lsn);
MarkBufferDirty(metabuf);
-
- /*
- * Force the on-disk state of init forks to always be in sync with the
- * state in shared buffers. See XLogReadBufferForRedoExtended. We need
- * special handling for init forks as create index operations don't log a
- * full page image of the metapage.
- */
- XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
- if (forknum == INIT_FORKNUM)
- FlushOneBuffer(metabuf);
+ XLogFlushBufferForRedoIfInit(record, 0, metabuf);
/* all done */
UnlockReleaseBuffer(metabuf);
@@ -68,7 +58,6 @@ hash_xlog_init_bitmap_page(XLogReaderState *record)
Page page;
HashMetaPage metap;
uint32 num_buckets;
- ForkNumber forknum;
xl_hash_init_bitmap_page *xlrec = (xl_hash_init_bitmap_page *) XLogRecGetData(record);
@@ -79,16 +68,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record)
_hash_initbitmapbuffer(bitmapbuf, xlrec->bmsize, true);
PageSetLSN(BufferGetPage(bitmapbuf), lsn);
MarkBufferDirty(bitmapbuf);
-
- /*
- * Force the on-disk state of init forks to always be in sync with the
- * state in shared buffers. See XLogReadBufferForRedoExtended. We need
- * special handling for init forks as create index operations don't log a
- * full page image of the metapage.
- */
- XLogRecGetBlockTag(record, 0, NULL, &forknum, NULL);
- if (forknum == INIT_FORKNUM)
- FlushOneBuffer(bitmapbuf);
+ XLogFlushBufferForRedoIfInit(record, 0, bitmapbuf);
UnlockReleaseBuffer(bitmapbuf);
/* add the new bitmap page to the metapage's list of bitmaps */
@@ -109,10 +89,7 @@ hash_xlog_init_bitmap_page(XLogReaderState *record)
PageSetLSN(page, lsn);
MarkBufferDirty(metabuf);
-
- XLogRecGetBlockTag(record, 1, NULL, &forknum, NULL);
- if (forknum == INIT_FORKNUM)
- FlushOneBuffer(metabuf);
+ XLogFlushBufferForRedoIfInit(record, 1, metabuf);
}
if (BufferIsValid(metabuf))
UnlockReleaseBuffer(metabuf);
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index fdc341d8fa4..d8c179c5dcc 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -321,6 +321,28 @@ XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)
return buf;
}
+/*
+ * If a redo routine modified an init fork, flush the buffer immediately.
+ *
+ * At the end of crash recovery the init forks of unlogged relations are
+ * copied to the main fork directly from disk, without going through shared
+ * buffers. Therefore, redo routines that update init forks without
+ * restoring a full-page image must call this after setting the page LSN and
+ * marking the buffer dirty.
+ */
+void
+XLogFlushBufferForRedoIfInit(XLogReaderState *record, uint8 block_id,
+ Buffer buffer)
+{
+ ForkNumber forknum;
+
+ Assert(BufferIsValid(buffer));
+
+ XLogRecGetBlockTag(record, block_id, NULL, &forknum, NULL);
+ if (forknum == INIT_FORKNUM)
+ FlushOneBuffer(buffer);
+}
+
/*
* XLogReadBufferForRedoExtended
* Like XLogReadBufferForRedo, but with extra options.
@@ -398,7 +420,9 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
* At the end of crash recovery the init forks of unlogged relations
* are copied, without going through shared buffers. So we need to
* force the on-disk state of init forks to always be in sync with the
- * state in shared buffers.
+ * state in shared buffers. Use XLogFlushBufferForRedoIfInit() for
+ * redo routines that dirty init-fork buffers without restoring a
+ * full-page image.
*/
if (forknum == INIT_FORKNUM)
FlushOneBuffer(*buf);
diff --git a/src/backend/commands/sequence_xlog.c b/src/backend/commands/sequence_xlog.c
index d0aed48e268..fcb3230cf3b 100644
--- a/src/backend/commands/sequence_xlog.c
+++ b/src/backend/commands/sequence_xlog.c
@@ -63,6 +63,7 @@ seq_redo(XLogReaderState *record)
memcpy(page, localpage, BufferGetPageSize(buffer));
MarkBufferDirty(buffer);
+ XLogFlushBufferForRedoIfInit(record, 0, buffer);
UnlockReleaseBuffer(buffer);
pfree(localpage);
diff --git a/src/include/access/xlogutils.h b/src/include/access/xlogutils.h
index b97387c6d4c..0c6c7410069 100644
--- a/src/include/access/xlogutils.h
+++ b/src/include/access/xlogutils.h
@@ -87,6 +87,8 @@ typedef struct ReadLocalXLogPageNoWaitPrivate
extern XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record,
uint8 block_id, Buffer *buf);
extern Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id);
+extern void XLogFlushBufferForRedoIfInit(XLogReaderState *record,
+ uint8 block_id, Buffer buffer);
extern XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record,
uint8 block_id,
ReadBufferMode mode, bool get_cleanup_lock,
--
2.53.0