From cf8acadbd94b165ac834f0f382235820aa5699b0 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Date: Fri, 30 Oct 2026 21:06:47 +0900
Subject: [PATCH 3/3] Stabilize 010_index_concurrently_upsert

---
 .../t/010_index_concurrently_upsert.pl        | 55 +++++++++++++------
 1 file changed, 39 insertions(+), 16 deletions(-)

diff --git a/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
index 50a0e7db8f7..bc3fade60fe 100644
--- a/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
+++ b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
@@ -117,7 +117,7 @@ wakeup_injection_point($node,
 # Wakeup s2 (exec-insert-before-insert-speculative)
 wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 # Cleanup test 1
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
@@ -177,7 +177,7 @@ wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 wakeup_injection_point($node,
 	'check-exclusion-or-unique-constraint-no-conflict');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
 
@@ -238,7 +238,7 @@ wakeup_injection_point($node,
 	'reindex-relation-concurrently-before-set-dead');
 wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
 
@@ -298,7 +298,7 @@ wakeup_injection_point($node,
 	'check-exclusion-or-unique-constraint-no-conflict');
 wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
 
@@ -357,7 +357,7 @@ wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 wakeup_injection_point($node,
 	'check-exclusion-or-unique-constraint-no-conflict');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
 
@@ -418,7 +418,7 @@ wakeup_injection_point($node,
 	'reindex-relation-concurrently-before-set-dead');
 wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
 
@@ -478,7 +478,7 @@ wakeup_injection_point($node,
 	'check-exclusion-or-unique-constraint-no-conflict');
 wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
 
@@ -537,7 +537,7 @@ wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 wakeup_injection_point($node,
 	'check-exclusion-or-unique-constraint-no-conflict');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
 
@@ -598,7 +598,7 @@ wakeup_injection_point($node,
 	'reindex-relation-concurrently-before-set-dead');
 wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
 
@@ -645,7 +645,7 @@ wakeup_injection_point($node, 'reindex-relation-concurrently-before-swap');
 wakeup_injection_point($node,
 	'exec-init-partition-after-get-partition-ancestors');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
 
@@ -739,7 +739,7 @@ wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 wakeup_injection_point($node,
 	'check-exclusion-or-unique-constraint-no-conflict');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
 
@@ -828,7 +828,7 @@ wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
 wakeup_injection_point($node,
 	'check-exclusion-or-unique-constraint-no-conflict');
 
-clean_safe_quit_ok($s1, $s2, $s3);
+clean_safe_quit_ok($node, $s1, $s2, $s3);
 
 $node->safe_psql('postgres', 'TRUNCATE TABLE test.tblexpr');
 
@@ -921,7 +921,7 @@ SELECT injection_points_wakeup('$point_name');
 }
 
 # Wait for any pending query to complete, capture stderr, and close the session.
-# Returns the stderr output (excluding internal markers).
+# Returns the stderr output (excluding internal markers) and the backend PID.
 sub safe_quit
 {
 	my ($session) = @_;
@@ -943,18 +943,41 @@ sub safe_quit
 	my $stderr = $session->{stderr};
 	$stderr =~ s/$banner_match//;
 
+	# The session is idle now, so its backend PID can be queried safely.
+	$session->{stdout} = '';
+	$session->{stderr} = '';
+	my $pid = $session->query_safe('SELECT pg_backend_pid()', verbose => 0);
+	chomp $pid;
+
 	# Close the session
 	$session->quit;
 
-	return $stderr;
+	return ($stderr, $pid);
 }
 
-# Helper function: verify that the given sessions exit cleanly.
+# Helper function: verify that the given sessions exit cleanly, then wait for
+# their server backends to finish. This ensures that injection points are
+# cleaned up by a before_shmem_exit callback.
 sub clean_safe_quit_ok
 {
+	my $node = shift;
+	my @backend_pids;
 	my $i = 1;
 	foreach my $session (@_)
 	{
-		is(safe_quit($session), '', "session " . $i++ . " quit cleanly");
+		my ($stderr, $pid) = safe_quit($session);
+
+		is($stderr, '', "session " . $i++ . " quit cleanly");
+		die "invalid backend PID: $pid" unless $pid =~ /^\d+$/;
+		push @backend_pids, $pid;
 	}
+
+	# Confirmed all sessions are closed. Ensure all related backends have
+	# exited.
+	my $pid_list = join(',', @backend_pids);
+
+	$node->poll_query_until(
+		'postgres',
+		"SELECT count(*) = 0 FROM pg_stat_activity WHERE pid IN ($pid_list)")
+	  or die "timed out waiting for backends $pid_list to exit";
 }
-- 
2.52.0

