From ef3d301697afabc3ad121dccbbcaba0149d1df25 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@flight.local>
Date: Thu, 10 Feb 2022 21:47:44 +0500
Subject: [PATCH] Try to check indexes on standby

---
 contrib/amcheck/t/002_cic.pl             | 48 +++++++++++++--------
 contrib/amcheck/t/003_cic_2pc.pl         | 53 +++++++++++++-----------
 src/test/perl/PostgreSQL/Test/Cluster.pm | 48 +++++++++++++++++++++
 3 files changed, 108 insertions(+), 41 deletions(-)

diff --git a/contrib/amcheck/t/002_cic.pl b/contrib/amcheck/t/002_cic.pl
index f6b91e89ad..58689423c9 100644
--- a/contrib/amcheck/t/002_cic.pl
+++ b/contrib/amcheck/t/002_cic.pl
@@ -9,7 +9,7 @@ use Config;
 use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::Utils;
 
-use Test::More tests => 3;
+use Test::More tests => 4;
 
 my ($node, $result);
 
@@ -25,18 +25,32 @@ $node->safe_psql('postgres', q(CREATE TABLE tbl(i int)));
 $node->safe_psql('postgres', q(CREATE INDEX idx ON tbl(i)));
 
 #
-# Stress CIC with pgbench.
-#
-# pgbench might try to launch more than one instance of the CIC
-# transaction concurrently.  That would deadlock, so use an advisory
-# lock to ensure only one CIC runs at a time.
+# Stress CIC with pgbench
 #
