On 7/7/26 23:00, Daniel Gustafsson wrote: >> On 6 Jul 2026, at 13:43, Tomas Vondra <[email protected]> wrote: > >> Here's a rebased patch, addressing the minor bitrot, and with some very >> minor adjustments. > > Thanks for picking up this! > >> 3) I've noticed the SGML docs don't have default values for the existing >> parameters (cost_delay, cost_limit), so I added those. Maybe backpatch? > > +1, that sounds like a good idea. > >> 4) I've added the new 'fast' parameter to the DataChecksums module, but >> mostly just for completeness. The TAP tests still use the default value, >> so that it doesn't take longer. > > + spread (throttled), which may increase the time needed to enable > checksums, > > I think we should stick to the "data checksums" terminology for consistency. > > @@ -1676,6 +1686,7 @@ DataChecksumsWorkerMain(Datum arg) > > DataChecksumState->cost_delay = > DataChecksumState->launch_cost_delay; > DataChecksumState->cost_limit = > DataChecksumState->launch_cost_limit; > + DataChecksumState->fast_checkpoint = > DataChecksumState->launch_fast_checkpoint; > } > else > costs_updated = false; > > This update will only change the fast parameter if cost_limit or cost_delay > were changed, shouldn't the fast parameter also be checked for runtime update? >
Ah, good catch. I missed that. The attached v3 fixes this. >> I've spent some time thinking about the default value. For a while I >> thought maybe it should default to 'false' (in which case we use spread >> checkpoints, which is the less disruptive option). >> >> But that's actually futile, because the cost_delay still defaults to 0, >> which means the "data page rewrite" phase won't be paced. It seems >> surprising the first phase would not be throttled by default, while the >> checkpoints are ... >> >> So I ended up setting the 'fast=true' default. > > I think fast=true is the right default. > Ack >> This is not a very complicated patch, and it's been part of the old >> patch series for quite a while. So unless there's some objections I'll >> get this committed in a couple days. > > +1 Thanks! -- Tomas Vondra
From b632d2b4e1c2904ab26cf1fdfc874e71f59d2a5a Mon Sep 17 00:00:00 2001 From: Tomas Vondra <[email protected]> Date: Mon, 6 Jul 2026 11:31:19 +0200 Subject: [PATCH v3] Allow using spread checkpoints when enabling/disabling checksums This commit allows using spread (throttled) checkpoints while enabling or disabling data checksums in a running cluster, to limit the impact on the rest of the system. While enabling checksums, the process has to rewrite all data pages, and then perform perform checkpoints at certain points. It was possible to throttle the rewriting of data pages, to limit the impact on the rest of the cluster, the ckeckpoints were always issued as CHECKPOINT_FAST. This can be very disruptive. When disabling checksums, it's not necessary to rewrite any data pages, but the checkpoint may still trigger a lot of I/O. So a seemingly cheap operation (disabling checksums) may get rather disruptive. This commit introduces a new "fast" parameter for functions initiating the checksum state change. The default "fast=true" means the requested checkpoints are fast. --- doc/src/sgml/func/func-admin.sgml | 22 ++++++++++++-- src/backend/access/transam/xlog.c | 24 +++++++++++---- src/backend/postmaster/datachecksum_state.c | 30 +++++++++++++------ src/include/access/xlog.h | 4 +-- src/include/catalog/pg_proc.dat | 12 ++++---- .../test_checksums/t/DataChecksums/Utils.pm | 24 +++++++++++++-- 6 files changed, 89 insertions(+), 27 deletions(-) diff --git a/doc/src/sgml/func/func-admin.sgml b/doc/src/sgml/func/func-admin.sgml index 0eae1c1f616..97b768638c5 100644 --- a/doc/src/sgml/func/func-admin.sgml +++ b/doc/src/sgml/func/func-admin.sgml @@ -3158,7 +3158,7 @@ SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8'); <indexterm> <primary>pg_enable_data_checksums</primary> </indexterm> - <function>pg_enable_data_checksums</function> ( <optional><parameter>cost_delay</parameter> <type>int</type>, <parameter>cost_limit</parameter> <type>int</type></optional> ) + <function>pg_enable_data_checksums</function> ( <optional><parameter>cost_delay</parameter> <type>int</type> <literal>DEFAULT</literal> <literal>0</literal>, <parameter>cost_limit</parameter> <type>int</type> <literal>DEFAULT</literal> <literal>100</literal>, <parameter>fast</parameter> <type>bool</type> <literal>DEFAULT</literal> <literal>true</literal></optional> ) <returnvalue>void</returnvalue> </para> <para> @@ -3175,6 +3175,15 @@ SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8'); specified, the process is throttled using the same principles as <link linkend="runtime-config-resource-vacuum-cost">Cost-based Vacuum Delay</link>. </para> + <para> + The <parameter>fast</parameter> specifies whether the checkpoints performed + are fast or spread. + When set to <literal>true</literal> the checkpoints are fast, which may cause + a spike in I/O. When set to <literal>false</literal>, the checkpoints are + spread (throttled), which may increase the time needed to enable checksums, + but it limits the impact on the system. + The default value is <literal>true</literal>. + </para> </entry> </row> @@ -3183,7 +3192,7 @@ SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8'); <indexterm> <primary>pg_disable_data_checksums</primary> </indexterm> - <function>pg_disable_data_checksums</function> () + <function>pg_disable_data_checksums</function> ( <optional><parameter>fast</parameter> <type>bool</type> <literal>DEFAULT</literal> <literal>false</literal></optional> ) <returnvalue>void</returnvalue> </para> <para> @@ -3193,6 +3202,15 @@ SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8'); stopped validating data checksums, the data checksum state will be set to <literal>off</literal>. </para> + <para> + The <parameter>fast</parameter> specifies whether the checkpoints performed + are fast or spread. + When set to <literal>true</literal> the checkpoints are fast, which may cause + a spike in I/O. When set to <literal>false</literal>, the checkpoints are + spread (throttled), which may increase the time needed to disable checksums, + but it limits the impact on the system. + The default value is <literal>true</literal>. + </para> </entry> </row> </tbody> diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a81912b7441..aaca0437a5f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -4799,9 +4799,10 @@ SetDataChecksumsOnInProgress(void) * state transition. */ void -SetDataChecksumsOn(void) +SetDataChecksumsOn(bool fast) { uint64 barrier; + int flags; SpinLockAcquire(&XLogCtl->info_lck); @@ -4815,7 +4816,7 @@ SetDataChecksumsOn(void) SpinLockRelease(&XLogCtl->info_lck); elog(WARNING, "cannot set data checksums to \"on\", current state is not \"inprogress-on\", disabling"); - SetDataChecksumsOff(); + SetDataChecksumsOff(fast); return; } @@ -4845,7 +4846,12 @@ SetDataChecksumsOn(void) MyProc->delayChkptFlags &= ~DELAY_CHKPT_START; END_CRIT_SECTION(); - RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT | CHECKPOINT_FAST); + /* determine flags for the checkpoint */ + flags = CHECKPOINT_FORCE | CHECKPOINT_WAIT; + if (fast) + flags |= CHECKPOINT_FAST; + + RequestCheckpoint(flags); WaitForProcSignalBarrier(barrier); } @@ -4863,9 +4869,15 @@ SetDataChecksumsOn(void) * state transition. */ void -SetDataChecksumsOff(void) +SetDataChecksumsOff(bool fast) { uint64 barrier; + int flags; + + /* determine flags for the checkpoint(s) */ + flags = CHECKPOINT_FORCE | CHECKPOINT_WAIT; + if (fast) + flags |= CHECKPOINT_FAST; SpinLockAcquire(&XLogCtl->info_lck); @@ -4907,7 +4919,7 @@ SetDataChecksumsOff(void) MyProc->delayChkptFlags &= ~DELAY_CHKPT_START; END_CRIT_SECTION(); - RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT | CHECKPOINT_FAST); + RequestCheckpoint(flags); WaitForProcSignalBarrier(barrier); /* @@ -4945,7 +4957,7 @@ SetDataChecksumsOff(void) MyProc->delayChkptFlags &= ~DELAY_CHKPT_START; END_CRIT_SECTION(); - RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT | CHECKPOINT_FAST); + RequestCheckpoint(flags); WaitForProcSignalBarrier(barrier); } diff --git a/src/backend/postmaster/datachecksum_state.c b/src/backend/postmaster/datachecksum_state.c index 68557c16cb9..bff73535e8e 100644 --- a/src/backend/postmaster/datachecksum_state.c +++ b/src/backend/postmaster/datachecksum_state.c @@ -307,6 +307,7 @@ typedef struct DataChecksumsStateStruct DataChecksumsWorkerOperation launch_operation; int launch_cost_delay; int launch_cost_limit; + bool launch_fast_checkpoint; /* * Is a launcher process currently running? This is set by the main @@ -340,6 +341,7 @@ typedef struct DataChecksumsStateStruct DataChecksumsWorkerOperation operation; int cost_delay; int cost_limit; + bool fast_checkpoint; /* * Signaling between the launcher and the worker process. Protected by @@ -382,7 +384,8 @@ static DataChecksumsWorkerOperation operation; /* Prototypes */ static void StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op, int cost_delay, - int cost_limit); + int cost_limit, + bool fast); static void DataChecksumsShmemRequest(void *arg); static bool DatabaseExists(Oid dboid); static List *BuildDatabaseList(void); @@ -543,6 +546,8 @@ AbsorbDataChecksumsBarrier(ProcSignalBarrierType barrier) Datum disable_data_checksums(PG_FUNCTION_ARGS) { + bool fast = PG_GETARG_BOOL(0); + PreventCommandDuringRecovery("pg_disable_data_checksums()"); if (!superuser()) @@ -550,7 +555,7 @@ disable_data_checksums(PG_FUNCTION_ARGS) errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to change data checksum state")); - StartDataChecksumsWorkerLauncher(DISABLE_DATACHECKSUMS, 0, 0); + StartDataChecksumsWorkerLauncher(DISABLE_DATACHECKSUMS, 0, 0, fast); PG_RETURN_VOID(); } @@ -564,6 +569,7 @@ enable_data_checksums(PG_FUNCTION_ARGS) { int cost_delay = PG_GETARG_INT32(0); int cost_limit = PG_GETARG_INT32(1); + bool fast = PG_GETARG_BOOL(2); PreventCommandDuringRecovery("pg_enable_data_checksums()"); @@ -582,7 +588,7 @@ enable_data_checksums(PG_FUNCTION_ARGS) errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cost limit must be greater than zero")); - StartDataChecksumsWorkerLauncher(ENABLE_DATACHECKSUMS, cost_delay, cost_limit); + StartDataChecksumsWorkerLauncher(ENABLE_DATACHECKSUMS, cost_delay, cost_limit, fast); PG_RETURN_VOID(); } @@ -602,7 +608,8 @@ enable_data_checksums(PG_FUNCTION_ARGS) static void StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op, int cost_delay, - int cost_limit) + int cost_limit, + bool fast) { BackgroundWorker bgw; BackgroundWorkerHandle *bgw_handle; @@ -622,6 +629,7 @@ StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op, DataChecksumState->launch_operation = op; DataChecksumState->launch_cost_delay = cost_delay; DataChecksumState->launch_cost_limit = cost_limit; + DataChecksumState->launch_fast_checkpoint = fast; /* Is the launcher already running? If so, what is it doing? */ running = DataChecksumState->launcher_running; @@ -989,7 +997,7 @@ launcher_exit(int code, Datum arg) * the state to off since processing cannot be resumed. */ if (DataChecksumsInProgressOn()) - SetDataChecksumsOff(); + SetDataChecksumsOff(DataChecksumState->fast_checkpoint); LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE); launcher_running = false; @@ -1130,6 +1138,7 @@ DataChecksumsWorkerLauncherMain(Datum arg) DataChecksumState->operation = operation; DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay; DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit; + DataChecksumState->fast_checkpoint = DataChecksumState->launch_fast_checkpoint; LWLockRelease(DataChecksumsWorkerLock); /* @@ -1184,7 +1193,7 @@ again: * Data checksums have been set on all pages, set the state to on in * order to instruct backends to validate checksums on reading. */ - SetDataChecksumsOn(); + SetDataChecksumsOn(DataChecksumState->fast_checkpoint); ereport(LOG, errmsg("data checksums are now enabled")); @@ -1196,7 +1205,7 @@ again: pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE, PROGRESS_DATACHECKSUMS_PHASE_DISABLING); - SetDataChecksumsOff(); + SetDataChecksumsOff(DataChecksumState->fast_checkpoint); ereport(LOG, errmsg("data checksums are now disabled")); } @@ -1224,6 +1233,7 @@ done: operation = DataChecksumState->launch_operation; DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay; DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit; + DataChecksumState->fast_checkpoint = DataChecksumState->launch_fast_checkpoint; LWLockRelease(DataChecksumsWorkerLock); goto again; } @@ -1313,7 +1323,7 @@ ProcessAllDatabases(void) * Disable checksums on cluster, because we failed one of the * databases and this is an all or nothing process. */ - SetDataChecksumsOff(); + SetDataChecksumsOff(DataChecksumState->fast_checkpoint); ereport(ERROR, errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("data checksums failed to get enabled in all databases, aborting"), @@ -1667,7 +1677,8 @@ DataChecksumsWorkerMain(Datum arg) break; } if ((DataChecksumState->launch_cost_delay != DataChecksumState->cost_delay) - || (DataChecksumState->launch_cost_limit != DataChecksumState->cost_limit)) + || (DataChecksumState->launch_cost_limit != DataChecksumState->cost_limit) + || (DataChecksumState->launch_fast_checkpoint != DataChecksumState->fast_checkpoint)) { costs_updated = true; VacuumCostDelay = DataChecksumState->launch_cost_delay; @@ -1676,6 +1687,7 @@ DataChecksumsWorkerMain(Datum arg) DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay; DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit; + DataChecksumState->fast_checkpoint = DataChecksumState->launch_fast_checkpoint; } else costs_updated = false; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 4dd98624204..8a9e8961a6a 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -253,8 +253,8 @@ extern bool DataChecksumsOn(void); extern bool DataChecksumsOff(void); extern bool DataChecksumsInProgressOn(void); extern void SetDataChecksumsOnInProgress(void); -extern void SetDataChecksumsOn(void); -extern void SetDataChecksumsOff(void); +extern void SetDataChecksumsOn(bool fast); +extern void SetDataChecksumsOff(bool fast); extern const char *show_data_checksums(void); extern const char *get_checksum_state_string(uint32 state); extern void InitLocalDataChecksumState(void); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 3cb84359adf..c7ef5a9c930 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12415,13 +12415,15 @@ # data checksum management functions { oid => '6507', descr => 'disable data checksums', proname => 'pg_disable_data_checksums', provolatile => 'v', - proparallel => 'r', prorettype => 'void', proargtypes => '', - prosrc => 'disable_data_checksums', proacl => '{POSTGRES=X}' }, + proparallel => 'r', prorettype => 'void', proargtypes => 'bool', + proallargtypes => '{bool}', proargmodes => '{i}', proargnames => '{fast}', + proargdefaults => '{true}', prosrc => 'disable_data_checksums', + proacl => '{POSTGRES=X}' }, { oid => '6506', descr => 'enable data checksums', proname => 'pg_enable_data_checksums', provolatile => 'v', proparallel => 'r', - prorettype => 'void', proargtypes => 'int4 int4', - proallargtypes => '{int4,int4}', proargmodes => '{i,i}', - proargnames => '{cost_delay,cost_limit}', proargdefaults => '{0,100}', + prorettype => 'void', proargtypes => 'int4 int4 bool', + proallargtypes => '{int4,int4,bool}', proargmodes => '{i,i,i}', + proargnames => '{cost_delay,cost_limit,fast}', proargdefaults => '{0,100,true}', prosrc => 'enable_data_checksums', proacl => '{POSTGRES=X}' }, # collation management functions diff --git a/src/test/modules/test_checksums/t/DataChecksums/Utils.pm b/src/test/modules/test_checksums/t/DataChecksums/Utils.pm index 3aa533d777f..77c208dd5fd 100644 --- a/src/test/modules/test_checksums/t/DataChecksums/Utils.pm +++ b/src/test/modules/test_checksums/t/DataChecksums/Utils.pm @@ -118,6 +118,11 @@ If defined, the function will wait for the state defined in this parameter, waiting timing out, before returning. The function will wait for $PostgreSQL::Test::Utils::timeout_default seconds before timing out. +=item fast + +The B<fast> determines if checkpoints will be fast or spread, default is +'true' (which means fast checkpoints). + =back =cut @@ -130,13 +135,14 @@ sub enable_data_checksums # Set sane defaults for the parameters $params{cost_delay} = 0 unless (defined($params{cost_delay})); $params{cost_limit} = 100 unless (defined($params{cost_limit})); + $params{fast} = 'true' unless (defined($params{fast})); my $query = <<'EOQ'; -SELECT pg_enable_data_checksums(%s, %s); +SELECT pg_enable_data_checksums(%s, %s, %s); EOQ $postgresnode->safe_psql('postgres', - sprintf($query, $params{cost_delay}, $params{cost_limit})); + sprintf($query, $params{cost_delay}, $params{cost_limit}, $params{fast})); if (defined($params{wait})) { @@ -166,6 +172,11 @@ waiting timing out, before returning. The function will wait for $PostgreSQL::Test::Utils::timeout_default seconds before timing out. Unlike in C<enable_data_checksums> the value of the parameter is discarded. +=item fast + +The B<fast> determines if checkpoints will be fast or spread, default is +'true' (which means fast checkpoints). + =back =cut @@ -175,8 +186,15 @@ sub disable_data_checksums my $postgresnode = shift; my %params = @_; + # Set sane defaults for the parameters + $params{fast} = 'true' unless (defined($params{fast})); + + my $query = <<'EOQ'; +SELECT pg_disable_data_checksums(%s); +EOQ + $postgresnode->safe_psql('postgres', - 'SELECT pg_disable_data_checksums();'); + sprintf($query, $params{fast})); if (defined($params{wait})) { -- 2.54.0
