From ee1d1eabd7cce8217258a85cc43736340eff7005 Mon Sep 17 00:00:00 2001 From: Zsolt Parragi Date: Thu, 2 Jul 2026 17:48:55 +0000 Subject: [PATCH 1/3] Add durable unlogged-reset generation counter Bump a control-file counter whenever crash recovery or pg_resetwal resets unlogged relations, and mirror it in shared memory. Add GetUnloggedPopulatedEpoch() = (timeline << 32) | reset-gen to stamp when an unlogged matview was populated. --- src/backend/access/transam/xlog.c | 43 +++++++++++++++++++++++++ src/bin/pg_controldata/pg_controldata.c | 2 ++ src/bin/pg_resetwal/pg_resetwal.c | 8 +++++ src/include/access/xlog.h | 2 ++ src/include/catalog/pg_control.h | 5 ++- 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a81912b7441..60c05ee9ce4 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -469,6 +469,9 @@ typedef struct XLogCtlData /* Fake LSN counter, for unlogged relations. */ pg_atomic_uint64 unloggedLSN; + /* Shared-memory mirror of ControlFile->unloggedResetGen. */ + pg_atomic_uint64 unloggedResetGen; + /* Time and LSN of last xlog segment switch. Protected by WALWriteLock. */ pg_time_t lastSegSwitchTime; XLogRecPtr lastSegSwitchLSN; @@ -4273,6 +4276,7 @@ InitControlFile(uint64 sysidentifier, uint32 data_checksum_version) memcpy(ControlFile->mock_authentication_nonce, mock_auth_nonce, MOCK_AUTH_NONCE_LEN); ControlFile->state = DB_SHUTDOWNED; ControlFile->unloggedLSN = FirstNormalUnloggedLSN; + ControlFile->unloggedResetGen = 0; /* Set important parameter values for use when replaying WAL */ ControlFile->MaxConnections = MaxConnections; @@ -5009,6 +5013,29 @@ GetFakeLSNForUnloggedRel(void) return pg_atomic_fetch_add_u64(&XLogCtl->unloggedLSN, 1); } +/* Cluster-wide unlogged-reset generation, read from the shmem mirror. */ +uint64 +GetUnloggedResetGeneration(void) +{ + return pg_atomic_read_u64(&XLogCtl->unloggedResetGen); +} + +/* + * Epoch stamped into pg_class.relpopulated for a populated unlogged matview: + * (timeline << 32) | reset-gen. Crash recovery bumps the gen and + * promotion/PITR changes the timeline, so either invalidates old stamps. + * tli >= 1 keeps the epoch above the reserved values 0 and 1. + */ +uint64 +GetUnloggedPopulatedEpoch(void) +{ + uint64 gen = GetUnloggedResetGeneration(); + TimeLineID tli = GetWALInsertionTimeLine(); + + Assert(tli >= 1); + return ((uint64) tli << 32) | (uint32) gen; +} + /* * Auto-tune the number of XLOG buffers. * @@ -5435,6 +5462,7 @@ XLOGShmemInit(void *arg) pg_atomic_init_u64(&XLogCtl->logWriteResult, InvalidXLogRecPtr); pg_atomic_init_u64(&XLogCtl->logFlushResult, InvalidXLogRecPtr); pg_atomic_init_u64(&XLogCtl->unloggedLSN, InvalidXLogRecPtr); + pg_atomic_init_u64(&XLogCtl->unloggedResetGen, 0); } /* @@ -6073,6 +6101,10 @@ StartupXLOG(void) pg_atomic_write_membarrier_u64(&XLogCtl->unloggedLSN, FirstNormalUnloggedLSN); + /* Mirror the durable unlogged-reset counter into shared memory. */ + pg_atomic_write_membarrier_u64(&XLogCtl->unloggedResetGen, + ControlFile->unloggedResetGen); + /* * Copy any missing timeline history files between 'now' and the recovery * target timeline from archive to pg_wal. While we don't need those files @@ -6347,6 +6379,17 @@ StartupXLOG(void) if (InRecovery) ResetUnloggedRelations(UNLOGGED_RELATION_INIT); + /* + * Crash recovery reset all unlogged relations; bump the durable reset + * generation and refresh the mirror. ControlFile is persisted below. + */ + if (InRecovery) + { + ControlFile->unloggedResetGen++; + pg_atomic_write_membarrier_u64(&XLogCtl->unloggedResetGen, + ControlFile->unloggedResetGen); + } + /* * Pre-scan prepared transactions to find out the range of XIDs present. * This information is not quite needed yet, but it is positioned here so diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index fe5fc5ec133..07a7edb07ab 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -293,6 +293,8 @@ main(int argc, char *argv[]) ckpttime_str); printf(_("Fake LSN counter for unlogged rels: %X/%08X\n"), LSN_FORMAT_ARGS(ControlFile->unloggedLSN)); + printf(_("Unlogged reset generation: " UINT64_FORMAT "\n"), + ControlFile->unloggedResetGen); printf(_("Minimum recovery ending location: %X/%08X\n"), LSN_FORMAT_ARGS(ControlFile->minRecoveryPoint)); printf(_("Min recovery ending loc's timeline: %u\n"), diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index f8d25afed9d..ec3fc2b8717 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -712,6 +712,7 @@ GuessControlValues(void) ControlFile.time = (pg_time_t) time(NULL); ControlFile.checkPoint = ControlFile.checkPointCopy.redo; ControlFile.unloggedLSN = FirstNormalUnloggedLSN; + ControlFile.unloggedResetGen = 0; /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */ @@ -913,6 +914,13 @@ RewriteControlFile(void) ControlFile.state = DB_SHUTDOWNED; ControlFile.checkPoint = ControlFile.checkPointCopy.redo; + + /* + * WAL reset skips crash recovery, so unlogged storage is suspect. Bump + * the reset generation so pre-reset epoch stamps read as "not populated". + */ + ControlFile.unloggedResetGen++; + ControlFile.minRecoveryPoint = InvalidXLogRecPtr; ControlFile.minRecoveryPointTLI = 0; ControlFile.backupStartPoint = InvalidXLogRecPtr; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 4dd98624204..a77550bfbe8 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -261,6 +261,8 @@ extern void InitLocalDataChecksumState(void); extern void SetLocalDataChecksumState(uint32 data_checksum_version); extern bool GetDefaultCharSignedness(void); extern XLogRecPtr GetFakeLSNForUnloggedRel(void); +extern uint64 GetUnloggedResetGeneration(void); +extern uint64 GetUnloggedPopulatedEpoch(void); extern void BootStrapXLOG(uint32 data_checksum_version); extern void InitializeWalConsistencyChecking(void); extern void LocalProcessControlFile(bool reset); diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 80b3a730e03..8ff1faf3994 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -22,7 +22,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 1902 +#define PG_CONTROL_VERSION 1903 /* Nonce key length, see below */ #define MOCK_AUTH_NONCE_LEN 32 @@ -143,6 +143,9 @@ typedef struct ControlFileData CheckPoint checkPointCopy; /* copy of last check point record */ XLogRecPtr unloggedLSN; /* current fake LSN value, for unlogged rels */ + uint64 unloggedResetGen; /* bumped whenever unlogged relations are + * reset (end of recovery) or become + * unsafe to trust (pg_resetwal) */ /* * These two values determine the minimum point we must recover up to -- 2.43.0