From a18656a6059d5605a62ffd729b10e8f2856a7df8 Mon Sep 17 00:00:00 2001 From: Zhijie Hou Date: Wed, 19 Nov 2025 18:25:48 +0800 Subject: [PATCH v4 2/2] Add a tap-test using injection point --- src/backend/access/transam/xlog.c | 3 + .../recovery/t/046_checkpoint_logical_slot.pl | 81 ++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 17324259cff..56c69574238 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7838,6 +7838,9 @@ CreateRestartPoint(int flags) replayPtr = GetXLogReplayRecPtr(&replayTLI); endptr = (receivePtr < replayPtr) ? replayPtr : receivePtr; KeepLogSeg(endptr, &_logSegNo); + + INJECTION_POINT("restartpoint-before-slot-invalidation", NULL); + if (InvalidateObsoleteReplicationSlots(RS_INVAL_WAL_REMOVED | RS_INVAL_IDLE_TIMEOUT, _logSegNo, InvalidOid, InvalidTransactionId)) diff --git a/src/test/recovery/t/046_checkpoint_logical_slot.pl b/src/test/recovery/t/046_checkpoint_logical_slot.pl index 0242a1573ca..339289c0b59 100644 --- a/src/test/recovery/t/046_checkpoint_logical_slot.pl +++ b/src/test/recovery/t/046_checkpoint_logical_slot.pl @@ -20,8 +20,7 @@ if ($ENV{enable_injection_points} ne 'yes') my ($node, $result); $node = PostgreSQL::Test::Cluster->new('mike'); -$node->init; -$node->append_conf('postgresql.conf', "wal_level = 'logical'"); +$node->init(allows_streaming => 'logical'); $node->start; # Check if the extension injection_points is available, as it may be @@ -139,4 +138,82 @@ eval { }; is($@, '', "Logical slot still valid"); +# Verify that while syncing a slot to the standby server, if the WAL before the +# remote restart_lsn is at risk of being removed by a checkpoint, the slot +# cannot be synced. Otherwise, even if the slot syncing succeeds, it may be +# immediately invalidated by the checkpoint. +my $primary = $node; + +$primary->append_conf('postgresql.conf', "autovacuum = off"); +$primary->reload; + +my $backup_name = 'backup'; + +$primary->backup($backup_name); + +# Create a standby +my $standby = PostgreSQL::Test::Cluster->new('standby'); +$standby->init_from_backup( + $primary, $backup_name, + has_streaming => 1, + has_restoring => 1); + +# Increase the log_min_messages setting to DEBUG2 on both the standby and +# primary to debug test failures, if any. +my $connstr_1 = $primary->connstr; +$standby->append_conf( + 'postgresql.conf', qq( +hot_standby_feedback = on +primary_slot_name = 'phys_slot' +primary_conninfo = '$connstr_1 dbname=postgres' +log_min_messages = 'debug2' +)); + +$primary->psql('postgres', + q{SELECT pg_create_logical_replication_slot('failover_slot', 'test_decoding', false, false, true); + SELECT pg_create_physical_replication_slot('phys_slot');} +); + +$standby->start; + +# Generate some activity and switch WAL file on the primary +$primary->advance_wal(1); +$primary->psql('postgres', "CHECKPOINT"); +$primary->wait_for_replay_catchup($standby); + +# checkpoint on the standby and make it wait on the injection point so that the +# checkpoint stops right before invalidating replication slots. +note('starting checkpoint'); + +$checkpoint = $standby->background_psql('postgres'); +$checkpoint->query_safe( + q(select injection_points_attach('restartpoint-before-slot-invalidation','wait')) +); +$checkpoint->query_until( + qr/starting_checkpoint/, + q(\echo starting_checkpoint +checkpoint; +\q +)); + +# Wait until the checkpoint stops right before invalidating slots +note('waiting for injection_point'); +$standby->wait_for_event('checkpointer', 'restartpoint-before-slot-invalidation'); +note('injection_point is reached'); + +# Synchronize the failover slot to the standby +$standby->safe_psql('postgres', "SELECT pg_sync_replication_slots();"); + +# Confirm that the logical slot is not synced +is( $standby->safe_psql( + 'postgres', + q{SELECT count(*) = 0 FROM pg_replication_slots WHERE slot_name = 'failover_slot';} + ), + "t", + 'logical slot is not synced because the required WALs could be potentially removed'); + +$standby->safe_psql('postgres', + q{select injection_points_wakeup('restartpoint-before-slot-invalidation'); + select injection_points_detach('restartpoint-before-slot-invalidation')}); + done_testing(); -- 2.51.1.windows.1