From f4ef4653e43bfaaecca7c1483d564f161a603ff6 Mon Sep 17 00:00:00 2001 From: "suyu.cmj" Date: Mon, 15 Sep 2025 08:44:17 +0000 Subject: [PATCH] Newly created replication slot may be invalidated by checkpoint Commit 2090edc6f32f652a2c995ca5f7e65748ae1e4c5d introduced a change that the minimal restart_lsn is obtained at the start of checkpoint creation. If a replication slot is created and performs a WAL reservation concurrently, the WAL segment contains the new slot's restart_lsn could be removed by the ongoing checkpoint. Add a perl test to reproduce this scenario. With some modification by v.davydov@postgrespro.ru --- src/backend/replication/slot.c | 5 ++ .../recovery/t/049_invalidate_new_slot.pl | 76 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/test/recovery/t/049_invalidate_new_slot.pl diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index b9e2b115dab..43adfb75499 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -57,6 +57,7 @@ #include "utils/builtins.h" #include "utils/guc_hooks.h" #include "utils/varlena.h" +#include "utils/injection_point.h" /* * Replication slot on-disk data structure. @@ -1443,6 +1444,10 @@ ReplicationSlotReserveWal(void) slot->data.restart_lsn = restart_lsn; SpinLockRelease(&slot->mutex); +#ifdef USE_INJECTION_POINTS + INJECTION_POINT("delay_slot_reserve_wal"); +#endif + /* prevent WAL removal as fast as possible */ ReplicationSlotsComputeRequiredLSN(); diff --git a/src/test/recovery/t/049_invalidate_new_slot.pl b/src/test/recovery/t/049_invalidate_new_slot.pl new file mode 100644 index 00000000000..93b09316e5f --- /dev/null +++ b/src/test/recovery/t/049_invalidate_new_slot.pl @@ -0,0 +1,76 @@ +# This test checks that the new slot maybe invalidated by checkpoint +# + +use strict; +use warnings; +use Config; +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'; +} + +# Setup primary node +my $node = PostgreSQL::Test::Cluster->new('primary'); +$node->init(allows_streaming => 1); + +$node->append_conf( + 'postgresql.conf', qq( + log_checkpoints = true + checkpoint_timeout = 3000 +)); +$node->start; + +my $res = + $node->safe_psql('postgres', "create extension injection_points;"); + +$res = $node->safe_psql('postgres', + "select injection_points_attach('delay_slot_reserve_wal', 'wait')"); + +my $createslot = $node->background_psql('postgres'); + +$createslot->query_until( + qr/start-create-slot/, + q(\echo start-create-slot +select pg_create_physical_replication_slot('standby1', true); +)); + +# Wait until the checkpoint stops right before removing WAL segments. +note('waiting for injection_point'); +$node->wait_for_event('client backend', 'delay_slot_reserve_wal'); +note('injection_point is reached'); + +$node->advance_wal(3); + +my $checkpoint = $node->background_psql('postgres'); +$checkpoint->query_safe(q{select injection_points_attach('checkpoint-before-old-wal-removal','wait')}); +$checkpoint->query_until( + qr/start-checkpoint/, + q(\echo start-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'); + +# continue create slot command +$node->safe_psql('postgres', q{select injection_points_wakeup('delay_slot_reserve_wal')}); + +$createslot->quit(); + +# continue checkpoint +$node->safe_psql('postgres', q{select injection_points_wakeup('checkpoint-before-old-wal-removal')}); + +# check if slot is invalidated +$res = $node->safe_psql('postgres', + "select invalidation_reason from pg_replication_slots where slot_name='standby1'"); +print "res:$res\n"; +is($res, "wal_removed", "slot is invalidated by checkpoint"); + +done_testing(); -- 2.34.1