From 9c91b2354657fb266ae810078ad37705cc9738d0 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Sun, 25 Oct 2026 00:10:07 -0400 Subject: [PATCH 1/2] Test WAL buffer reads by a new walsender --- src/test/recovery/meson.build | 1 + .../recovery/t/062_wal_buffers_reconnect.pl | 103 ++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/test/recovery/t/062_wal_buffers_reconnect.pl diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build index d68cfb149f0..5087126421d 100644 --- a/src/test/recovery/meson.build +++ b/src/test/recovery/meson.build @@ -70,6 +70,7 @@ tests += { 't/059_remote_apply_status_interval.pl', 't/060_multixact_crashed_updater.pl', 't/061_wal_buffers_recycling.pl', + 't/062_wal_buffers_reconnect.pl', ], }, } diff --git a/src/test/recovery/t/062_wal_buffers_reconnect.pl b/src/test/recovery/t/062_wal_buffers_reconnect.pl new file mode 100644 index 00000000000..50d38a749e8 --- /dev/null +++ b/src/test/recovery/t/062_wal_buffers_reconnect.pl @@ -0,0 +1,103 @@ +# Variant of Kirill's 061 test: after the checkpoint removes the segment, +# terminate the paused sender instead of waking it. The receiver reconnects +# and a new sender starts at the same position. Its reader has +# seg.ws_tli == 0 until the first disk read, so WALReadFromBuffers() returns 0. +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; +use Time::HiRes qw(usleep); + +if (!defined($ENV{enable_injection_points}) + || $ENV{enable_injection_points} ne 'yes') +{ + plan skip_all => 'Injection points not supported by this build'; +} + +my $primary = PostgreSQL::Test::Cluster->new('primary'); +$primary->init(allows_streaming => 1, extra => ['--wal-segsize=1']); +$primary->append_conf('postgresql.conf', qq( +shared_preload_libraries = 'injection_points' +archive_mode = off +wal_buffers = '32MB' +min_wal_size = '2MB' +max_wal_size = '4MB' +wal_keep_size = 0 +checkpoint_timeout = '1h' +wal_sender_timeout = 0 +autovacuum = off +)); +$primary->start; +$primary->safe_psql('postgres', + 'CREATE EXTENSION injection_points; CREATE TABLE buffers_test (i int)'); +$primary->backup('base'); + +my $receiver = PostgreSQL::Test::Cluster->new('receiver'); +$receiver->init_from_backup($primary, 'base', has_streaming => 1); +$receiver->append_conf('postgresql.conf', qq( +restore_command = '' +wal_receiver_timeout = 0 +wal_retrieve_retry_interval = '100ms' +)); +$receiver->start; +$primary->safe_psql('postgres', 'INSERT INTO buffers_test VALUES (1)'); +my $initial_lsn = $primary->lsn('insert'); +$primary->wait_for_catchup($receiver, 'replay', $initial_lsn); +my $old_segment = $primary->safe_psql('postgres', + "SELECT pg_walfile_name('$initial_lsn')"); +my $sender_pid = $primary->safe_psql('postgres', + 'SELECT pid FROM pg_stat_replication'); + +$primary->safe_psql( + 'postgres', + q{SELECT injection_points_attach('walwriter-before-background-flush', 'wait')}); +$primary->wait_for_event('walwriter', 'walwriter-before-background-flush'); + +$primary->safe_psql('postgres', + q{SELECT injection_points_attach('walsender-before-wal-read', 'wait')}); +$primary->safe_psql('postgres', 'INSERT INTO buffers_test VALUES (2)'); +$primary->wait_for_event('walsender', 'walsender-before-wal-read'); + +$primary->safe_psql('postgres', q{ +SELECT pg_logical_emit_message(false, 'fill', repeat('x', 1024 * 1024)) +FROM generate_series(1, 12); +CHECKPOINT; +}); +my $target_lsn = $primary->lsn('insert'); +ok(!-e $primary->data_dir . "/pg_wal/$old_segment", + 'checkpoint removed the segment needed by the paused sender') + or BAIL_OUT('checkpoint did not remove the required local segment'); + +# Kill the paused sender, so that the receiver reconnects. +my $log_offset = -s $primary->logfile; +$primary->safe_psql('postgres', qq{ +SELECT injection_points_detach('walsender-before-wal-read'); +SELECT pg_terminate_backend($sender_pid); +}); + +# Poll for up to 30 seconds, without the default 180 second timeout. +my $caught_up = 0; +for (1 .. 300) +{ + if ($receiver->safe_psql('postgres', + "SELECT pg_last_wal_replay_lsn() >= '$target_lsn'::pg_lsn") eq 't') + { + $caught_up = 1; + last; + } + usleep(100_000); +} +my $removed = $primary->log_contains('has already been removed', $log_offset); +note "caught_up=$caught_up removed_error_in_log=" . ($removed ? 1 : 0); +ok($caught_up, 'receiver catches up after reconnecting'); +ok(!$removed, 'no "already been removed" error after reconnect'); + +$primary->safe_psql('postgres', q{ +SELECT injection_points_detach('walwriter-before-background-flush'); +SELECT injection_points_wakeup('walwriter-before-background-flush'); +}); +$receiver->stop; +$primary->stop; +done_testing(); -- 2.37.1 (Apple Git-137.1)