+
+# Run background pgbench with CIC. We cannot mix-in this script into single
+# pgbench: CIC will deadlock with itself occasionally.
+my $pgbench_out   = '';
+my $pgbench_timer = IPC::Run::timeout(180);
+my $pgbench_h     = $node->background_pgbench(
+	'--no-vacuum --client=1 --transactions=200',
+	{
+		'002_pgbench_concurrent_cic' => q(
+			DROP INDEX CONCURRENTLY idx;
+			CREATE INDEX CONCURRENTLY idx ON tbl(i);
+			SELECT bt_index_check('idx',true);
+		   )
+	},
+	\$pgbench_out,
+	$pgbench_timer);
+
+# Run pgbench.
 $node->pgbench(
-	'--no-vacuum --client=5 --transactions=100',
+	'--no-vacuum --client=5 --transactions=200',
 	0,
 	[qr{actually processed}],
 	[qr{^$}],
-	'concurrent INSERTs and CIC',
+	'concurrent INSERTs',
 	{
 		'002_pgbench_concurrent_transaction' => q(
 			BEGIN;
@@ -48,17 +62,17 @@ $node->pgbench(
 			SAVEPOINT s1;
 			INSERT INTO tbl VALUES(0);
 			COMMIT;
-		  ),
-		'002_pgbench_concurrent_cic' => q(
-			SELECT pg_try_advisory_lock(42)::integer AS gotlock \gset
-			\if :gotlock
-				DROP INDEX CONCURRENTLY idx;
-				CREATE INDEX CONCURRENTLY idx ON tbl(i);
-				SELECT bt_index_check('idx',true);
-				SELECT pg_advisory_unlock(42);
-			\endif
 		  )
 	});
 
+$pgbench_h->pump_nb;
+$pgbench_h->finish();
+$result =
+    ($Config{osname} eq "MSWin32")
+  ? ($pgbench_h->full_results)[0]
+  : $pgbench_h->result(0);
+is($result, 0, "pgbench with CIC works");
+
+# done
 $node->stop;
 done_testing();
diff --git a/contrib/amcheck/t/003_cic_2pc.pl b/contrib/amcheck/t/003_cic_2pc.pl
index 2f804efb84..e78de60e64 100644
--- a/contrib/amcheck/t/003_cic_2pc.pl
+++ b/contrib/amcheck/t/003_cic_2pc.pl
@@ -9,7 +9,7 @@ use Config;
 use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::Utils;
 
-use Test::More tests => 5;
+use Test::More tests => 6;
 
 local $TODO = 'filesystem bug' if PostgreSQL::Test::Utils::has_wal_read_bug;
 
@@ -134,52 +134,57 @@ is($result, '0', 'bt_index_check after 2PC and restart');
 #
 # Stress CIC+2PC with pgbench
 #
-# pgbench might try to launch more than one instance of the CIC
-# transaction concurrently.  That would deadlock, so use an advisory
-# lock to ensure only one CIC runs at a time.
 
 # Fix broken index first
 $node->safe_psql('postgres', q(REINDEX TABLE tbl;));
 
+# Run background pgbench with CIC. We cannot mix-in this script into single
+# pgbench: CIC will deadlock with itself occasionally.
+my $pgbench_out   = '';
+my $pgbench_timer = IPC::Run::timeout(180);
+my $pgbench_h     = $node->background_pgbench(
+	'--no-vacuum --client=1 --transactions=100',
+	{
+		'002_pgbench_concurrent_cic' => q(
+			DROP INDEX CONCURRENTLY idx;
+			CREATE INDEX CONCURRENTLY idx ON tbl(i);
+			SELECT bt_index_check('idx',true);
+		   )
+	},
+	\$pgbench_out,
+	$pgbench_timer);
+
 # Run pgbench.
 $node->pgbench(
 	'--no-vacuum --client=5 --transactions=100',
 	0,
 	[qr{actually processed}],
 	[qr{^$}],
-	'concurrent INSERTs w/ 2PC and CIC',
+	'concurrent INSERTs w/ 2PC',
 	{
-		'003_pgbench_concurrent_2pc' => q(
+		'002_pgbench_concurrent_2pc' => q(
 			BEGIN;
 			INSERT INTO tbl VALUES(0);
 			PREPARE TRANSACTION 'c:client_id';
 			COMMIT PREPARED 'c:client_id';
 		  ),
-		'003_pgbench_concurrent_2pc_savepoint' => q(
+		'002_pgbench_concurrent_2pc_savepoint' => q(
 			BEGIN;
 			SAVEPOINT s1;
 			INSERT INTO tbl VALUES(0);
 			PREPARE TRANSACTION 'c:client_id';
 			COMMIT PREPARED 'c:client_id';
-		  ),
-		'003_pgbench_concurrent_cic' => q(
-			SELECT pg_try_advisory_lock(42)::integer AS gotlock \gset
-			\if :gotlock
-				DROP INDEX CONCURRENTLY idx;
-				CREATE INDEX CONCURRENTLY idx ON tbl(i);
-				SELECT bt_index_check('idx',true);
-				SELECT pg_advisory_unlock(42);
-			\endif
-		  ),
-		'004_pgbench_concurrent_ric' => q(
-			SELECT pg_try_advisory_lock(42)::integer AS gotlock \gset
-			\if :gotlock
-				REINDEX INDEX CONCURRENTLY idx;
-				SELECT bt_index_check('idx',true);
-				SELECT pg_advisory_unlock(42);
-			\endif
 		  )
 	});
 
+$pgbench_h->pump_nb;
+$pgbench_h->finish();
+$result =
+    ($Config{osname} eq "MSWin32")
+  ? ($pgbench_h->full_results)[0]
+  : $pgbench_h->result(0);
+is($result, 0, "pgbench with CIC works");
+
+# done
 $node->stop;
 done_testing();
diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 41beadff9d..8041092edb 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -2142,6 +2142,54 @@ sub pgbench
 
 =pod
 
+=item $node->background_pgbench($opts, $files, \$stdout, $timer) => harness
+
+Invoke B<pgbench> and return an IPC::Run harness object.  The process's stdin
+is empty, and its stdout and stderr go to the $stdout scalar reference.  This
+allows the caller to act on other parts of the system while B<pgbench> is
+running.  Errors from B<pgbench> are the caller's problem.
+
+The specified timer object is attached to the harness, as well.  It's caller's
+responsibility to select the timeout length, and to restart the timer after
+each command if the timeout is per-command.
+
+Be sure to "finish" the harness when done with it.
+
+=over
+
+=item $opts
+
+Options as a string to be split on spaces.
+
+=item $files
+
+Reference to filename/contents dictionary.
+
+=back
+
+=cut
+
+sub background_pgbench
+{
+	my ($self, $opts, $files, $stdout, $timer) = @_;
+
+	my @cmd =
+	  ('pgbench', split(/\s+/, $opts), $self->_pgbench_make_files($files));
+
+	local %ENV = $self->_get_env();
+
+	my $stdin = "";
+	# IPC::Run would otherwise append to existing contents:
+	$$stdout = "" if ref($stdout);
+
+	my $harness = IPC::Run::start \@cmd, '<', \$stdin, '>', $stdout, '2>&1',
+	  $timer;
+
+	return $harness;
+}
+
+=pod
+
 =item $node->connect_ok($connstr, $test_name, %params)
 
 Attempt a connection with a custom connection string.  This is expected
-- 
2.32.0 (Apple Git-132)

