From 22d5d6bbc093a80688ff4e9d4b5865c8fde37d9c Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 10 Apr 2024 16:48:21 +0900
Subject: [PATCH v3 2/2] Implement consistency check with clean buffers for
 wal_consistency_checking

This extends WAL records to track if a buffer registered in a record was
"clean", aka it is registered without being modified.  This is done by
adding a new flag called BKPIMAGE_CLEAN, recorded when a block uses
REGBUF_NO_CHANGE.

This uses a bit in a record for nothing, so while it is useful for
sanity checks, this should never *ever* be applied as-is.
---
 src/include/access/xlogreader.h           |  3 +++
 src/include/access/xlogrecord.h           |  2 ++
 src/backend/access/rmgrdesc/xlogdesc.c    | 11 +++++++++--
 src/backend/access/transam/xloginsert.c   |  4 ++++
 src/backend/access/transam/xlogreader.c   |  1 +
 src/backend/access/transam/xlogrecovery.c | 15 +++++++++++++++
 6 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 2e9e5f43eb..6e42346c31 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -135,6 +135,7 @@ typedef struct
 	/* Information on full-page image, if any */
 	bool		has_image;		/* has image, even for consistency checking */
 	bool		apply_image;	/* has image that should be restored */
+	bool		clean_image;	/* image cleanly registered  */
 	char	   *bkp_image;
 	uint16		hole_offset;
 	uint16		hole_length;
@@ -424,6 +425,8 @@ extern bool DecodeXLogRecord(XLogReaderState *state,
 	((decoder)->record->blocks[block_id].has_image)
 #define XLogRecBlockImageApply(decoder, block_id)		\
 	((decoder)->record->blocks[block_id].apply_image)
+#define XLogRecBlockImageClean(decoder, block_id)		\
+	((decoder)->record->blocks[block_id].clean_image)
 #define XLogRecHasBlockData(decoder, block_id)		\
 	((decoder)->record->blocks[block_id].has_data)
 
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index b9e5c59fae..c38de8a96e 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -157,6 +157,8 @@ typedef struct XLogRecordBlockImageHeader
 #define BKPIMAGE_HAS_HOLE		0x01	/* page image has "hole" */
 #define BKPIMAGE_APPLY			0x02	/* page image should be restored
 										 * during replay */
+#define BKPIMAGE_CLEAN			0x20	/* clean buffer registered */
+
 /* compression methods supported */
 #define BKPIMAGE_COMPRESS_PGLZ	0x04
 #define BKPIMAGE_COMPRESS_LZ4	0x08
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index e455400716..f045dfe8a0 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -272,8 +272,10 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 						method = "unknown";
 
 					appendStringInfo(buf,
-									 " (FPW%s); hole: offset: %u, length: %u, "
+									 " (FPW%s%s); hole: offset: %u, length: %u, "
 									 "compression saved: %u, method: %s",
+									 XLogRecBlockImageClean(record, block_id) ?
+									 "" : " clean",
 									 XLogRecBlockImageApply(record, block_id) ?
 									 "" : " for WAL verification",
 									 XLogRecGetBlock(record, block_id)->hole_offset,
@@ -286,7 +288,9 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 				else
 				{
 					appendStringInfo(buf,
-									 " (FPW%s); hole: offset: %u, length: %u",
+									 " (FPW%s%s); hole: offset: %u, length: %u",
+									 XLogRecBlockImageClean(record, block_id) ?
+									 "" : " clean",
 									 XLogRecBlockImageApply(record, block_id) ?
 									 "" : " for WAL verification",
 									 XLogRecGetBlock(record, block_id)->hole_offset,
@@ -329,6 +333,9 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 					appendStringInfoString(buf, " FPW");
 				else
 					appendStringInfoString(buf, " FPW for WAL verification");
+
+				if (XLogRecBlockImageClean(record, block_id))
+					appendStringInfoString(buf, " clean");
 			}
 		}
 	}
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 9047601534..227c83c477 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -720,6 +720,10 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			if (needs_backup)
 				bimg.bimg_info |= BKPIMAGE_APPLY;
 
+			/* If buffer is clean, register this information */
+			if (regbuf->flags & REGBUF_NO_CHANGE)
+				bimg.bimg_info |= BKPIMAGE_CLEAN;
+
 			if (is_compressed)
 			{
 				/* The current compression is stored in the WAL record */
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 37d2a57961..6737cbe9e3 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1791,6 +1791,7 @@ DecodeXLogRecord(XLogReaderState *state,
 				COPY_HEADER_FIELD(&blk->bimg_info, sizeof(uint8));
 
 				blk->apply_image = ((blk->bimg_info & BKPIMAGE_APPLY) != 0);
+				blk->clean_image = ((blk->bimg_info & BKPIMAGE_CLEAN) != 0);
 
 				if (BKPIMAGE_COMPRESSED(blk->bimg_info))
 				{
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index b2fe2d04cc..98ecb85c0b 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -2551,6 +2551,21 @@ verifyBackupPageConsistency(XLogReaderState *record)
 					(errcode(ERRCODE_INTERNAL_ERROR),
 					 errmsg_internal("%s", record->errormsg_buf)));
 
+		/*
+		 * If the replayed block was registered while clean, check that its
+		 * LSN matches with the copy coming from the primary.
+		 */
+		if (XLogRecBlockImageClean(record, block_id))
+		{
+			XLogRecPtr	primary_lsn = PageGetLSN((Page) primary_image_masked);
+			XLogRecPtr	replay_lsn = PageGetLSN((Page) replay_image_masked);
+
+			if (primary_lsn != replay_lsn)
+				elog(FATAL, "inconsistent page LSN replayed %X/%X primary %X/%X",
+					 LSN_FORMAT_ARGS(replay_lsn),
+					 LSN_FORMAT_ARGS(primary_lsn));
+		}
+
 		/*
 		 * If masking function is defined, mask both the primary and replay
 		 * images
-- 
2.43.0

