From 5f76ed5d6baa9bfa9ebd2f1d97eaf6dcbfe76b5f Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Mon, 28 Sep 2026 17:46:50 +0200 Subject: [PATCH] Avoid idle WAL waits in checksum tests Switch to wait_for_replay_catchup in checksum tests to explicitly wait for the upstream flush LSN and not the write LSN. The write LSN can include unrelated unflushed WAL which isn't available for streaming on an idle primary. In the test for using a rewound node as standby of a new primary in the 013_rewind suite; also switch WAL on the source before starting the target. This flushes through the minimum recovery point chosen to avoid a startup delay while an unflushed full_page_writes change waits for the background writer to log its next snapshot. This reduces test times significantly without changing the semantics of the tests. Author: Nazir Bilal Yavuz Reviewed-by: Zsolt Parragi Reviewed-by: Daniel Gustafsson Discussion: https://postgr.es/m/CAN55FZ1Yak_xBqMaDQsD7atpBkGLEkF-DKXcs3nLHM1Uq4YRew%40mail.gmail.com --- .../test_checksums/t/003_standby_restarts.pl | 12 ++++-------- .../test_checksums/t/011_standby_straddle.pl | 15 +++++---------- src/test/modules/test_checksums/t/013_rewind.pl | 12 +++++++++--- .../t/021_rewind_divergent_transitions.pl | 8 ++++---- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/test/modules/test_checksums/t/003_standby_restarts.pl b/src/test/modules/test_checksums/t/003_standby_restarts.pl index b05ffd8643a..d70b90598da 100644 --- a/src/test/modules/test_checksums/t/003_standby_restarts.pl +++ b/src/test/modules/test_checksums/t/003_standby_restarts.pl @@ -42,8 +42,7 @@ $node_primary->safe_psql('postgres', "CREATE TABLE t AS SELECT generate_series(1,10000) AS a;"); # Wait for standby to catch up -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); # Check that checksums are turned off on all nodes test_checksum_state($node_primary, 'off'); @@ -140,8 +139,7 @@ $node_primary->safe_psql( SELECT g, repeat('x', 100) FROM generate_series(1, 1000) g; CREATE INDEX unlogged_promo_payload_idx ON unlogged_promo (payload); ]); -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); # Get the relfilenode and database OID so we can inspect the filesystem my $unlogged_rfn = $node_primary->safe_psql('postgres', @@ -161,8 +159,7 @@ wait_for_checksum_state($node_standby, 'on'); # After standby replays, the unlogged main file must still not exist. # If the bug were present, FPI replay would materialize the full table. -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); ok( !-f "$standby_datadir/base/$db_oid/$unlogged_rfn", 'standby has no main fork for unlogged table after enable'); @@ -181,8 +178,7 @@ is($result, '1000', # Alter persistence to logged, and make sure we can read it on both the primary # and standby without any page verification errors in the logfiles. $node_primary->safe_psql('postgres', 'ALTER TABLE unlogged_tbl SET logged;'); -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); $result = $node_primary->safe_psql('postgres', 'SELECT sum(a) FROM unlogged_tbl;'); diff --git a/src/test/modules/test_checksums/t/011_standby_straddle.pl b/src/test/modules/test_checksums/t/011_standby_straddle.pl index e50fb65fcaf..4d3aa6f1249 100644 --- a/src/test/modules/test_checksums/t/011_standby_straddle.pl +++ b/src/test/modules/test_checksums/t/011_standby_straddle.pl @@ -92,8 +92,7 @@ bgwriter_lru_maxpages = 0 ]); $node_standby->start; -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); test_checksum_state($node_primary, 'off'); test_checksum_state($node_standby, 'off'); @@ -102,8 +101,7 @@ test_checksum_state($node_standby, 'off'); # record reaches the standby until the enabling is released, so this remains # the standby's backup starting checkpoint throughout. $node_primary->safe_psql('postgres', 'CHECKPOINT;'); -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); $node_standby->safe_psql('postgres', 'CHECKPOINT;'); # Put everything the enabling writes into fresh WAL segments, so that the @@ -143,8 +141,7 @@ $node_primary->safe_psql('postgres', # The standby has now replayed the state change: its pg_control says "on" # while the rewritten pages are only dirty in its shared buffers. -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); wait_for_checksum_state($node_standby, 'on'); # Crash the standby, losing the dirty rewritten pages. @@ -197,8 +194,7 @@ isnt($ret, 0, 'standby refuses connections while below the state change'); $node_standby->enable_streaming($node_primary); $node_standby->reload; $node_standby->poll_query_until('postgres', 'SELECT true;'); -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); # The rewritten pages are again only dirty in shared buffers, so the on-disk # pages still lack checksums. A base backup must skip verification entirely @@ -228,8 +224,7 @@ $node_primary->poll_query_until('postgres', # A restartpoint on the final checkpoint lets verification resume, and a # backup started from it must again pass. -$node_primary->wait_for_catchup($node_standby, 'replay', - $node_primary->lsn('insert')); +$node_primary->wait_for_replay_catchup($node_standby); $node_standby->safe_psql('postgres', 'CHECKPOINT;'); $node_standby->command_checks_all( diff --git a/src/test/modules/test_checksums/t/013_rewind.pl b/src/test/modules/test_checksums/t/013_rewind.pl index a791e24317d..21715ace9f3 100644 --- a/src/test/modules/test_checksums/t/013_rewind.pl +++ b/src/test/modules/test_checksums/t/013_rewind.pl @@ -71,7 +71,8 @@ my $node_b = PostgreSQL::Test::Cluster->new('node_b'); $node_b->init_from_backup($node_a, 'backup', has_streaming => 1); $node_b->start; -$node_a->wait_for_catchup($node_b, 'replay', $node_a->lsn('insert')); +# Backup completion has flushed the required WAL. +$node_a->wait_for_replay_catchup($node_b); test_checksum_state($node_a, 'off'); test_checksum_state($node_b, 'off'); @@ -83,7 +84,7 @@ $node_b->safe_psql('postgres', # in a background session; it will block on the injection point with # the checkpointer busy until released. $node_a->safe_psql('postgres', "CHECKPOINT;"); -$node_a->wait_for_catchup($node_b, 'replay', $node_a->lsn('insert')); +$node_a->wait_for_replay_catchup($node_b); my $bg_psql = $node_b->background_psql('postgres', on_error_stop => 0); $bg_psql->query_until( @@ -182,9 +183,14 @@ port = @{[$node_a->port]} primary_conninfo = '$connstr application_name=@{[$node_a->name]}' ]); $node_a->set_standby_mode; + +# Flush WAL through the minimum recovery point chosen by pg_rewind. The +# full_page_writes change can leave an unflushed record on the idle source, +# delaying startup until the background writer logs its next snapshot. +$node_b->safe_psql('postgres', 'SELECT pg_switch_wal();'); $node_a->start; -$node_b->wait_for_catchup($node_a, 'replay', $node_b->lsn('insert')); +$node_b->wait_for_replay_catchup($node_a); test_checksum_state($node_a, 'on'); is($node_a->safe_psql('postgres', "SELECT count(*) FROM t;"), diff --git a/src/test/modules/test_checksums/t/021_rewind_divergent_transitions.pl b/src/test/modules/test_checksums/t/021_rewind_divergent_transitions.pl index 6f95384f76a..e52fcc56ee5 100644 --- a/src/test/modules/test_checksums/t/021_rewind_divergent_transitions.pl +++ b/src/test/modules/test_checksums/t/021_rewind_divergent_transitions.pl @@ -87,7 +87,7 @@ $node_a->backup('backup'); my $node_b = PostgreSQL::Test::Cluster->new('node_b'); $node_b->init_from_backup($node_a, 'backup', has_streaming => 1); $node_b->start; -$node_a->wait_for_catchup($node_b, 'replay', $node_a->lsn('insert')); +$node_a->wait_for_replay_catchup($node_b); # Clean switchover to B; enable checksums online on it. $node_a->stop('fast'); @@ -137,7 +137,7 @@ primary_conninfo = '$connstr_b application_name=@{[$node_a->name]}' $node_a->set_standby_mode; $node_a->start; -$node_b->wait_for_catchup($node_a, 'replay', $node_b->lsn('insert')); +$node_b->wait_for_replay_catchup($node_a); test_checksum_state($node_a, 'on'); is($node_a->safe_psql('postgres', "SELECT count(*) FROM t_div;"), @@ -147,7 +147,7 @@ is($node_a->safe_psql('postgres', "SELECT count(*) FROM t_div;"), # checksums online so the next divergence point carries "off", and let # A replay the change. disable_data_checksums($node_b, wait => 'off'); -$node_b->wait_for_catchup($node_a, 'replay', $node_b->lsn('insert')); +$node_b->wait_for_replay_catchup($node_a); test_checksum_state($node_a, 'off'); # Clean switchover back to A; enable checksums online on it. @@ -184,7 +184,7 @@ primary_conninfo = '$connstr_a application_name=@{[$node_b->name]}' $node_b->set_standby_mode; $node_b->start; -$node_a->wait_for_catchup($node_b, 'replay', $node_a->lsn('insert')); +$node_a->wait_for_replay_catchup($node_b); test_checksum_state($node_b, 'on'); is($node_b->safe_psql('postgres', "SELECT count(*) FROM t;"),