From 818be9b73c945e480805fff7e24101da161f71d1 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Wed, 20 Aug 2025 15:00:14 +0900 Subject: [PATCH] Fix assertion failure with replication slot release in single-user mode Some replication slot manipulations (logical decoding via SQL, advancing) were failing an assertion when releasing a slot in single-user mode, because active_pid was not set in a ReplicationSlot when its slot is acquired. ReplicationSlotAcquire() has some logic to be able to work with the single-user mode. This commit sets ReplicationSlot->active_pid to MyProcPid, to let the slot-related logic fall-through, considering the single process as the one holding the slot. Some TAP tests are added for various replication slot functions with the single-user mode, while on it, for slot creation, drop, advancing, copy and logical decoding with multiple slot types (temporary, physical vs logical). These tests are skipped on Windows, as direct calls of postgres --single would fail on permission failures. There is no platform-specific behavior that needs to be checked, so living with this restriction should be fine. The CI is OK with that, now let's see what the buildfarm tells. Author: Hayato Kuroda Reviewed-by: Paul A. Jungwirth Reviewed-by: Mutaamba Maasha Discussion: https://postgr.es/m/OSCPR01MB14966ED588A0328DAEBE8CB25F5FA2@OSCPR01MB14966.jpnprd01.prod.outlook.com Backpatch-through: 13 --- src/backend/replication/slot.c | 2 +- src/test/modules/test_misc/Makefile | 2 + .../test_misc/t/008_replslot_single_user.pl | 95 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/test/modules/test_misc/t/008_replslot_single_user.pl diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index 80d96db8eb9..7defd3391b0 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -494,7 +494,7 @@ retry: SpinLockRelease(&s->mutex); } else - active_pid = MyProcPid; + s->active_pid = active_pid = MyProcPid; LWLockRelease(ReplicationSlotControlLock); /* diff --git a/src/test/modules/test_misc/Makefile b/src/test/modules/test_misc/Makefile index 39c6c2014a0..06a8ee9d03a 100644 --- a/src/test/modules/test_misc/Makefile +++ b/src/test/modules/test_misc/Makefile @@ -2,6 +2,8 @@ TAP_TESTS = 1 +EXTRA_INSTALL = contrib/test_decoding + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/src/test/modules/test_misc/t/008_replslot_single_user.pl b/src/test/modules/test_misc/t/008_replslot_single_user.pl new file mode 100644 index 00000000000..796700d621f --- /dev/null +++ b/src/test/modules/test_misc/t/008_replslot_single_user.pl @@ -0,0 +1,95 @@ +# Copyright (c) 2025, PostgreSQL Global Development Group + +# Test manipulations of replication slots with the single-user mode. + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +# Skip the tests on Windows, as single-user mode would fail on permission +# failure with privileged accounts. +if ($windows_os) +{ + plan skip_all => 'this test is not supported by this platform'; +} + +# Run set of queries in single-user mode. +sub test_single_mode +{ + my ($node, $queries, $testname) = @_; + + my $result = run_log( + [ + 'postgres', '--single', '-F', + '-c' => 'exit_on_error=true', + '-D' => $node->data_dir, + 'postgres' + ], + '<' => \$queries); + + ok($result, $testname); +} + +my $slot_logical = 'slot_logical'; +my $slot_physical = 'slot_physical'; + +# Initialize a node +my $node = PostgreSQL::Test::Cluster->new('node'); +$node->init(allows_streaming => "logical"); +$node->start; + +# Define initial table +$node->safe_psql('postgres', "CREATE TABLE foo (id int)"); + +$node->stop; + +test_single_mode( + $node, + "SELECT pg_create_logical_replication_slot('$slot_logical', 'test_decoding')", + "logical slot creation"); +test_single_mode( + $node, + "SELECT pg_create_physical_replication_slot('$slot_physical', true)", + "physical slot creation"); +test_single_mode( + $node, + "SELECT pg_create_physical_replication_slot('slot_tmp', true, true)", + "temporary physical slot creation"); + +test_single_mode( + $node, qq( +INSERT INTO foo VALUES (1); +SELECT pg_logical_slot_get_changes('$slot_logical', NULL, NULL); +), + "logical decoding"); + +test_single_mode( + $node, + "SELECT pg_replication_slot_advance('$slot_logical', pg_current_wal_lsn())", + "logical slot advance"); +test_single_mode( + $node, + "SELECT pg_replication_slot_advance('$slot_physical', pg_current_wal_lsn())", + "physical slot advance"); + +test_single_mode( + $node, + "SELECT pg_copy_logical_replication_slot('$slot_logical', 'slot_log_copy')", + "logical slot copy"); +test_single_mode( + $node, + "SELECT pg_copy_physical_replication_slot('$slot_physical', 'slot_phy_copy')", + "physical slot copy"); + +test_single_mode( + $node, + "SELECT pg_drop_replication_slot('$slot_logical')", + "logical slot drop"); +test_single_mode( + $node, + "SELECT pg_drop_replication_slot('$slot_physical')", + "physical slot drop"); + +done_testing(); -- 2.47.2