Thread

  1. [PATCH 1/3] stress test for repack concurrently

    Mikhail Nikalayeu <mihailnikalayeu@gmail.com> — 2025-12-13T17:13:37Z

    ---
     contrib/amcheck/meson.build       |   1 +
     contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++
     doc/src/sgml/regress.sgml         |  16 ++++
     3 files changed, 138 insertions(+)
     create mode 100644 contrib/amcheck/t/007_repack_1.pl
    
    diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build
    index d5137ef691d..cb4bc32e98a 100644
    --- a/contrib/amcheck/meson.build
    +++ b/contrib/amcheck/meson.build
    @@ -50,6 +50,7 @@ tests += {
           't/004_verify_nbtree_unique.pl',
           't/005_pitr.pl',
           't/006_verify_gin.pl',
    +      't/007_repack_1.pl',
         ],
       },
     }
    diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl
    new file mode 100644
    index 00000000000..17f3caa3a38
    --- /dev/null
    +++ b/contrib/amcheck/t/007_repack_1.pl
    @@ -0,0 +1,121 @@
    +# Copyright (c) 2021-2026, PostgreSQL Global Development Group
    +
    +# Test REPACK CONCURRENTLY with concurrent modifications
    +use strict;
    +use warnings FATAL => 'all';
    +
    +use PostgreSQL::Test::Cluster;
    +use PostgreSQL::Test::Utils;
    +
    +use Test::More;
    +
    +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test,
    +# 1 means the test takes about 6 seconds, and the increase should be
    +# roughly linear.
    +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/;
    +my $stressval = $+{stressval} // 1;
    +if (!$matched or $stressval == 0)
    +{
    +	plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test';
    +}
    +
    +note "stressval is $stressval";
    +
    +my $node;
    +
    +# Test set-up
    +$node = PostgreSQL::Test::Cluster->new('CIC_test');
    +$node->init;
    +$node->append_conf('postgresql.conf',
    +	'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default));
    +$node->append_conf(
    +	'postgresql.conf', qq(
    +wal_level = logical
    +));
    +
    +my $nrows = 10_000 * $stressval;
    +my $duration = 6 * $stressval;
    +my $no_hot = int(rand(2));
    +
    +$node->start;
    +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int)));
    +
    +if ($no_hot)
    +{
    +	$node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);));
    +}
    +else
    +{
    +	$node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);));
    +}
    +
    +
    +# Load amcheck
    +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck));
    +
    +# Insert $nrows rows into tbl
    +$node->safe_psql('postgres', qq(
    +	INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g
    +));
    +
    +my $sum = $node->safe_psql('postgres', q(
    +	SELECT SUM(val) AS sum FROM tbl
    +));
    +
    +
    +$node->pgbench(
    +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration",
    +0,
    +[qr{actually processed}],
    +[qr{^$}],
    +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY',
    +{
    +	'concurrent_ops' => qq(
    +		SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset
    +		\\if :gotlock
    +			REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey;
    +			SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true);
    +			SELECT bt_index_parent_check('test_idx', heapallindexed => true);
    +			\\sleep 10 ms
    +
    +			REPACK (CONCURRENTLY) tbl USING INDEX test_idx;
    +			SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true);
    +			SELECT bt_index_parent_check('test_idx', heapallindexed => true);
    +			\\sleep 10 ms
    +
    +			REPACK (CONCURRENTLY) tbl;
    +			SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true);
    +			SELECT bt_index_parent_check('test_idx', heapallindexed => true);
    +			\\sleep 10 ms
    +
    +			SELECT pg_advisory_unlock(42);
    +		\\else
    +			\\set num_a random(1, $nrows)
    +			\\set num_b random(1, $nrows)
    +			\\set diff random(1, 10000)
    +			BEGIN;
    +			UPDATE tbl SET val = val + :diff WHERE id = :num_a;
    +			\\sleep 1 ms
    +			UPDATE tbl SET val = val - :diff WHERE id = :num_b;
    +			\\sleep 1 ms
    +			COMMIT;
    +
    +			BEGIN
    +			--TRANSACTION ISOLATION LEVEL REPEATABLE READ
    +			;
    +			SELECT 1;
    +			\\sleep 1 ms
    +			SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_
    +			\\if :p_sum != $sum
    +				COMMIT;
    +				SELECT (:p_sum) / 0;
    +			\\endif
    +
    +			COMMIT;
    +		\\endif
    +	)
    +});
    +
    +$node->stop;
    +
    +done_testing();
    diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml
    index c74941bfbf2..c5b1b79a60c 100644
    --- a/doc/src/sgml/regress.sgml
    +++ b/doc/src/sgml/regress.sgml
    @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption'
          </listitem>
         </varlistentry>
     
    +    <varlistentry>
    +     <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term>
    +     <listitem>
    +      <para>
    +       Run some additional, moderately expensive tests for commands with a
    +       <literal>CONCURRENTLY</literal> option.  Optionally, an integer can be
    +       given after an equal sign, which affects how long each such test
    +       runs for.
    +       Each test is calibrated so that, when given a value of 10,
    +       it runs for approximately 60 seconds.
    +       If no value is given, 1 is assumed.
    +       Explicitly giving a value of 0 causes the test to be skipped.
    +      </para>
    +     </listitem>
    +    </varlistentry>
    +
         <varlistentry>
          <term><literal>wal_consistency_checking</literal></term>
          <listitem>
    -- 
    2.47.3
    
    
    --tbdehtstrqwksfdh
    Content-Type: text/x-diff; charset=utf-8
    Content-Disposition: attachment;
    	filename="0002-one-more-stress-test-for-repack-concurrently.patch"