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
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