v1-0001-Fix-WAIT-FOR-LSN-cleanup-on-subtransaction-abort.patch

application/octet-stream

Filename: v1-0001-Fix-WAIT-FOR-LSN-cleanup-on-subtransaction-abort.patch
Type: application/octet-stream
Part: 0
Message: [PATCH] Fix WAIT FOR LSN cleanup on subtransaction abort
From b34f7771c58fc1dc4f6d0f9307a296ae5ed1622e Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
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