# Copyright (c) 2025, PostgreSQL Global Development Group # # This test verifies the case when the physical slot is advanced during # checkpoint. The test checks that the physical slot's restart_lsn still refers # to an existed WAL segment after immediate restart. # use strict; use warnings FATAL => 'all'; use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; if ($ENV{enable_injection_points} ne 'yes') { plan skip_all => 'Injection points not supported by this build'; } my ($node, $result); $node = PostgreSQL::Test::Cluster->new('mike'); $node->init; $node->append_conf('postgresql.conf', "wal_level = 'replica'"); $node->start; # Check if the extension injection_points is available, as it may be # possible that this script is run with installcheck, where the module # would not be installed by default. if (!$node->check_extension('injection_points')) { plan skip_all => 'Extension injection_points not installed'; } $node->safe_psql('postgres', q(CREATE EXTENSION injection_points)); # Create a physical replication slot. $node->safe_psql('postgres', q{select pg_create_physical_replication_slot('slot_physical', true)}); # Advance slot to the current position, just to have everything "valid". $node->safe_psql('postgres', q{select pg_replication_slot_advance('slot_physical', pg_current_wal_lsn())} ); # Run checkpoint to flush current state to disk and set a baseline. $node->safe_psql('postgres', q{checkpoint}); # Insert 2M rows; that's about 260MB (~20 segments) worth of WAL. $node->advance_wal(20); # Advance slot to the current position, just to have everything "valid". $node->safe_psql('postgres', q{select pg_replication_slot_advance('slot_physical', pg_current_wal_lsn())} ); # Run another checkpoint to set a new restore LSN. $node->safe_psql('postgres', q{checkpoint}); # Another 2M rows; that's about 260MB (~20 segments) worth of WAL. $node->advance_wal(20); my $restart_lsn_init = $node->safe_psql('postgres', q{select restart_lsn from pg_replication_slots where slot_name = 'slot_physical'} ); chomp($restart_lsn_init); note("restart lsn before checkpoint: $restart_lsn_init"); # Run another checkpoint, this time in the background, and make it wait # on the injection point) so that the checkpoint stops right before # removing old WAL segments. note('starting checkpoint'); my $checkpoint = $node->background_psql('postgres'); $checkpoint->query_safe( q{select injection_points_attach('checkpoint-before-old-wal-removal','wait')} ); $checkpoint->query_until( qr/starting_checkpoint/, q(\echo starting_checkpoint checkpoint; \q )); # Wait until the checkpoint stops right before removing WAL segments. note('waiting for injection_point'); $node->wait_for_event('checkpointer', 'checkpoint-before-old-wal-removal'); note('injection_point is reached'); # OK, we're in the right situation: time to advance the physical slot, which # recalculates the required LSN and then unblock the checkpoint, which # removes the WAL still needed by the physical slot. $node->safe_psql('postgres', q{select pg_replication_slot_advance('slot_physical', pg_current_wal_lsn())} ); # Continue the checkpoint and wait for its completion. my $log_offset = -s $node->logfile; $node->safe_psql('postgres', q{select injection_points_wakeup('checkpoint-before-old-wal-removal')}); $node->wait_for_log(qr/checkpoint complete/, $log_offset); my $restart_lsn_old = $node->safe_psql('postgres', q{select restart_lsn from pg_replication_slots where slot_name = 'slot_physical'} ); chomp($restart_lsn_old); note("restart lsn before stop: $restart_lsn_old"); # Abruptly stop the server. $node->stop('immediate'); $node->start; # Get the restart_lsn of the slot right after restarting. my $restart_lsn = $node->safe_psql('postgres', q{select restart_lsn from pg_replication_slots where slot_name = 'slot_physical'} ); chomp($restart_lsn); note("restart lsn: $restart_lsn"); # Get the WAL segment name for the slot's restart_lsn. my $restart_lsn_segment = $node->safe_psql('postgres', "SELECT pg_walfile_name('$restart_lsn'::pg_lsn)"); chomp($restart_lsn_segment); # Check if the required wal segment exists. note("required by slot segment name: $restart_lsn_segment"); my $datadir = $node->data_dir; ok( -f "$datadir/pg_wal/$restart_lsn_segment", "WAL segment $restart_lsn_segment for physical slot's restart_lsn $restart_lsn exists" ); done_testing();