v5-0001-Introduce-TAP-test-for-2PC-with-CIC-behavior.patch
application/octet-stream
Filename: v5-0001-Introduce-TAP-test-for-2PC-with-CIC-behavior.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v5-0001
Subject: Introduce TAP test for 2PC with CIC behavior
| File | + | − |
|---|---|---|
| contrib/amcheck/Makefile | 1 | 1 |
| contrib/amcheck/t/002_cic_2pc.pl | 238 | 0 |
From 6edccb3906472e88b57f7642d496c42078fddc81 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Mon, 26 Jul 2021 15:46:01 +0500
Subject: [PATCH v5 1/2] Introduce TAP test for 2PC with CIC behavior
Currently there are two tests:
1. Deterministic reproduction of the bug with 3 psql sessions
2. Probablistic reproduction of the bug with 2 pgbench sessions
---
contrib/amcheck/Makefile | 2 +-
contrib/amcheck/t/002_cic_2pc.pl | 238 +++++++++++++++++++++++++++++++
2 files changed, 239 insertions(+), 1 deletion(-)
create mode 100644 contrib/amcheck/t/002_cic_2pc.pl
diff --git a/contrib/amcheck/Makefile b/contrib/amcheck/Makefile
index b82f221e50..5b5427d193 100644
--- a/contrib/amcheck/Makefile
+++ b/contrib/amcheck/Makefile
@@ -12,7 +12,7 @@ PGFILEDESC = "amcheck - function for verifying relation integrity"
REGRESS = check check_btree check_heap
-TAP_TESTS = 1
+TAP_TESTS = 2
ifdef USE_PGXS
PG_CONFIG = pg_config
diff --git a/contrib/amcheck/t/002_cic_2pc.pl b/contrib/amcheck/t/002_cic_2pc.pl
new file mode 100644
index 0000000000..541ac2e8a9
--- /dev/null
+++ b/contrib/amcheck/t/002_cic_2pc.pl
@@ -0,0 +1,238 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Test for point-in-time-recovery (PITR) with prepared transactions
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+
+use Test::More tests => 5;
+
+my ($node, $result);
+
+#
+# Test set-up
+#
+$node = get_new_node('CIC_2PC_test');
+$node->init;
+$node->append_conf('postgresql.conf', 'max_prepared_transactions = 10');
+$node->append_conf('postgresql.conf', 'lock_timeout = 5000');
+$node->start;
+$node->safe_psql('postgres', q(CREATE EXTENSION amcheck));
+$node->safe_psql('postgres', q(CREATE TABLE tbl(i int)));
+$node->safe_psql('postgres', q(CREATE INDEX idx on tbl(i)));
+
+
+#
+# Test 1. Run 3 overlapping 2PC transactions with concurrent reindex rebuild
+#
+# We have two concurrent background psqls: main_h for transaction and
+# reindex_h for cuncurrent reindex. Also we commit transactions from independent
+# psql's sometimes.
+#
+
+my $main_in = '';
+my $main_out = '';
+my $main_timer = IPC::Run::timeout(5);
+
+my $main_h = $node->background_psql('postgres', \$main_in, \$main_out, $main_timer,
+ on_error_stop => 1);
+$main_in .= q(
+BEGIN;
+INSERT INTO tbl VALUES(0);
+);
+$main_h->pump_nb;
+
+my $reindex_in = '';
+my $reindex_out = '';
+my $reindex_timer = IPC::Run::timeout(5);
+my $reindex_h = $node->background_psql('postgres', \$reindex_in, \$reindex_out, $reindex_timer,
+ on_error_stop => 1);
+$reindex_in .= q(
+\echo start
+REINDEX TABLE CONCURRENTLY tbl;
+);
+pump $reindex_h until $reindex_out =~ /start/ || $reindex_timer->is_expired;
+
+$main_in .= q(
+PREPARE TRANSACTION 'a';
+);
+$main_h->pump_nb;
+
+$main_in .= q(
+BEGIN;
+INSERT INTO tbl VALUES(0);
+);
+$main_h->pump_nb;
+
+$node->safe_psql('postgres', q(COMMIT PREPARED 'a';));
+
+$main_in .= q(
+PREPARE TRANSACTION 'b';
+BEGIN;
+INSERT INTO tbl VALUES(0);
+);
+$main_h->pump_nb;
+
+$node->safe_psql('postgres', q(COMMIT PREPARED 'b';));
+
+$main_in .= q(
+PREPARE TRANSACTION 'c';
+COMMIT PREPARED 'c';
+);
+$main_h->pump_nb;
+
+$main_h->finish;
+$reindex_h->finish;
+
+$result = $node->psql('postgres', q(SELECT bt_index_check('idx',true)));
+is($result, '0', 'bt_index_check checks index');
+
+
+#
+# Test 2. Stress CIC+2PC with pgbench
+#
+
+# Fix broken index first
+$node->safe_psql('postgres', q(REINDEX TABLE CONCURRENTLY tbl;));
+
+# Run background pgbench with CIC. We cannot mix-in this script into single pgbench:
+# CIC will deadlock with itself occasionally.
+my $pgbench_in = '';
+my $pgbench_out = '';
+my $pgbench_timer = IPC::Run::timeout(180);
+my $pgbench_h = background_pgbench('postgres', \$reindex_in, \$reindex_out, $reindex_timer,
+ {
+ '002_pgbench_concurrent_cic' =>
+ q(
+ REINDEX INDEX CONCURRENTLY idx;
+ SELECT bt_index_check('idx',true);
+ )
+ },
+ '--no-vacuum --client=1 --time=1');
+
+# Run pgbench.
+pgbench(
+ '--no-vacuum --client=5 --time=1',
+ 0,
+ [qr{actually processed}],
+ [qr{^$}],
+ 'concurrent CID and 2PC',
+ {
+ '002_pgbench_concurrent_2pc' =>
+ q(
+ \set txid random(1, 1000000000)
+ BEGIN;
+ INSERT INTO tbl VALUES(0);
+ PREPARE TRANSACTION 'tx:txid';
+ COMMIT PREPARED 'tx:txid';
+ )
+ });
+
+$pgbench_h->pump_nb;
+$pgbench_h->finish();
+$result = $pgbench_h->result(0); # Need extra check for ($Config{osname} eq "MSWin32") ?
+is($result, 0, "pgbench with CIC works");
+
+# done
+$node->safe_psql('postgres', q(DROP TABLE tbl;));
+$node->stop;
+done_testing();
+
+# AUX stuff, taken from 001_pgbench_with_server.pl
+
+# invoke pgbench, with parameters:
+# $opts: options as a string to be split on spaces
+# $stat: expected exit status
+# $out: reference to a regexp list that must match stdout
+# $err: reference to a regexp list that must match stderr
+# $name: name of test for error messages
+# $files: reference to filename/contents dictionary
+# @args: further raw options or arguments
+sub pgbench
+{
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ my ($opts, $stat, $out, $err, $name, $files, @args) = @_;
+ my @cmd = ('pgbench', split /\s+/, $opts);
+ my @filenames = ();
+ if (defined $files)
+ {
+
+ # note: files are ordered for determinism
+ for my $fn (sort keys %$files)
+ {
+ my $filename = $node->basedir . '/' . $fn;
+ push @cmd, '-f', $filename;
+
+ # cleanup file weight
+ $filename =~ s/\@\d+$//;
+
+ #push @filenames, $filename;
+ # filenames are expected to be unique on a test
+ if (-e $filename)
+ {
+ ok(0, "$filename must not already exist");
+ unlink $filename or die "cannot unlink $filename: $!";
+ }
+ append_to_file($filename, $$files{$fn});
+ }
+ }
+
+ push @cmd, @args;
+
+ $node->command_checks_all(\@cmd, $stat, $out, $err, $name);
+
+ # cleanup?
+ #unlink @filenames or die "cannot unlink files (@filenames): $!";
+
+ return;
+}
+
+# invoke pgbench in background alike background_psql
+sub background_pgbench
+{
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+ my ($dbname, $stdin, $stdout, $timer, $files, $opts) = @_;
+
+ my @cmd = ('pgbench', split /\s+/, $opts);
+ my @filenames = ();
+ if (defined $files)
+ {
+
+ # note: files are ordered for determinism
+ for my $fn (sort keys %$files)
+ {
+ my $filename = $node->basedir . '/' . $fn;
+ push @cmd, '-f', $filename;
+
+ # cleanup file weight
+ $filename =~ s/\@\d+$//;
+
+ #push @filenames, $filename;
+ # filenames are expected to be unique on a test
+ if (-e $filename)
+ {
+ ok(0, "$filename must not already exist");
+ unlink $filename or die "cannot unlink $filename: $!";
+ }
+ append_to_file($filename, $$files{$fn});
+ }
+ }
+
+ local %ENV = $node->_get_env();
+
+ # Ensure there is no data waiting to be sent:
+ $$stdin = "" if ref($stdin);
+ # IPC::Run would otherwise append to existing contents:
+ $$stdout = "" if ref($stdout);
+
+ my $harness = IPC::Run::start \@cmd,
+ '<', $stdin, '>', $stdout, $timer;
+
+ # Not checking actual input unlike in background_psql
+
+ return $harness;
+}
\ No newline at end of file
--
2.24.3 (Apple Git-128)