From 8e4375d389bfbf68a3e8d19f4d69a0183c994b5b Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Wed, 26 Mar 2025 19:03:50 +0900 Subject: [PATCH] Avoid using active slots in 035_standby_logical_decoding --- .../t/035_standby_logical_decoding.pl | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl index 8120dfc2132..9ab08d75fd3 100644 --- a/src/test/recovery/t/035_standby_logical_decoding.pl +++ b/src/test/recovery/t/035_standby_logical_decoding.pl @@ -44,27 +44,37 @@ sub wait_for_xmins # Create the required logical slots on standby. sub create_logical_slots { - my ($node, $slot_prefix) = @_; + my ($node, $slot_prefix, $needs_active_slot) = @_; - my $active_slot = $slot_prefix . 'activeslot'; my $inactive_slot = $slot_prefix . 'inactiveslot'; $node->create_logical_slot_on_standby($node_primary, qq($inactive_slot), 'testdb'); - $node->create_logical_slot_on_standby($node_primary, qq($active_slot), - 'testdb'); + + if ($needs_active_slot) + { + my $active_slot = $slot_prefix . 'activeslot'; + + $node->create_logical_slot_on_standby($node_primary, qq($active_slot), + 'testdb'); + } } # Drop the logical slots on standby. sub drop_logical_slots { - my ($slot_prefix) = @_; - my $active_slot = $slot_prefix . 'activeslot'; + my ($slot_prefix, $needs_active_slot) = @_; my $inactive_slot = $slot_prefix . 'inactiveslot'; $node_standby->psql('postgres', qq[SELECT pg_drop_replication_slot('$inactive_slot')]); - $node_standby->psql('postgres', - qq[SELECT pg_drop_replication_slot('$active_slot')]); + + if ($needs_active_slot) + { + my $active_slot = $slot_prefix . 'activeslot'; + + $node_standby->psql('postgres', + qq[SELECT pg_drop_replication_slot('$active_slot')]); + } } # Acquire one of the standby logical slots created by create_logical_slots(). @@ -191,22 +201,22 @@ sub check_slots_conflicting_status } } -# Drop the slots, re-create them, change hot_standby_feedback, -# check xmin and catalog_xmin values, make slot active and reset stat. +# Create slots, change hot_standby_feedback, check xmin and catalog_xmin +# values, make slot active and reset stat. sub reactive_slots_change_hfs_and_wait_for_xmins { - my ($previous_slot_prefix, $slot_prefix, $hsf, $invalidated) = @_; - - # drop the logical slots - drop_logical_slots($previous_slot_prefix); + my ($slot_prefix, $hsf, $invalidated, $needs_active_slot) = @_; # create the logical slots - create_logical_slots($node_standby, $slot_prefix); + create_logical_slots($node_standby, $slot_prefix, $needs_active_slot); change_hot_standby_feedback_and_wait_for_xmins($hsf, $invalidated); - $handle = - make_slot_active($node_standby, $slot_prefix, 1, \$stdout, \$stderr); + if ($needs_active_slot) + { + $handle = + make_slot_active($node_standby, $slot_prefix, 1, \$stdout, \$stderr); + } # reset stat: easier to check for confl_active_logicalslot in pg_stat_database_conflicts $node_standby->psql('testdb', q[select pg_stat_reset();]); @@ -215,9 +225,8 @@ sub reactive_slots_change_hfs_and_wait_for_xmins # Check invalidation in the logfile and in pg_stat_database_conflicts sub check_for_invalidation { - my ($slot_prefix, $log_start, $test_name) = @_; + my ($slot_prefix, $log_start, $test_name, $checks_active_slot) = @_; - my $active_slot = $slot_prefix . 'activeslot'; my $inactive_slot = $slot_prefix . 'inactiveslot'; # message should be issued @@ -226,18 +235,23 @@ sub check_for_invalidation $log_start), "inactiveslot slot invalidation is logged $test_name"); - ok( $node_standby->log_contains( - "invalidating obsolete replication slot \"$active_slot\"", - $log_start), - "activeslot slot invalidation is logged $test_name"); - - # Verify that pg_stat_database_conflicts.confl_active_logicalslot has been updated - ok( $node_standby->poll_query_until( - 'postgres', - "select (confl_active_logicalslot = 1) from pg_stat_database_conflicts where datname = 'testdb'", - 't'), - 'confl_active_logicalslot updated' - ) or die "Timed out waiting confl_active_logicalslot to be updated"; + if ($checks_active_slot) + { + my $active_slot = $slot_prefix . 'activeslot'; + + ok( $node_standby->log_contains( + "invalidating obsolete replication slot \"$active_slot\"", + $log_start), + "activeslot slot invalidation is logged $test_name"); + + # Verify that pg_stat_database_conflicts.confl_active_logicalslot has been updated + ok( $node_standby->poll_query_until( + 'postgres', + "select (confl_active_logicalslot = 1) from pg_stat_database_conflicts where datname = 'testdb'", + 't'), + 'confl_active_logicalslot updated' + ) or die "Timed out waiting confl_active_logicalslot to be updated"; + } } # Launch $sql query, wait for a new snapshot that has a newer horizon and @@ -262,6 +276,10 @@ sub wait_until_vacuum_can_remove # Launch our sql. $node_primary->safe_psql('testdb', qq[$sql]); + # XXX: Reproducer - must be removed before being pushed + $node_primary->safe_psql('testdb', 'CHECKPOINT'); + sleep(20); + # Wait until we get a newer horizon. $node_primary->poll_query_until('testdb', "SELECT (select pg_snapshot_xmin(pg_current_snapshot())::text::int - $xid_horizon) > 0" @@ -389,7 +407,7 @@ $node_standby->safe_psql('postgres', ################################################## # create the logical slots -create_logical_slots($node_standby, 'behaves_ok_'); +create_logical_slots($node_standby, 'behaves_ok_', 1); $node_primary->safe_psql('testdb', qq[CREATE TABLE decoding_test(x integer, y text);]); @@ -536,11 +554,13 @@ $node_subscriber->stop; # Scenario 1: hot_standby_feedback off and vacuum FULL ################################################## +# drop the logical slots used by previous tests +drop_logical_slots('behaves_ok_', 1); + # One way to produce recovery conflict is to create/drop a relation and # launch a vacuum full on pg_class with hot_standby_feedback turned off on # the standby. -reactive_slots_change_hfs_and_wait_for_xmins('behaves_ok_', 'vacuum_full_', - 0, 1); +reactive_slots_change_hfs_and_wait_for_xmins('vacuum_full_', 0, 1, 0); # This should trigger the conflict wait_until_vacuum_can_remove( @@ -550,19 +570,11 @@ wait_until_vacuum_can_remove( $node_primary->wait_for_replay_catchup($node_standby); # Check invalidation in the logfile and in pg_stat_database_conflicts -check_for_invalidation('vacuum_full_', 1, 'with vacuum FULL on pg_class'); +check_for_invalidation('vacuum_full_', 1, 'with vacuum FULL on pg_class', 0); # Verify slots are reported as conflicting in pg_replication_slots check_slots_conflicting_status(1); -$handle = - make_slot_active($node_standby, 'vacuum_full_', 0, \$stdout, \$stderr); - -# We are not able to read from the slot as it has been invalidated -check_pg_recvlogical_stderr($handle, - "can no longer get changes from replication slot \"vacuum_full_activeslot\"" -); - # Turn hot_standby_feedback back on change_hot_standby_feedback_and_wait_for_xmins(1, 1); @@ -580,7 +592,7 @@ check_slots_conflicting_status(1); # Get the restart_lsn from an invalidated slot my $restart_lsn = $node_standby->safe_psql('postgres', - "SELECT restart_lsn from pg_replication_slots WHERE slot_name = 'vacuum_full_activeslot' and conflicting is true;" + "SELECT restart_lsn from pg_replication_slots WHERE slot_name = 'vacuum_full_inactiveslot' and conflicting is true;" ); chomp($restart_lsn); @@ -615,14 +627,16 @@ ok(!-f "$standby_walfile", # Scenario 2: conflict due to row removal with hot_standby_feedback off. ################################################## +# drop the logical slots used by previous tests +drop_logical_slots('vacuum_full_', 0); + # get the position to search from in the standby logfile my $logstart = -s $node_standby->logfile; # One way to produce recovery conflict is to create/drop a relation and # launch a vacuum on pg_class with hot_standby_feedback turned off on the # standby. -reactive_slots_change_hfs_and_wait_for_xmins('vacuum_full_', 'row_removal_', - 0, 1); +reactive_slots_change_hfs_and_wait_for_xmins('row_removal_', 0, 1, 0); # This should trigger the conflict wait_until_vacuum_can_remove( @@ -632,32 +646,26 @@ wait_until_vacuum_can_remove( $node_primary->wait_for_replay_catchup($node_standby); # Check invalidation in the logfile and in pg_stat_database_conflicts -check_for_invalidation('row_removal_', $logstart, 'with vacuum on pg_class'); +check_for_invalidation('row_removal_', $logstart, 'with vacuum on pg_class', 0); # Verify slots are reported as conflicting in pg_replication_slots check_slots_conflicting_status(1); -$handle = - make_slot_active($node_standby, 'row_removal_', 0, \$stdout, \$stderr); - -# We are not able to read from the slot as it has been invalidated -check_pg_recvlogical_stderr($handle, - "can no longer get changes from replication slot \"row_removal_activeslot\"" -); - ################################################## # Recovery conflict: Same as Scenario 2 but on a shared catalog table # Scenario 3: conflict due to row removal with hot_standby_feedback off. ################################################## +# drop the logical slots used by previous tests +drop_logical_slots('row_removal_', 0); + # get the position to search from in the standby logfile $logstart = -s $node_standby->logfile; # One way to produce recovery conflict on a shared catalog table is to # create/drop a role and launch a vacuum on pg_authid with # hot_standby_feedback turned off on the standby. -reactive_slots_change_hfs_and_wait_for_xmins('row_removal_', - 'shared_row_removal_', 0, 1); +reactive_slots_change_hfs_and_wait_for_xmins('shared_row_removal_', 0, 1, 0); # Trigger the conflict wait_until_vacuum_can_remove( @@ -668,29 +676,23 @@ $node_primary->wait_for_replay_catchup($node_standby); # Check invalidation in the logfile and in pg_stat_database_conflicts check_for_invalidation('shared_row_removal_', $logstart, - 'with vacuum on pg_authid'); + 'with vacuum on pg_authid', 0); # Verify slots are reported as conflicting in pg_replication_slots check_slots_conflicting_status(1); -$handle = make_slot_active($node_standby, 'shared_row_removal_', 0, \$stdout, - \$stderr); - -# We are not able to read from the slot as it has been invalidated -check_pg_recvlogical_stderr($handle, - "can no longer get changes from replication slot \"shared_row_removal_activeslot\"" -); - ################################################## # Recovery conflict: Same as Scenario 2 but on a non catalog table # Scenario 4: No conflict expected. ################################################## +# drop the logical slots used by previous tests +drop_logical_slots('shared_row_removal_', 0); + # get the position to search from in the standby logfile $logstart = -s $node_standby->logfile; -reactive_slots_change_hfs_and_wait_for_xmins('shared_row_removal_', - 'no_conflict_', 0, 1); +reactive_slots_change_hfs_and_wait_for_xmins('no_conflict_', 0, 1); # This should not trigger a conflict wait_until_vacuum_can_remove( @@ -733,13 +735,15 @@ $node_standby->restart; # Scenario 5: conflict due to on-access pruning. ################################################## +# drop the logical slots used by previous tests +drop_logical_slots('no_conflict_', 1); + # get the position to search from in the standby logfile $logstart = -s $node_standby->logfile; # One way to produce recovery conflict is to trigger an on-access pruning # on a relation marked as user_catalog_table. -reactive_slots_change_hfs_and_wait_for_xmins('no_conflict_', 'pruning_', 0, - 0); +reactive_slots_change_hfs_and_wait_for_xmins('pruning_', 0, 0, 0); # This should trigger the conflict $node_primary->safe_psql('testdb', @@ -754,17 +758,13 @@ $node_primary->safe_psql('testdb', qq[UPDATE prun SET s = 'E';]); $node_primary->wait_for_replay_catchup($node_standby); # Check invalidation in the logfile and in pg_stat_database_conflicts -check_for_invalidation('pruning_', $logstart, 'with on-access pruning'); +check_for_invalidation('pruning_', $logstart, 'with on-access pruning', 0); # Verify slots are reported as conflicting in pg_replication_slots check_slots_conflicting_status(1); $handle = make_slot_active($node_standby, 'pruning_', 0, \$stdout, \$stderr); -# We are not able to read from the slot as it has been invalidated -check_pg_recvlogical_stderr($handle, - "can no longer get changes from replication slot \"pruning_activeslot\""); - # Turn hot_standby_feedback back on change_hot_standby_feedback_and_wait_for_xmins(1, 1); @@ -777,10 +777,10 @@ change_hot_standby_feedback_and_wait_for_xmins(1, 1); $logstart = -s $node_standby->logfile; # drop the logical slots -drop_logical_slots('pruning_'); +drop_logical_slots('pruning_', 0); # create the logical slots -create_logical_slots($node_standby, 'wal_level_'); +create_logical_slots($node_standby, 'wal_level_', 1); $handle = make_slot_active($node_standby, 'wal_level_', 1, \$stdout, \$stderr); @@ -798,7 +798,7 @@ $node_primary->restart; $node_primary->wait_for_replay_catchup($node_standby); # Check invalidation in the logfile and in pg_stat_database_conflicts -check_for_invalidation('wal_level_', $logstart, 'due to wal_level'); +check_for_invalidation('wal_level_', $logstart, 'due to wal_level', 1); # Verify slots are reported as conflicting in pg_replication_slots check_slots_conflicting_status(1); @@ -830,10 +830,10 @@ check_pg_recvlogical_stderr($handle, ################################################## # drop the logical slots -drop_logical_slots('wal_level_'); +drop_logical_slots('wal_level_', 1); # create the logical slots -create_logical_slots($node_standby, 'drop_db_'); +create_logical_slots($node_standby, 'drop_db_', 1); $handle = make_slot_active($node_standby, 'drop_db_', 1, \$stdout, \$stderr); @@ -897,14 +897,14 @@ $node_cascading_standby->append_conf( $node_cascading_standby->start; # create the logical slots -create_logical_slots($node_standby, 'promotion_'); +create_logical_slots($node_standby, 'promotion_', 1); # Wait for the cascading standby to catchup before creating the slots $node_standby->wait_for_replay_catchup($node_cascading_standby, $node_primary); # create the logical slots on the cascading standby too -create_logical_slots($node_cascading_standby, 'promotion_'); +create_logical_slots($node_cascading_standby, 'promotion_', 1); # Make slots actives $handle = -- 2.43.5