Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wZ7Y9-000mvT-0P for pgsql-hackers@arkaria.postgresql.org; Mon, 15 Jun 2026 13:40:25 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wZ7Y7-00C8h3-2k for pgsql-hackers@arkaria.postgresql.org; Mon, 15 Jun 2026 13:40:23 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wZ7Y7-00C8gv-1V for pgsql-hackers@lists.postgresql.org; Mon, 15 Jun 2026 13:40:23 +0000 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wZ7Y4-00000000WeT-3W3x for pgsql-hackers@lists.postgresql.org; Mon, 15 Jun 2026 13:40:22 +0000 Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-49222b6e871so19885865e9.3 for ; Mon, 15 Jun 2026 06:40:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781530819; x=1782135619; darn=lists.postgresql.org; h=content-disposition:mime-version:message-id:subject:to:from:date :from:to:cc:subject:date:message-id:reply-to; bh=TDQrSxGf2aDnaKGKl7y+HBOQiK8IGNTHhZwW4tTUF6w=; b=JNBMGK2HOKv5radZEbvHXYVbaI1k2gMroSdXz0XZ0Ue+G7ORcbR3LTSu3jN+Xpm1qM Yyg6amKMRRKSXBObdgyZm7+5bjNTdJtQlVu04MdeivTxDfqHkw9IIv37d2fifK/QM36K s15HB4frYtgIgq/m47VDi4OvVM/s9uV78mV/bLLI+pxOUCanPC4pJgeHuCMxKTKiy7pm 1AEd1eltn0mzRM0x7z1lrp0BvnMNmuxUoXl+VHJjL7cyDtFJKF9U7QbHTJQmY65F3j9s M2fqcqzbbVnDVN5chB5Jhc7ELqbyGd9eqihEayd+0VeRhuv/b3avas5bwM4bMra8RMNq kOGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781530819; x=1782135619; h=content-disposition:mime-version:message-id:subject:to:from:date :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=TDQrSxGf2aDnaKGKl7y+HBOQiK8IGNTHhZwW4tTUF6w=; b=L8VpcIAFTq3tzqoKHtT33Q5mRlEpOawJ4OWXbMxdhj3XKvy0KOU03Z4hpph4SkaUnW eZfgZptOc2rIs6RunABL3IAZZC2eUTiF3LsHvp3vygJiojCnANn2xDksMLCPaZdh1hk7 W+L/2pjTRr8UoioBEGdyfQzhDrt3YgNj2gohh+uxz54jQwJwADPfBlcz+M/ZrK1ACkzD HXj30BABnIyCsFzdw0wm9r3DrHKUB0toPasWTWiBiwiu/9PE/jFXjJeEO5+dffwwB/r5 sRSDqVI10Gi7oBewFjAmTdc+7QVkFOSZehZCgQsJ1Mbo44FF5kkYLIYNsNhNElWHq/gJ SqVg== X-Gm-Message-State: AOJu0YyoH3SDNGFrWXhDKiwVKLdH1SZlpZOTwCqDrOD98oe6dUr/h2iL tEoZhF7wqRoT0uAu42xCSo4ga3GIbhWaFtRuPcAeH8hTPwY+V2IqMJtxy6aKJQ== X-Gm-Gg: Acq92OHfuDGJIcHLWTLwhpmNyRevHOlQw/aDibnbq4WqLRaiuPWNsNFblF7TBq9Jx0O S0lMj4yDNSooZBzvyHporCQqrLP6VWzWgiZ1pG+g+BDYNB5gbUV2GAmQZzfzBRpvzbQHGit1uYm 6Bdpsp+KuOA4GqPIyxa++rVsrnc898Wpj15ckWnoDxDOdrJHfwMvEhPIyoCCCzpiKeCJNtIWgR+ bIgzny1PgW8+UpmduUkgVkyewlJIvSO7+NRYM+Am1E7tebRcPqkmDQLIZ/oyhs9mUjQs3JEPEgf pfwuoXROok4fApmZXkFkXIasknBl9gRD7Lo/Z4SK9Kmn8Y1l6+hVS0Nytrut3qQaYcRkw5v+nw6 GJ/azyUi2L12CH7kwDk03/IurbEWnghoBg+3AWPdWCG/b6CmgYIGZI9UJQmVIsLNvH1HeuU20sT qCst3s76ddVYepVLZbdO9iapYpguJ3JOp6UPqB9Z/GJOoyE4H5tJp/QokDxQ4IhQUwPivYbbp7V AOfnkjzOFk= X-Received: by 2002:a05:600c:3b90:b0:490:9ea0:c11f with SMTP id 5b1f17b1804b1-490ec4c5bb8mr185848205e9.5.1781530818941; Mon, 15 Jun 2026 06:40:18 -0700 (PDT) Received: from bdtpg (ec2-15-237-197-144.eu-west-3.compute.amazonaws.com. [15.237.197.144]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-492202e5d61sm234970285e9.1.2026.06.15.06.40.18 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Jun 2026 06:40:18 -0700 (PDT) Date: Mon, 15 Jun 2026 13:40:17 +0000 From: Bertrand Drouvot To: pgsql-hackers@lists.postgresql.org Subject: Allow wal_log_hints to be changed without restart Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="9ItUckmLlnvfmpcW" Content-Disposition: inline List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --9ItUckmLlnvfmpcW Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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-KJMy8xiM4sTqkgKvHyDrCoojhXzRSKiW57+g@mail.gmail.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com --9ItUckmLlnvfmpcW Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0001-Allow-wal_log_hints-to-be-changed-without-restart.patch" From e1729c994aed9baa87e63875f6567b34f6d06b98 Mon Sep 17 00:00:00 2001 From: Bertrand Drouvot 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 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' - This parameter can only be set at server start. The default value is off. + This parameter can only be set in the postgresql.conf + file or on the server command line. + The default value is off. 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--