Hi hackers, As f19c0ecca introduced online enabling and disabling of data checksums, I think that it would make sense to do the same for wal_log_hints. Indeed, it can be used to test how much extra WAL-logging would occur if your database had data checksums enabled. Since 04bec894a04, data checksums is on by default, but it could still be set to false due to user choice or after an upgrade.
In that case, I think that it sounds weird to ask for a restart to set wal_log_hints to test the impact of data checksums that does not require a restart anymore. I think that the entire flow (test + enable or disable) should be done without a restart (not just a portion of it). So, PFA a patch to $SUBJECT. Basically, it: - Changes wal_log_hints from PGC_POSTMASTER to PGC_SIGHUP. - uses the same pattern as full_page_writes as it does not need the complexity of procsignal barriers that has been used in f19c0ecca. Indeed, it's not a multi state transition and no all backends must agree simultaneously. The key concern is that when turning wal_log_hints OFF, no backend should stop WAL-logging hint bit updates before the parameter change is itself WAL-logged. Simply propagating the GUC via SIGHUP would leave a window where a backend could acknowledge the change before the WAL record is written. To address this, the patch introduces a shared memory flag (XLogCtl->walLogHints) that serves as the authoritative value read by all backends via XLogHintBitIsNeeded(). Only the checkpointer writes this flag, using the same ordering pattern as UpdateFullPageWrites(): - When enabling: set the shared flag to true first, then WAL-log the parameter change. Backends immediately start WAL-logging hints and the extra WAL before the record is harmless. - When disabling: WAL-log the parameter change first, then set the shared flag to false. As in commit 3b682df3260 (which added the same pattern to UpdateFullPageWrites()), a critical section is used as extra protection to ensure consistency between the flag and the WAL record, even though the ordering makes both failure directions harmless (extra WAL-logging). As Michael noted in the original thread [1], pg_rewind only needs wal_log_hints to be on "when WAL forked": it only scans WAL from the fork point forward. Toggling wal_log_hints off and back on before a timeline divergence does not create a gap, because hint-bit changes from before the fork are irrelevant to pg_rewind. The pg_rewind check on ControlFile->wal_log_hints (which reflects the current state at rewind time) remains sufficient. [1] https://postgr.es/m/cab7npqsxys4jg-kjmy8xim4stqkgkvhydrcoojhxzrskiw5...@mail.gmail.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
>From e1729c994aed9baa87e63875f6567b34f6d06b98 Mon Sep 17 00:00:00 2001 From: Bertrand Drouvot <[email protected]> Date: Sat, 13 Jun 2026 07:20:08 +0000 Subject: [PATCH v1] Allow wal_log_hints to be changed without restart Change wal_log_hints from PGC_POSTMASTER to PGC_SIGHUP, allowing it to be changed without restarting the server. As f19c0ecca introduced online enabling and disabling of data checksums, it makes sense to do the same for wal_log_hints (as it can be used to test how much extra WAL-logging would occur if your database had data checksums enabled). This commit: - Changes wal_log_hints from PGC_POSTMASTER to PGC_SIGHUP. - uses the same pattern as full_page_writes as it does not need the complexity of procsignal barriers that has been used in f19c0ecca. Indeed, it's not a multi state transition and no all backends must agree simultaneously. The key concern is that when turning wal_log_hints OFF, no backend should stop WAL-logging hint bit updates before the parameter change is itself WAL-logged. Simply propagating the GUC via SIGHUP would leave a window where a backend could acknowledge the change before the WAL record is written. To address this, we introduce a shared memory flag (XLogCtl->walLogHints) that serves as the authoritative value read by all backends via XLogHintBitIsNeeded(). Only the checkpointer writes this flag, using the same ordering pattern as UpdateFullPageWrites(): - When enabling: set the shared flag to true first, then WAL-log the parameter change. Backends immediately start WAL-logging hints and the extra WAL before the record is harmless. - When disabling: WAL-log the parameter change first, then set the shared flag to false. As in commit 3b682df3260 (which added the same pattern to UpdateFullPageWrites()), a critical section is used as extra protection to ensure consistency between the flag and the WAL record, even though the ordering makes both failure directions harmless (extra WAL-logging). Author: Bertrand Drouvot <[email protected]> Reviewed-by: Discussion: --- doc/src/sgml/config.sgml | 4 +- src/backend/access/transam/xlog.c | 82 +++++++++++++++++++++++ src/backend/postmaster/checkpointer.c | 6 ++ src/backend/utils/misc/guc_parameters.dat | 2 +- src/bin/pg_rewind/t/012_wal_log_hints.pl | 77 +++++++++++++++++++++ src/include/access/xlog.h | 3 +- 6 files changed, 171 insertions(+), 3 deletions(-) 5.0% doc/src/sgml/ 44.8% src/backend/access/transam/ 3.3% src/backend/utils/misc/ 41.4% src/bin/pg_rewind/t/ 5.3% src/ diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index fa566c9e553..ffa6235a5ba 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3646,7 +3646,9 @@ include_dir 'conf.d' </para> <para> - This parameter can only be set at server start. The default value is <literal>off</literal>. + This parameter can only be set in the <filename>postgresql.conf</filename> + file or on the server command line. + The default value is <literal>off</literal>. </para> </listitem> </varlistentry> diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6c2304fef33..f0ba2291433 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -559,6 +559,16 @@ typedef struct XLogCtlData /* last data_checksum_version we've seen */ uint32 data_checksum_version; + /* + * walLogHints is the authoritative value used by all backends to + * determine whether to WAL-log hint bit updates. This shared value, + * instead of the process-local wal_log_hints, is required because, when + * wal_log_hints is changed by SIGHUP, we must ensure proper ordering + * between the WAL parameter change record and the actual behavior change. + * Checkpointer updates it after SIGHUP. + */ + bool walLogHints; + slock_t info_lck; /* locks shared variables shown above */ } XLogCtlData; @@ -4678,6 +4688,23 @@ DataChecksumsNeedWrite(void) LocalDataChecksumState == PG_DATA_CHECKSUM_INPROGRESS_OFF); } +/* + * XLogHintBitIsNeeded + * Returns whether hint bit must be written or not + * + * Returns true if wal_log_hints is enabled in shared memory, or if data + * checksums require writes. The shared memory value is used (rather than + * the process-local wal_log_hints) to ensure all backends observe the change + * atomically with respect to the WAL record that logs the state transition. + */ +bool +XLogHintBitIsNeeded(void) +{ + volatile XLogCtlData *xlogctl = XLogCtl; + + return (xlogctl->walLogHints || DataChecksumsNeedWrite()); +} + bool DataChecksumsOff(void) @@ -5429,6 +5456,9 @@ XLOGShmemInit(void *arg) XLogCtl->data_checksum_version = ControlFile->data_checksum_version; SetLocalDataChecksumState(XLogCtl->data_checksum_version); + /* Use wal_log_hints from control file */ + XLogCtl->walLogHints = ControlFile->wal_log_hints; + SpinLockInit(&XLogCtl->Insert.insertpos_lck); SpinLockInit(&XLogCtl->info_lck); pg_atomic_init_u64(&XLogCtl->logInsertResult, InvalidXLogRecPtr); @@ -6555,6 +6585,9 @@ StartupXLOG(void) Insert->fullPageWrites = lastFullPageWrites; UpdateFullPageWrites(); + /* Update wal_log_hints in shared memory */ + UpdateWalLogHints(); + /* * Emit checkpoint or end-of-recovery record in XLOG, if required. */ @@ -8812,6 +8845,55 @@ UpdateFullPageWrites(void) END_CRIT_SECTION(); } +/* + * Update wal_log_hints in shared memory, and write an + * XLOG_PARAMETER_CHANGE record if necessary. + * + * This follows the same ordering pattern as UpdateFullPageWrites(): + * when setting to true, update shared memory first (so backends start + * WAL-logging hints before the WAL record), and when setting to false, + * write the WAL record first (so the change is logged before backends + * stop WAL-logging hints). + */ +void +UpdateWalLogHints(void) +{ + bool recoveryInProgress; + + if (wal_log_hints == XLogCtl->walLogHints) + return; + + /* + * Perform this outside critical section so that the WAL insert + * initialization done by RecoveryInProgress() doesn't trigger an + * assertion failure. + */ + recoveryInProgress = RecoveryInProgress(); + + START_CRIT_SECTION(); + + /* + * It's always safe to WAL-log hint bits, even when not strictly required, + * but not the other round. So if we're setting wal_log_hints to true, + * first set it true and then write the WAL record. If we're setting it to + * false, first write the WAL record and then set the global flag. + */ + if (wal_log_hints) + XLogCtl->walLogHints = true; + + /* + * XLogReportParameters will WAL-log and update the control file. During + * recovery, we can't write WAL so just update the shared flag. + */ + if (!recoveryInProgress) + XLogReportParameters(); + + if (!wal_log_hints) + XLogCtl->walLogHints = false; + + END_CRIT_SECTION(); +} + /* * XLOG resource manager's routines * diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c index 087120db090..463a24edf55 100644 --- a/src/backend/postmaster/checkpointer.c +++ b/src/backend/postmaster/checkpointer.c @@ -1510,6 +1510,12 @@ UpdateSharedMemoryConfig(void) */ UpdateFullPageWrites(); + /* + * If wal_log_hints has been changed by SIGHUP, we update pg_control and + * write an XLOG_PARAMETER_CHANGE record. + */ + UpdateWalLogHints(); + elog(DEBUG2, "checkpointer updated shared memory configuration values"); } diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat index afaa058b046..1a39fa09ea1 100644 --- a/src/backend/utils/misc/guc_parameters.dat +++ b/src/backend/utils/misc/guc_parameters.dat @@ -3502,7 +3502,7 @@ options => 'wal_level_options', }, -{ name => 'wal_log_hints', type => 'bool', context => 'PGC_POSTMASTER', group => 'WAL_SETTINGS', +{ name => 'wal_log_hints', type => 'bool', context => 'PGC_SIGHUP', group => 'WAL_SETTINGS', short_desc => 'Writes full pages to WAL when first modified after a checkpoint, even for a non-critical modification.', variable => 'wal_log_hints', boot_val => 'false', diff --git a/src/bin/pg_rewind/t/012_wal_log_hints.pl b/src/bin/pg_rewind/t/012_wal_log_hints.pl new file mode 100644 index 00000000000..9eb9c48db7c --- /dev/null +++ b/src/bin/pg_rewind/t/012_wal_log_hints.pl @@ -0,0 +1,77 @@ + +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# +# Test pg_rewind interaction with wal_log_hints: +# - Error out when wal_log_hints=off and no data checksums +# - Succeeds after reload to on +# +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +# Initialize primary without data checksums and with wal_log_hints=off +my $node_primary = PostgreSQL::Test::Cluster->new('primary'); +$node_primary->init(allows_streaming => 1, no_data_checksums => 1); +$node_primary->append_conf( + 'postgresql.conf', qq{ +wal_log_hints = off +wal_level = replica +wal_keep_size = 64MB +autovacuum = off +}); +$node_primary->start; + +$node_primary->safe_psql('postgres', + "CREATE TABLE t(id int); INSERT INTO t SELECT generate_series(1,100)"); +$node_primary->safe_psql('postgres', "CHECKPOINT"); + +# Create standby, diverge +my $node_standby = PostgreSQL::Test::Cluster->new('standby'); +$node_primary->backup('my_backup'); +$node_standby->init_from_backup($node_primary, 'my_backup', + has_streaming => 1); +$node_standby->start; +$node_primary->wait_for_catchup($node_standby); + +$node_standby->promote; +$node_standby->safe_psql('postgres', "INSERT INTO t VALUES (999)"); +$node_primary->safe_psql('postgres', "INSERT INTO t VALUES (888)"); +$node_primary->safe_psql('postgres', "CHECKPOINT"); +$node_primary->stop; + +# pg_rewind must refuse: wal_log_hints=off +command_fails_like( + [ + 'pg_rewind', + '--target-pgdata' => $node_primary->data_dir, + '--source-server' => $node_standby->connstr('postgres'), + '--no-sync', + ], + qr/target server needs to use either data checksums or "wal_log_hints = on"/, + 'pg_rewind refuses with wal_log_hints=off and no data checksums'); + +# Restart primary and enable wal_log_hints via reload +$node_primary->start; +$node_primary->append_conf('postgresql.conf', 'wal_log_hints = on'); +$node_primary->reload; + +my $result = $node_primary->safe_psql('postgres', "SHOW wal_log_hints"); +is($result, 'on', 'wal_log_hints changed to on via reload'); + +$node_primary->stop; + +# Same pg_rewind now succeeds +command_ok( + [ + 'pg_rewind', + '--target-pgdata' => $node_primary->data_dir, + '--source-server' => $node_standby->connstr('postgres'), + '--no-sync', + ], + 'pg_rewind succeeds after enabling wal_log_hints via reload'); + +$node_standby->stop; +done_testing(); diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 4dd98624204..8016eaadb53 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -120,7 +120,7 @@ extern PGDLLIMPORT bool XLogLogicalInfo; * of the bits make it to disk, but the checksum wouldn't match. Also WAL-log * them if forced by wal_log_hints=on. */ -#define XLogHintBitIsNeeded() (wal_log_hints || DataChecksumsNeedWrite()) +extern bool XLogHintBitIsNeeded(void); /* Do we need to WAL-log information required only for Hot Standby and logical replication? */ #define XLogStandbyInfoActive() (wal_level >= WAL_LEVEL_REPLICA) @@ -274,6 +274,7 @@ extern void XLogPutNextOid(Oid nextOid); extern XLogRecPtr XLogRestorePoint(const char *rpName); extern XLogRecPtr XLogAssignLSN(void); extern void UpdateFullPageWrites(void); +extern void UpdateWalLogHints(void); extern void GetFullPageWriteInfo(XLogRecPtr *RedoRecPtr_p, bool *doPageWrites_p); extern XLogRecPtr GetRedoRecPtr(void); extern XLogRecPtr GetInsertRecPtr(void); -- 2.34.1
