From 058794eda6e5156d4b6f2080f76515060f04d541 Mon Sep 17 00:00:00 2001
From: "Andrey M. Borodin" <x4mmm@flight.local>
Date: Sat, 12 Feb 2022 13:51:06 +0500
Subject: [PATCH v2] Test B-tree index on standby during REINDEX CONCURRENTLY

Currently we do not transfer WaitForLockersMultiple() information
to standby. This may lead to improper index use.
---
 contrib/amcheck/t/004_cic_standby.pl     | 96 ++++++++++++++++++++++++
 src/test/perl/PostgreSQL/Test/Cluster.pm | 48 ++++++++++++
 2 files changed, 144 insertions(+)
 create mode 100644 contrib/amcheck/t/004_cic_standby.pl

diff --git a/contrib/amcheck/t/004_cic_standby.pl b/contrib/amcheck/t/004_cic_standby.pl
new file mode 100644
index 0000000000..16b361cc64
--- /dev/null
+++ b/contrib/amcheck/t/004_cic_standby.pl
@@ -0,0 +1,96 @@
+
+# Copyright (c) 2021-2022, PostgreSQL Global Development Group
+
+# Minimal test testing REINDEX CONCURRENTLY with streaming replication
+use strict;
+use warnings;
+use Config;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 4;
+
+# Initialize primary node
+my $node_primary = PostgreSQL::Test::Cluster->new('primary');
+# A specific role is created for replication purposes
+$node_primary->init(
+	allows_streaming => 1,
+	auth_extra       => [ '--create-role', 'repl_role' ]);
+$node_primary->append_conf('postgresql.conf', 'lock_timeout = 180000');
+$node_primary->start;
+my $backup_name = 'my_backup';
+
+# Take backup
+$node_primary->backup($backup_name);
+
+# Create streaming standby linking to primary
+my $node_standby_1 = PostgreSQL::Test::Cluster->new('standby_1');
+$node_standby_1->init_from_backup($node_primary, $backup_name,
+	has_streaming => 1);
+$node_standby_1->start;
+
+# Create some content on primary and check its presence in standby nodes
+$node_primary->safe_psql('postgres', q(CREATE EXTENSION amcheck));
+$node_primary->safe_psql('postgres', q(CREATE TABLE tbl(i int)));
+$node_primary->safe_psql('postgres', q(CREATE INDEX idx ON tbl(i)));
+
+# Wait for standbys to catch up
+my $primary_lsn = $node_primary->lsn('write');
+$node_primary->wait_for_catchup($node_standby_1, 'replay', $primary_lsn);
+
+#
+# Stress CIC with pgbench
+#
+
+# Run background pgbench with bt_index_check on standby
+my $pgbench_out   = '';
+my $pgbench_timer = IPC::Run::timeout(180);
+my $pgbench_h     = $node_standby_1->background_pgbench(
+	'--no-vacuum --client=1 --time=30',
+	{
+		'004_pgbench_standby_check_1' => q(
+			set enable_bitmapscan to off;
+			set enable_seqscan to off;
+			\set id random(1, 1000000)
+			select i from tbl where i = :id;
+		   )
+	},
+	\$pgbench_out,
+	$pgbench_timer);
+
+# Run pgbench with data data manipulations and REINDEX on primary.
+# pgbench might try to launch more than one instance of the RIC
+# transaction concurrently.  That would deadlock, so use an advisory
+# lock to ensure only one CIC runs at a time.
+$node_primary->pgbench(
+	'--no-vacuum --client=5 --time=30',
+	0,
+	[qr{actually processed}],
+	[qr{^$}],
+	'concurrent INSERTs and CIC',
+	{
+		'004_pgbench_concurrent_transaction' => q(
+			BEGIN;
+			INSERT INTO tbl VALUES((random()*1000000)::int);
+			COMMIT;
+		  ),
+		'004_pgbench_concurrent_ric' => q(
+			SELECT pg_try_advisory_lock(42)::integer AS gotlock \gset
+			\if :gotlock
+                REINDEX INDEX CONCURRENTLY idx;
+				SELECT pg_advisory_unlock(42);
+			\endif
+		  )
+	});
+
+$pgbench_h->pump_nb;
+$pgbench_h->finish();
+my $result =
+    ($Config{osname} eq "MSWin32")
+  ? ($pgbench_h->full_results)[0]
+  : $pgbench_h->result(0);
+is($result, 0, "pgbench with bt_index_check() on standby works");
+
+# done
+$node_primary->stop;
+$node_standby_1->stop;
+done_testing();
\ No newline at end of file
diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 9a2ada0a10..c88a140334 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -2099,6 +2099,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.24.3 (Apple Git-128)

