From 22d5d6bbc093a80688ff4e9d4b5865c8fde37d9c Mon Sep 17 00:00:00 2001 From: Michael Paquier 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