From b34f7771c58fc1dc4f6d0f9307a296ae5ed1622e Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 6 May 2026 11:37:36 +0530 Subject: [PATCH v1] Fix WAIT FOR LSN cleanup on subtransaction abort WAIT FOR LSN registers the current backend in shared memory before entering an interruptible wait loop. Top-level abort and backend exit already call WaitLSNCleanup(), but subtransaction abort did not. If an interrupt such as statement_timeout occurred while waiting inside a savepoint, rolling back to the savepoint left the backend marked as present in the WAIT FOR LSN heap. Clean up WAIT FOR LSN state from AbortSubTransaction() as well, and add a TAP test covering reuse of WAIT FOR LSN after a savepoint rollback. --- src/backend/access/transam/xact.c | 5 +++++ src/test/recovery/t/049_wait_for_lsn.pl | 28 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 48bc90c9673..5586fbe5b07 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -5289,6 +5289,11 @@ AbortSubTransaction(void) */ LWLockReleaseAll(); + /* + * Cleanup waiting for LSN if any. + */ + WaitLSNCleanup(); + pgstat_report_wait_end(); pgstat_progress_end_command(); diff --git a/src/test/recovery/t/049_wait_for_lsn.pl b/src/test/recovery/t/049_wait_for_lsn.pl index 9f8af351ba8..87e58b825cf 100644 --- a/src/test/recovery/t/049_wait_for_lsn.pl +++ b/src/test/recovery/t/049_wait_for_lsn.pl @@ -213,6 +213,34 @@ $output = $node_standby->safe_psql( WAIT FOR LSN '${lsn3}' WITH (timeout '10ms', no_throw);]); ok($output eq "timeout", "WAIT FOR returns correct status after timeout"); +# 4a. Check that aborting a subtransaction during WAIT FOR LSN cleans up the +# shared wait-state. The first WAIT FOR is interrupted by statement_timeout +# while still registered in the waiters heap. After rolling back to the +# savepoint, a second WAIT FOR in the same backend must be able to register +# itself again. +my $subxact_lsn = $node_primary->safe_psql('postgres', + "SELECT pg_current_wal_insert_lsn() + 10000000000"); +my ($subxact_ret, $subxact_stdout, $subxact_stderr) = $node_primary->psql( + 'postgres', qq[ + BEGIN; + SAVEPOINT wait_cleanup; + SET statement_timeout = '100ms'; + WAIT FOR LSN '${subxact_lsn}' WITH (MODE 'primary_flush'); + ROLLBACK TO wait_cleanup; + SET statement_timeout = 0; + WAIT FOR LSN '0/0' WITH (MODE 'primary_flush', timeout '10ms', no_throw); + COMMIT; +], + on_error_stop => 0); +is($subxact_ret, 0, + "WAIT FOR LSN can be reused after subtransaction abort"); +like($subxact_stderr, qr/canceling statement due to statement timeout/, + "statement timeout interrupted WAIT FOR LSN in subtransaction"); +is($subxact_stdout, "success", + "second WAIT FOR LSN completed after savepoint rollback"); +unlike($subxact_stderr, qr/server closed the connection unexpectedly/, + "WAIT FOR LSN after savepoint rollback did not disconnect"); + # 5. Check mode validation: standby modes error on primary, primary mode errors # on standby, and primary_flush works on primary. Also check that WAIT FOR # triggers an error if called within a function, procedure, anonymous DO block, -- 2.34.1