agora inbox for [email protected]
help / color / mirror / Atom feedFrom: Bertrand Drouvot <[email protected]>
Subject: [PATCH v1] Allow wal_log_hints to be changed without restart
Date: Sat, 13 Jun 2026 07:20:08 +0000
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
--9ItUckmLlnvfmpcW--
view thread (43+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v1] Allow wal_log_hints to be changed without restart
In-Reply-To: <no-message-id-876769@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox