From 16702f41408fec8d7a35868782fa6ca68226f715 Mon Sep 17 00:00:00 2001 From: Shlok Kyal Date: Wed, 24 Sep 2025 15:26:37 +0530 Subject: [PATCH v3 2/2] Add test for new stats for slot sync skip --- src/backend/replication/logical/slotsync.c | 5 + src/test/recovery/meson.build | 1 + src/test/recovery/t/049_slot_skip_stats.pl | 184 +++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 src/test/recovery/t/049_slot_skip_stats.pl diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c index 6259fad894c..4359120165e 100644 --- a/src/backend/replication/logical/slotsync.c +++ b/src/backend/replication/logical/slotsync.c @@ -64,6 +64,7 @@ #include "storage/procarray.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" +#include "utils/injection_point.h" #include "utils/pg_lsn.h" #include "utils/ps_status.h" #include "utils/timeout.h" @@ -994,6 +995,10 @@ synchronize_slots(WalReceiverConn *wrconn) if (started_tx) CommitTransactionCommand(); +#ifdef USE_INJECTION_POINTS + INJECTION_POINT("slot-sync-skip", NULL); +#endif + return some_slot_updated; } diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build index 52993c32dbb..640cbb2b796 100644 --- a/src/test/recovery/meson.build +++ b/src/test/recovery/meson.build @@ -57,6 +57,7 @@ tests += { 't/046_checkpoint_logical_slot.pl', 't/047_checkpoint_physical_slot.pl', 't/048_vacuum_horizon_floor.pl' + 't/049_slot_skip_stats.pl' ], }, } diff --git a/src/test/recovery/t/049_slot_skip_stats.pl b/src/test/recovery/t/049_slot_skip_stats.pl new file mode 100644 index 00000000000..21f4859f9c1 --- /dev/null +++ b/src/test/recovery/t/049_slot_skip_stats.pl @@ -0,0 +1,184 @@ +# Copyright (c) 2024-2025, PostgreSQL Global Development Group + +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 $primary = PostgreSQL::Test::Cluster->new('publisher'); +$primary->init( + allows_streaming => 'logical', + auth_extra => [ '--create-role' => 'repl_role' ]); +$primary->append_conf( + 'postgresql.conf', qq{ +autovacuum = off +max_prepared_transactions = 1 +}); +$primary->start; + +$primary->safe_psql('postgres', q(CREATE EXTENSION injection_points)); + +my $backup_name = 'backup'; +$primary->backup($backup_name); + +my $standby1 = PostgreSQL::Test::Cluster->new('standby1'); +$standby1->init_from_backup( + $primary, $backup_name, + has_streaming => 1, + has_restoring => 1); + +my $connstr_1 = $primary->connstr; +$standby1->append_conf( + 'postgresql.conf', qq( +hot_standby_feedback = on +primary_slot_name = 'sb1_slot' +primary_conninfo = '$connstr_1 dbname=postgres' +log_min_messages = 'debug2' +)); +$primary->psql('postgres', + q{SELECT pg_create_physical_replication_slot('sb1_slot');}); + +$standby1->start; + +$primary->safe_psql('postgres', + "SELECT pg_create_logical_replication_slot('slot_sync', 'test_decoding', false, false, true)" +); +$primary->wait_for_replay_catchup($standby1); + +$standby1->safe_psql('postgres', "SELECT pg_sync_replication_slots();"); + +my $result = $standby1->safe_psql( + 'postgres', + "SELECT slot_sync_skip_reason + FROM pg_replication_slots + WHERE slot_name = 'slot_sync' + AND synced" +); +is($result, 'none', "slot sync reason is none"); + + +# Change pg_hba.conf so that standby cannot connect to primary +unlink($primary->data_dir . '/pg_hba.conf'); +$primary->append_conf('pg_hba.conf', + qq{local all all trust} +); +$primary->restart; + +# Advance the failover slot so that confirmed flush LSN of remote slot become +# ahead of standby's flushed LSN +$primary->safe_psql( + 'postgres', qq( + CREATE TABLE t1(a int); + INSERT INTO t1 values(1); +)); +$primary->safe_psql('postgres', + "SELECT pg_replication_slot_advance('slot_sync', pg_current_wal_lsn());"); + +$standby1->psql('postgres', "SELECT pg_sync_replication_slots();"); + +# Confirm that standby is behind +$result = $standby1->safe_psql( + 'postgres', + "SELECT slot_sync_skip_reason + FROM pg_replication_slots + WHERE slot_name = 'slot_sync' + AND synced" +); +is($result, 'missing_wal_record', "slot sync skip when standby is behind"); + +$result = $standby1->safe_psql( + 'postgres', + "SELECT slot_sync_skip_count + FROM pg_stat_replication_slots + WHERE slot_name = 'slot_sync'" +); +is($result, '1', "check slot sync skip count"); + +# Repeat pg_sync_replication_slots to check slot_sync_skip_count is advancing +$standby1->psql('postgres', "SELECT pg_sync_replication_slots();"); + +$result = $standby1->safe_psql( + 'postgres', + "SELECT slot_sync_skip_reason + FROM pg_replication_slots + WHERE slot_name = 'slot_sync' + AND synced" +); +is($result, 'missing_wal_record', "slot sync skip when standby is behind"); + +$result = $standby1->safe_psql( + 'postgres', + "SELECT slot_sync_skip_count + FROM pg_stat_replication_slots + WHERE slot_name = 'slot_sync'" +); +is($result, '2', "check slot sync skip count"); + +# Restore the connect between primary and standby +unlink($primary->data_dir . '/pg_hba.conf'); +$primary->append_conf( + 'pg_hba.conf', + qq{ +local all all trust +local replication all trust +}); +$primary->restart; + +# Cleanup +$primary->safe_psql('postgres', + "SELECT pg_drop_replication_slot('slot_sync')"); +$primary->wait_for_replay_catchup($standby1); + +$standby1->safe_psql('postgres', "SELECT pg_sync_replication_slots();"); + +# Create a new logical slot on primary +$primary->safe_psql('postgres', + "SELECT pg_create_logical_replication_slot('slot_sync', 'test_decoding', false, false, true)" +); + +# Attach injection point +my $standby_psql = $standby1->background_psql('postgres'); +$standby_psql->query_safe( + q(select injection_points_attach('slot-sync-skip','wait'))); + +# initiate sync the failover slots +$standby_psql->query_until( + qr/slot_sync/, + q( +\echo slot_sync +select pg_sync_replication_slots(); +)); + +$standby1->wait_for_event('client backend', 'slot-sync-skip'); + +# the logical slot is in temporary state and the sync will skip as remote is +# behind the freashly created slot +$result = $standby1->safe_psql( + 'postgres', + "SELECT slot_sync_skip_reason + FROM pg_replication_slots + WHERE slot_name = 'slot_sync' + AND synced" +); +is($result, 'remote_behind', "slot sync skip as remote is behind"); + +$result = $standby1->safe_psql( + 'postgres', + "SELECT slot_sync_skip_count + FROM pg_stat_replication_slots + WHERE slot_name = 'slot_sync'" +); +is($result, '1', "check slot sync skip cout"); + +$standby1->safe_psql('postgres', + q{select injection_points_detach('slot-sync-skip')}); +$standby1->safe_psql('postgres', + q{select injection_points_wakeup('slot-sync-skip')}); + +done_testing(); -- 2.34.1