agora inbox for pgsql-hackers@postgresql.orghelp / color / mirror / Atom feed
[PATCH 2/2] Stop demanding that top xact must be seen before subxact in decoding. 411+ messages / 3 participants [nested] [flat]
* [PATCH 2/2] Stop demanding that top xact must be seen before subxact in decoding. @ 2019-10-23 12:56 Arseny Sher <sher-ars@yandex.ru> 0 siblings, 0 replies; 411+ messages in thread From: Arseny Sher @ 2019-10-23 12:56 UTC (permalink / raw) Manifested as ERROR: subtransaction logged without previous top-level txn record this check forbids legit behaviours like - First xl_xact_assignment record is beyond reading, i.e. earlier restart_lsn. - After restart_lsn there is some change of a subxact. - After that, there is second xl_xact_assignment (for another subxact) revealing relationship between top and first subxact. Such transaction won't be streamed anyway because we hadn't seen it in full; confirmed_flush_lsn must be past all these records. Saying for sure whether xact of some record encountered after snapshot was deserialized can be streamed or not requires to know whether it wrote something before deserialization point -- if yes, it hasn't been seen in full and can't be decoded. Snapshot doesn't have such info, so there is no easy way to relax the check. --- src/backend/replication/logical/reorderbuffer.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 8ce28ad629..6faba6077e 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -778,9 +778,6 @@ ReorderBufferAssignChild(ReorderBuffer *rb, TransactionId xid, txn = ReorderBufferTXNByXid(rb, xid, true, &new_top, lsn, true); subtxn = ReorderBufferTXNByXid(rb, subxid, true, &new_sub, lsn, false); - if (new_top && !new_sub) - elog(ERROR, "subtransaction logged without previous top-level txn record"); - if (!new_sub) { if (subtxn->is_known_as_subxact) -- 2.11.0 --=-=-= Content-Type: text/plain -- Arseny Sher Postgres Professional: http://www.postgrespro.com The Russian Postgres Company --=-=-=-- ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 8/8] reverse arguments .. isn't the other order a bug? @ 2020-03-12 21:33 Alvaro Herrera <alvherre@alvh.no-ip.org> 0 siblings, 0 replies; 411+ messages in thread From: Alvaro Herrera @ 2020-03-12 21:33 UTC (permalink / raw) --- src/backend/executor/nodeIncrementalSort.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/backend/executor/nodeIncrementalSort.c b/src/backend/executor/nodeIncrementalSort.c index 909d2df53f..bb88fca207 100644 --- a/src/backend/executor/nodeIncrementalSort.c +++ b/src/backend/executor/nodeIncrementalSort.c @@ -653,7 +653,7 @@ ExecIncrementalSort(PlanState *pstate) &node->incsort_info.fullsortGroupInfo, fullsort_state); - SO_printf("Setting execution_status to INCSORT_READFULLSORT (final tuple) \n"); + SO_printf("Setting execution_status to INCSORT_READFULLSORT (final tuple)\n"); node->execution_status = INCSORT_READFULLSORT; break; } @@ -713,7 +713,8 @@ ExecIncrementalSort(PlanState *pstate) * configuring sorting bound. */ SO2_printf("Changing bound_Done from %ld to %ld\n", - Min(node->bound, node->bound_Done + nTuples), node->bound_Done); + node->bound_Done, + Min(node->bound, node->bound_Done + nTuples)); node->bound_Done = Min(node->bound, node->bound_Done + nTuples); } @@ -721,7 +722,8 @@ ExecIncrementalSort(PlanState *pstate) * Once we find changed prefix keys we can complete the * sort and begin reading out the sorted tuples. */ - SO1_printf("Sorting fullsort tuplesort with %ld tuples\n", nTuples); + SO1_printf("Sorting fullsort tuplesort with %ld tuples\n", + nTuples); tuplesort_performsort(fullsort_state); if (pstate->instrument != NULL) @@ -882,7 +884,8 @@ ExecIncrementalSort(PlanState *pstate) * in configuring sorting bound. */ SO2_printf("Changing bound_Done from %ld to %ld\n", - Min(node->bound, node->bound_Done + nTuples), node->bound_Done); + node->bound_Done, + Min(node->bound, node->bound_Done + nTuples)); node->bound_Done = Min(node->bound, node->bound_Done + nTuples); } } -- 2.20.1 --jI8keyz6grp/JLjh-- ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 0 siblings, 0 replies; 411+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- 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" ^ permalink raw reply [nested|flat] 411+ messages in thread
end of thread, other threads:[~2025-12-13 17:13 UTC | newest] Thread overview: 411+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2019-10-23 12:56 [PATCH 2/2] Stop demanding that top xact must be seen before subxact in decoding. Arseny Sher <sher-ars@yandex.ru> 2020-03-12 21:33 [PATCH 8/8] reverse arguments .. isn't the other order a bug? Alvaro Herrera <alvherre@alvh.no-ip.org> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <mihailnikalayeu@gmail.com>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox